1(function($) {// v5 namespace 2var MK = { 3 api : {}, 4 ui : {}, 5 component : {}, 6}; 7 8// Global 9window.MK = MK; 10'use strict'; 11 12 13console.log( 23423 ); 14/** 15 * Copyright Marc J. Schmidt. See the LICENSE file at the top-level 16 * directory of this distribution and at 17 * https://github.com/marcj/css-element-queries/blob/master/LICENSE. 18 */ 19(function (root, factory) { 20 if (typeof define === "function" && define.amd) { 21 define(factory); 22 } else if (typeof exports === "object") { 23 module.exports = factory(); 24 } else { 25 root.ResizeSensor = factory(); 26 } 27}(typeof window !== 'undefined' ? window : this, function () { 28 29 // Make sure it does not throw in a SSR (Server Side Rendering) situation 30 if (typeof window === "undefined") { 31 return null; 32 } 33 // Only used for the dirty checking, so the event callback count is limited to max 1 call per fps per sensor. 34 // In combination with the event based resize sensor this saves cpu time, because the sensor is too fast and 35 // would generate too many unnecessary events. 36 var requestAnimationFrame = window.requestAnimationFrame || 37 window.mozRequestAnimationFrame || 38 window.webkitRequestAnimationFrame || 39 function (fn) { 40 return window.setTimeout(fn, 20); 41 }; 42 43 /** 44 * Iterate over each of the provided element(s). 45 * 46 * @param {HTMLElement|HTMLElement[]} elements 47 * @param {Function} callback 48 */ 49 function forEachElement(elements, callback){ 50 var elementsType = Object.prototype.toString.call(elements); 51 var isCollectionTyped = ('[object Array]' === elementsType 52 || ('[object NodeList]' === elementsType) 53 || ('[object HTMLCollection]' === elementsType) 54 || ('[object Object]' === elementsType) 55 || ('undefined' !== typeof jQuery && elements instanceof jQuery) //jquery 56 || ('undefined' !== typeof Elements && elements instanceof Elements) //mootools 57 ); 58 var i = 0, j = elements.length; 59 if (isCollectionTyped) { 60 for (; i < j; i++) { 61 callback(elements[i]); 62 } 63 } else { 64 callback(elements); 65 } 66 } 67 68 /** 69 * Get element size 70 * @param {HTMLElement} element 71 * @returns {Object} {width, height} 72 */ 73 function getElementSize(element) { 74 if (!element.getBoundingClientRect) { 75 return { 76 width: element.offsetWidth, 77 height: element.offsetHeight 78 } 79 } 80 81 var rect = element.getBoundingClientRect(); 82 return { 83 width: Math.round(rect.width), 84 height: Math.round(rect.height) 85 } 86 } 87 88 /** 89 * Class for dimension change detection. 90 * 91 * @param {Element|Element[]|Elements|jQuery} element 92 * @param {Function} callback 93 * 94 * @constructor 95 */ 96 var ResizeSensor = function(element, callback) { 97 /** 98 * 99 * @constructor 100 */ 101 function EventQueue() { 102 var q = []; 103 this.add = function(ev) { 104 q.push(ev); 105 }; 106 107 var i, j; 108 this.call = function() { 109 for (i = 0, j = q.length; i < j; i++) { 110 q[i].call(); 111 } 112 }; 113 114 this.remove = function(ev) { 115 var newQueue = []; 116 for(i = 0, j = q.length; i < j; i++) { 117 if(q[i] !== ev) newQueue.push(q[i]); 118 } 119 q = newQueue; 120 }; 121 122 this.length = function() { 123 return q.length; 124 } 125 } 126 127 /** 128 * 129 * @param {HTMLElement} element 130 * @param {Function} resized 131 */ 132 function attachResizeEvent(element, resized) { 133 if (!element) return; 134 if (element.resizedAttached) { 135 element.resizedAttached.add(resized); 136 return; 137 } 138 139 element.resizedAttached = new EventQueue(); 140 element.resizedAttached.add(resized); 141 142 element.resizeSensor = document.createElement('div'); 143 element.resizeSensor.dir = 'ltr'; 144 element.resizeSensor.className = 'resize-sensor'; 145 var style = 'position: absolute; left: -10px; top: -10px; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;'; 146 var styleChild = 'position: absolute; left: 0; top: 0; transition: 0s;'; 147 148 element.resizeSensor.style.cssText = style; 149 element.resizeSensor.innerHTML = 150 '<div class="resize-sensor-expand" style="' + style + '">' + 151 '<div style="' + styleChild + '"></div>' + 152 '</div>' + 153 '<div class="resize-sensor-shrink" style="' + style + '">' + 154 '<div style="' + styleChild + ' width: 200%; height: 200%"></div>' + 155 '</div>'; 156 element.appendChild(element.resizeSensor); 157 158 var position = window.getComputedStyle(element).getPropertyPriority('position'); 159 if ('absolute' !== position && 'relative' !== position && 'fixed' !== position) { 160 element.style.position = 'relative'; 161 } 162 163 var expand = element.resizeSensor.childNodes[0]; 164 var expandChild = expand.childNodes[0]; 165 var shrink = element.resizeSensor.childNodes[1]; 166 var dirty, rafId, newWidth, newHeight; 167 var size = getElementSize(element); 168 var lastWidth = size.width; 169 var lastHeight = size.height; 170 171 var reset = function() { 172 //set display to block, necessary otherwise hidden elements won't ever work 173 var invisible = element.offsetWidth === 0 && element.offsetHeight === 0; 174 175 if (invisible) { 176 var saveDisplay = element.style.display; 177 element.style.display = 'block'; 178 } 179 180 expandChild.style.width = '100000px'; 181 expandChild.style.height = '100000px'; 182 183 expand.scrollLeft = 100000; 184 expand.scrollTop = 100000; 185 186 shrink.scrollLeft = 100000; 187 shrink.scrollTop = 100000; 188 189 if (invisible) { 190 element.style.display = saveDisplay; 191 } 192 }; 193 element.resizeSensor.resetSensor = reset; 194 195 var onResized = function() { 196 rafId = 0; 197 198 if (!dirty) return; 199 200 lastWidth = newWidth; 201 lastHeight = newHeight; 202 203 if (element.resizedAttached) { 204 element.resizedAttached.call(); 205 } 206 }; 207 208 var onScroll = function() { 209 var size = getElementSize(element); 210 var newWidth = size.width; 211 var newHeight = size.height; 212 dirty = newWidth != lastWidth || newHeight != lastHeight; 213
214 if (dirty && !rafId) { 215 rafId = requestAnimationFrame(onResized); 216 } 217 218 reset(); 219 }; 220 221 var addEvent = function(el, name, cb) { 222 if (el.attachEvent) { 223 el.attachEvent('on' + name, cb); 224 } else { 225 el.addEventListener(name, cb); 226 } 227 }; 228 229 addEvent(expand, 'scroll', onScroll); 230 addEvent(shrink, 'scroll', onScroll); 231 232 // Fix for custom Elements 233 requestAnimationFrame(reset); 234 } 235 236 forEachElement(element, function(elem){ 237 attachResizeEvent(elem, callback); 238 }); 239 240 this.detach = function(ev) { 241 ResizeSensor.detach(element, ev); 242 }; 243 244 this.reset = function() { 245 element.resizeSensor.resetSensor(); 246 }; 247 }; 248 249 ResizeSensor.reset = function(element, ev) { 250 forEachElement(element, function(elem){ 251 elem.resizeSensor.resetSensor(); 252 }); 253 }; 254 255 ResizeSensor.detach = function(element, ev) { 256 forEachElement(element, function(elem){ 257 if (!elem) return; 258 if(elem.resizedAttached && typeof ev === "function"){ 259 elem.resizedAttached.remove(ev); 260 if(elem.resizedAttached.length()) return; 261 } 262 if (elem.resizeSensor) { 263 if (elem.contains(elem.resizeSensor)) { 264 elem.removeChild(elem.resizeSensor); 265 } 266 delete elem.resizeSensor; 267 delete elem.resizedAttached; 268 } 269 }); 270 }; 271 272 return ResizeSensor; 273 274})); 275 276'use strict'; 277 278/** 279 * Copyright Marc J. Schmidt. See the LICENSE file at the top-level 280 * directory of this distribution and at 281 * https://github.com/marcj/css-element-queries/blob/master/LICENSE. 282 */ 283(function (root, factory) { 284 if (typeof define === "function" && define.amd) { 285 define(['./ResizeSensor.js'], factory); 286 } else if (typeof exports === "object") { 287 module.exports = factory(require('./ResizeSensor.js')); 288 } else { 289 root.ElementQueries = factory(root.ResizeSensor); 290 root.ElementQueries.listen(); 291 } 292}(typeof window !== 'undefined' ? window : this, function (ResizeSensor) { 293 294 /** 295 * 296 * @type {Function} 297 * @constructor 298 */ 299 var ElementQueries = function () { 300 //<style> element with our dynamically created styles 301 var cssStyleElement; 302 303 //all rules found for element queries 304 var allQueries = {}; 305 306 //association map to identify which selector belongs to a element from the animationstart event. 307 var idToSelectorMapping = []; 308 309 /** 310 * 311 * @param element 312 * @returns {Number} 313 */ 314 function getEmSize(element) { 315 if (!element) { 316 element = document.documentElement; 317 } 318 var fontSize = window.getComputedStyle(element, null).fontSize; 319 return parseFloat(fontSize) || 16; 320 } 321 322 /** 323 * Get element size 324 * @param {HTMLElement} element 325 * @returns {Object} {width, height} 326 */ 327 function getElementSize(element) { 328 if (!element.getBoundingClientRect) { 329 return { 330 width: element.offsetWidth, 331 height: element.offsetHeight 332 } 333 } 334 335 var rect = element.getBoundingClientRect(); 336 return { 337 width: Math.round(rect.width), 338 height: Math.round(rect.height) 339 } 340 } 341 342 /** 343 * 344 * @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE 345 * 346 * @param {HTMLElement} element 347 * @param {*} value 348 * @returns {*} 349 */ 350 function convertToPx(element, value) { 351 var numbers = value.split(/\d/); 352 var units = numbers[numbers.length - 1]; 353 value = parseFloat(value); 354 switch (units) { 355 case "px": 356 return value; 357 case "em": 358 return value * getEmSize(element); 359 case "rem": 360 return value * getEmSize(); 361 // Viewport units! 362 // According to http://quirksmode.org/mobile/tableViewport.html 363 // documentElement.clientWidth/Height gets us the most reliable info 364 case "vw": 365 return value * document.documentElement.clientWidth / 100; 366 case "vh": 367 return value * document.documentElement.clientHeight / 100; 368 case "vmin": 369 case "vmax": 370 var vw = document.documentElement.clientWidth / 100; 371 var vh = document.documentElement.clientHeight / 100; 372 var chooser = Math[units === "vmin" ? "min" : "max"]; 373 return value * chooser(vw, vh); 374 default: 375 return value; 376 // for now, not supporting physical units (since they are just a set number of px) 377 // or ex/ch (getting accurate measurements is hard) 378 } 379 } 380 381 /** 382 * 383 * @param {HTMLElement} element 384 * @param {String} id 385 * @constructor 386 */ 387 function SetupInformation(element, id) { 388 this.element = element; 389 var key, option, elementSize, value, actualValue, attrValues, attrValue, attrName; 390 391 var attributes = ['min-width', 'min-height', 'max-width', 'max-height']; 392 393 /** 394 * Extracts the computed width/height and sets to min/max- attribute. 395 */ 396 this.call = function () { 397 // extract current dimensions 398 elementSize = getElementSize(this.element); 399 400 attrValues = {}; 401 402 for (key in allQueries[id]) { 403 if (!allQueries[id].hasOwnProperty(key)) { 404 continue; 405 } 406 option = allQueries[id][key]; 407 408 value = convertToPx(this.element, option.value); 409 410 actualValue = option.property === 'width' ? elementSize.width : elementSize.height; 411 attrName = option.mode + '-' + option.property; 412 attrValue = ''; 413 414 if (option.mode === 'min' && actualValue >= value) { 415 attrValue += option.value; 416 } 417 418 if (option.mode === 'max' && actualValue <= value) { 419 attrValue += option.value; 420 } 421 422 if (!attrValues[attrName]) attrValues[attrName] = ''; 423 if (attrValue && -1 === (' ' + attrValues[attrName] + ' ').indexOf(' ' + attrValue + ' ')) { 424 attrValues[attrName] += ' ' + attrValue; 425 } 426 } 427 428 for (var k in attributes) { 429 if (!attributes.hasOwnProperty(k)) continue; 430 431 if (attrValues[attributes[k]]) { 432 this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1)); 433 } else { 434 this.element.removeAttribute(attributes[k]); 435 } 436 } 437 }; 438 } 439 440 /** 441 * @param {HTMLElement} element 442 * @param {Object} id 443 */ 444 function setupElement(element, id) { 445 if (!element.elementQueriesSetupInformation) { 446 element.elementQueriesSetupInformation = new SetupInformation(element, id); 447 } 448 if (!element.elementQueriesSensor) { 449 element.elementQueriesSensor = new ResizeSensor(element, function () { 450 element.elementQueriesSetupInformation.call(); 451 }); 452 } 453 454 element.elementQueriesSetupInformation.call(); 455 } 456 457 /** 458 * Stores rules to the selector that should be applied once resized. 459 * 460 * @param {String} selector 461 * @param {String} mode min|max 462 * @param {String} property width|height 463 * @param {String} value 464 */ 465 function queueQuery(selector, mode, property, value) { 466 if (typeof(allQueries[selector]) === 'undefined') { 467 allQueries[selector] = []; 468 // add animation to trigger animationstart event, so we know exactly when a element appears in the DOM 469 470 var id = idToSelectorMapping.length; 471 cssStyleElement.innerHTML += '\n' + selector + ' {animation: 0.1s element-queries;}'; 472 cssStyleElement.innerHTML += '\n' + selector + ' > .resize-sensor {min-width: '+id+'px;}'; 473 idToSelectorMapping.push(selector); 474 } 475 476 allQueries[selector].push({ 477 mode: mode, 478 property: property, 479 value: value 480 }); 481 } 482 483 function getQuery(container) { 484 var query; 485 if (document.querySelectorAll) query = (container) ? container.querySelectorAll.bind(container) : document.querySelectorAll.bind(document); 486 if (!query && 'undefined' !== typeof $$) query = $$; 487 if (!query && 'undefined' !== typeof jQuery) query = jQuery; 488 489 if (!query) { 490 throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.'; 491 } 492 493 return query; 494 } 495 496 /** 497 * If animationStart didn't catch a new element in the DOM, we can manually search for it 498 */ 499 function findElementQueriesElements(container) { 500 var query = getQuery(container); 501 502 for (var selector in allQueries) if (allQueries.hasOwnProperty(mode)) { 503 // find all elements based on the extract query selector from the element query rule 504 var elements = query(selector, container); 505 506 for (var i = 0, j = elements.length; i < j; i++) { 507 setupElement(elements[i], selector); 508 } 509 } 510 } 511 512 /** 513 * 514 * @param {HTMLElement} element 515 */ 516 function attachResponsiveImage(element) { 517 var children = []; 518 var rules = []; 519 var sources = []; 520 var defaultImageId = 0; 521 var lastActiveImage = -1; 522 var loadedImages = []; 523 524 for (var i in element.children) { 525 if (!element.children.hasOwnProperty(i)) continue; 526 527 if (element.children[i].tagName && element.children[i].tagName.toLowerCase() === 'img') { 528 children.push(element.children[i]); 529 530 var minWidth = element.children[i].getAttribute('min-width') || element.children[i].getAttribute('data-min-width'); 531 //var minHeight = element.children[i].getAttribute('min-height') || element.children[i].getAttribute('data-min-height'); 532 var src = element.children[i].getAttribute('data-src') || element.children[i].getAttribute('url'); 533 534 sources.push(src); 535 536 var rule = { 537 minWidth: minWidth 538 }; 539 540 rules.push(rule); 541 542 if (!minWidth) { 543 defaultImageId = children.length - 1; 544 element.children[i].style.display = 'block'; 545 } else { 546 element.children[i].style.display = 'none'; 547 } 548 } 549 } 550 551 lastActiveImage = defaultImageId; 552 553 function check() { 554 var imageToDisplay = false, i; 555 556 for (i in children) { 557 if (!children.hasOwnProperty(i)) continue; 558 559 if (rules[i].minWidth) { 560 if (element.offsetWidth > rules[i].minWidth) { 561 imageToDisplay = i; 562 } 563 } 564 } 565 566 if (!imageToDisplay) { 567 //no rule matched, show default 568 imageToDisplay = defaultImageId; 569 } 570 571 if (lastActiveImage !== imageToDisplay) { 572 //image change 573 574 if (!loadedImages[imageToDisplay]) { 575 //image has not been loaded yet, we need to load the image first in memory to prevent flash of 576 //no content 577 578 var image = new Image(); 579 image.onload = function () { 580 children[imageToDisplay].src = sources[imageToDisplay]; 581 582 children[lastActiveImage].style.display = 'none'; 583 children[imageToDisplay].style.display = 'block'; 584 585 loadedImages[imageToDisplay] = true; 586 587 lastActiveImage = imageToDisplay; 588 }; 589 590 image.src = sources[imageToDisplay]; 591 } else { 592 children[lastActiveImage].style.display = 'none'; 593 children[imageToDisplay].style.display = 'block'; 594 lastActiveImage = imageToDisplay; 595 } 596 } else { 597 //make sure for initial check call the .src is set correctly 598 children[imageToDisplay].src = sources[imageToDisplay]; 599 } 600 } 601 602 element.resizeSensor = new ResizeSensor(element, check); 603 check(); 604 } 605 606 function findResponsiveImages() { 607 var query = getQuery(); 608 609 var elements = query('[data-responsive-image],[responsive-image]'); 610 for (var i = 0, j = elements.length; i < j; i++) { 611 attachResponsiveImage(elements[i]); 612 } 613 } 614 615 var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mg
615i; 616 var attrRegex = /\[[\s\t]*?(min|max)-(width|height)[\s\t]*?[~$\^]?=[\s\t]*?"([^"]*?)"[\s\t]*?]/mgi; 617 618 /** 619 * @param {String} css 620 */ 621 function extractQuery(css) { 622 var match, smatch, attrs, attrMatch; 623 624 css = css.replace(/'/g, '"'); 625 while (null !== (match = regex.exec(css))) { 626 smatch = match[1] + match[3]; 627 attrs = match[2]; 628 629 while (null !== (attrMatch = attrRegex.exec(attrs))) { 630 queueQuery(smatch, attrMatch[1], attrMatch[2], attrMatch[3]); 631 } 632 } 633 } 634 635 /** 636 * @param {CssRule[]|String} rules 637 */ 638 function readRules(rules) { 639 var selector = ''; 640 641 if (!rules) { 642 return; 643 } 644 645 if ('string' === typeof rules) { 646 rules = rules.toLowerCase(); 647 if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf('max-width')) { 648 extractQuery(rules); 649 } 650 } else { 651 for (var i = 0, j = rules.length; i < j; i++) { 652 if (1 === rules[i].type) { 653 selector = rules[i].selectorText || rules[i].cssText; 654 if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf('max-height')) { 655 extractQuery(selector); 656 } else if (-1 !== selector.indexOf('min-width') || -1 !== selector.indexOf('max-width')) { 657 extractQuery(selector); 658 } 659 } else if (4 === rules[i].type) { 660 readRules(rules[i].cssRules || rules[i].rules); 661 } else if (3 === rules[i].type) { 662 readRules(rules[i].styleSheet.cssRules); 663 } 664 } 665 } 666 } 667 668 var defaultCssInjected = false; 669 670 /** 671 * Searches all css rules and setups the event listener to all elements with element query rules.. 672 */ 673 this.init = function () { 674 var animationStart = 'animationstart'; 675 if (typeof document.documentElement.style['webkitAnimationName'] !== 'undefined') { 676 animationStart = 'webkitAnimationStart'; 677 } else if (typeof document.documentElement.style['MozAnimationName'] !== 'undefined') { 678 animationStart = 'mozanimationstart'; 679 } else if (typeof document.documentElement.style['OAnimationName'] !== 'undefined') { 680 animationStart = 'oanimationstart'; 681 } 682 683 document.body.addEventListener(animationStart, function (e) { 684 var element = e.target; 685 var styles = window.getComputedStyle(element, null); 686 687 if (-1 !== styles.getPropertyValue('animation-name').indexOf('element-queries')) { 688 element.elementQueriesSensor = new ResizeSensor(element, function () { 689 if (element.elementQueriesSetupInformation) { 690 element.elementQueriesSetupInformation.call(); 691 } 692 }); 693 694 var sensorStyles = window.getComputedStyle(element.resizeSensor, null); 695 var id = sensorStyles.getPropertyValue('min-width'); 696 id = parseInt(id.replace('px', '')); 697 setupElement(e.target, idToSelectorMapping[id]); 698 } 699 }); 700 701 if (!defaultCssInjected) { 702 cssStyleElement = document.createElement('style'); 703 cssStyleElement.type = 'text/css'; 704 cssStyleElement.innerHTML = '[responsive-image] > img, [data-responsive-image] {overflow: hidden; padding: 0; } [responsive-image] > img, [data-responsive-image] > img {width: 100%;}'; 705 706 //safari wants at least one rule in keyframes to start working 707 cssStyleElement.innerHTML += '\n@keyframes element-queries { 0% { visibility: inherit; } }'; 708 document.getElementsByTagName('head')[0].appendChild(cssStyleElement); 709 defaultCssInjected = true; 710 } 711 712 for (var i = 0, j = document.styleSheets.length; i < j; i++) { 713 try { 714 if (document.styleSheets[i].href && 0 === document.styleSheets[i].href.indexOf('file://')) { 715 console.log("CssElementQueries: unable to parse local css files, " + document.styleSheets[i].href); 716 } 717 718 readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText); 719 } catch (e) { 720 } 721 } 722 723 // findElementQueriesElements(); 724 findResponsiveImages(); 725 }; 726 727 /** 728 * Go through all collected rules (readRules()) and attach the resize-listener. 729 * Not necessary to call it manually, since we detect automatically when new elements 730 * are available in the DOM. However, sometimes handy for dirty DOM modifications. 731 * 732 * @param {HTMLElement} container only elements of the container are considered (document.body if not set) 733 */ 734 this.findElementQueriesElements = function (container) { 735 findElementQueriesElements(container); 736 }; 737 738 this.update = function () { 739 this.init(); 740 }; 741 }; 742 743 ElementQueries.update = function () {
744 ElementQueries.instance.update(); 745 }; 746 747 /** 748 * Removes all sensor and elementquery information from the element. 749 * 750 * @param {HTMLElement} element 751 */ 752 ElementQueries.detach = function (element) { 753 if (element.elementQueriesSetupInformation) { 754 //element queries 755 element.elementQueriesSensor.detach(); 756 delete element.elementQueriesSetupInformation; 757 delete element.elementQueriesSensor; 758 759 } else if (element.resizeSensor) { 760 //responsive image 761 762 element.resizeSensor.detach(); 763 delete element.resizeSensor; 764 } 765 }; 766 767 ElementQueries.init = function () { 768 if (!ElementQueries.instance) { 769 ElementQueries.instance = new ElementQueries(); 770 } 771 772 ElementQueries.instance.init(); 773 }; 774 775 var domLoaded = function (callback) { 776 /* Mozilla, Chrome, Opera */ 777 if (document.addEventListener) { 778 document.addEventListener('DOMContentLoaded', callback, false); 779 } 780 /* Safari, iCab, Konqueror */ 781 else if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) { 782 var DOMLoadTimer = setInterval(function () { 783 if (/loaded|complete/i.test(document.readyState)) { 784 callback(); 785 clearInterval(DOMLoadTimer); 786 } 787 }, 10); 788 } 789 /* Other web browsers */ 790 else window.onload = callback; 791 }; 792 793 ElementQueries.findElementQueriesElements = function (container) { 794 ElementQueries.instance.findElementQueriesElements(container); 795 }; 796 797 ElementQueries.listen = function () { 798 domLoaded(ElementQueries.init); 799 }; 800 801 return ElementQueries; 802 803})); 804 805(function($) { 806 'use strict'; 807 808 $.exists = function(selector) { 809 return ($(selector).length > 0); 810 }; 811 812 /** 813 * Helper to enable caching async scripts 814 * https://api.jquery.com/jquery.getscript/ 815 * http://www.vrdmn.com/2013/07/overriding-jquerygetscript-to-include.html 816 * 817 * @param {String} script url 818 * @param {Function} callback 819 */ 820 $.getCachedScript = function( url ) { 821 var options = { 822 dataType: "script", 823 cache: true, 824 url: url 825 }; 826 827 // Use $.ajax() since it is more flexible than $.getScript 828 // Return the jqXHR object so we can chain callbacks 829 return $.ajax( options ); 830 }; 831 832 833 834 // Fn to allow an event to fire after all images are loaded 835 // usage: 836 // $.ajax({ 837 // cache: false, 838 // url: 'ajax/content.php', 839 // success: function(data) { 840 // $('#divajax').html(data).imagesLoaded().then(function(){ 841 // // do stuff after images are loaded here 842 // }); 843 // } 844 // }); 845 $.fn.mk_imagesLoaded = function () { 846 847 // Edit: in strict mode, the var keyword is needed 848 var $imgs = this.find('img[src!=""]'); 849 // if there's no images, just return an already resolved promise 850 if (!$imgs.length) {return $.Deferred().resolve().promise();} 851 852 // for each image, add a deferred object to the array which resolves when the image is loaded (or if loading fails) 853 var dfds = []; 854 $imgs.each(function(){ 855 var dfd = $.Deferred(); 856 dfds.push(dfd); 857 var img = new Image(); 858 img.onload = function(){dfd.resolve();}; 859 img.onerror = function(){dfd.resolve();}; 860 img.src = this.src; 861 }); 862 863 // return a master promise object which will resolve when all the deferred objects have resolved 864 // IE - when all the images are loaded 865 return $.when.apply($,dfds); 866 867 }; 868 869}(jQuery)); 870/** 871* Detect Element Resize 872* 873* https://github.com/sdecima/javascript-detect-element-resize 874* Sebastian Decima 875* 876* version: 0.5.3 877**/ 878 879(function () { 880 var attachEvent = document.attachEvent, 881 stylesCreated = false; 882 883 if (!attachEvent) { 884 var requestFrame = (function(){ 885 var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || 886 function(fn){ return window.setTimeout(fn, 20); }; 887 return function(fn){ return raf(fn); }; 888 })(); 889 890 var cancelFrame = (function(){ 891 var cancel = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || 892 window.clearTimeout; 893 return function(id){ return cancel(id); }; 894 })(); 895 896 function resetTriggers(element){ 897 var triggers = element.__resizeTriggers__, 898 expand = triggers.firstElementChild, 899 contract = triggers.lastElementChild, 900 expandChild = expand.firstElementChild; 901 contract.scrollLeft = contract.scrollWidth; 902 contract.scrollTop = contract.scrollHeight; 903 expandChild.style.width = expand.offsetWidth + 1 + 'px'; 904 expandChild.style.height = expand.offsetHeight + 1 + 'px'; 905 expand.scrollLeft = expand.scrollWidth; 906 expand.scrollTop = expand.scrollHeight; 907 }; 908 909 function checkTriggers(element){ 910 return element.offsetWidth != element.__resizeLast__.width || 911 element.offsetHeight != element.__resizeLast__.height; 912 } 913 914 function scrollListener(e){ 915 var element = this; 916 resetTriggers(this); 917 if (this.__resizeRAF__) cancelFrame(this.__resizeRAF__); 918 this.__resizeRAF__ = requestFrame(function(){ 919 if (checkTriggers(element)) { 920 element.__resizeLast__.width = element.offsetWidth; 921 element.__resizeLast__.height = element.offsetHeight; 922 element.__resizeListeners__.forEach(function(fn){ 923 fn.call(element, e); 924 }); 925 } 926 }); 927 }; 928
929 /* Detect CSS Animations support to detect element display/re-attach */ 930 var animation = false, 931 animationstring = 'animation', 932 keyframeprefix = '', 933 animationstartevent = 'animationstart', 934 domPrefixes = 'Webkit Moz O ms'.split(' '), 935 startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(' '), 936 pfx = ''; 937 { 938 var elm = document.createElement('fakeelement'); 939 if( elm.style.animationName !== undefined ) { animation = true; } 940 941 if( animation === false ) { 942 for( var i = 0; i < domPrefixes.length; i++ ) { 943 if( elm.style[ domPrefixes[i] + 'AnimationName' ] !== undefined ) { 944 pfx = domPrefixes[ i ]; 945 animationstring = pfx + 'Animation'; 946 keyframeprefix = '-' + pfx.toLowerCase() + '-'; 947 animationstartevent = startEvents[ i ]; 948 animation = true; 949 break; 950 } 951 } 952 } 953 } 954 955 var animationName = 'resizeanim'; 956 var animationKeyframes = '@' + keyframeprefix + 'keyframes ' + animationName + ' { from { opacity: 0; } to { opacity: 0; } } '; 957 var animationStyle = keyframeprefix + 'animation: 1ms ' + animationName + '; '; 958 } 959 960 function createStyles() { 961 if (!stylesCreated) { 962 //opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360 963 var css = (animationKeyframes ? animationKeyframes : '') + 964 '.resize-triggers { ' + (animationStyle ? animationStyle : '') + 'visibility: hidden; opacity: 0; } ' + 965 '.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: \" \"; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }', 966 head = document.head || document.getElementsByTagName('head')[0], 967 style = document.createElement('style'); 968 969 style.type = 'text/css'; 970 if (style.styleSheet) { 971 style.styleSheet.cssText = css; 972 } else { 973 style.appendChild(document.createTextNode(css)); 974 } 975 976 head.appendChild(style); 977 stylesCreated = true; 978 } 979 } 980 981 window.addResizeListener = function(element, fn){ 982 if (attachEvent) element.attachEvent('onresize', fn); 983 else { 984 if (!element.__resizeTriggers__) { 985 if (getComputedStyle(element).position == 'static') element.style.position = 'relative'; 986 createStyles(); 987 element.__resizeLast__ = {}; 988 element.__resizeListeners__ = []; 989 (element.__resizeTriggers__ = document.createElement('div')).className = 'resize-triggers'; 990 element.__resizeTriggers__.innerHTML = '<div class="expand-trigger"><div></div></div>' + 991 '<div class="contract-trigger"></div>'; 992 element.appendChild(element.__resizeTriggers__); 993 resetTriggers(element); 994 element.addEventListener('scroll', scrollListener, true); 995 996 /* Listen for a css animation to detect element display/re-attach */ 997 animationstartevent && element.__resizeTriggers__.addEventListener(animationstartevent, function(e) { 998 if(e.animationName == animationName) 999 resetTriggers(element); 1000 }); 1001 } 1002 element.__resizeListeners__.push(fn); 1003 } 1004 }; 1005 1006 window.removeResizeListener = function(element, fn){ 1007 if (attachEvent) element.detachEvent('onresize', fn); 1008 else { 1009 element.__resizeListeners__.splice(element.__resizeListeners__.indexOf(fn), 1); 1010 if (!element.__resizeListeners__.length) { 1011 element.removeEventListener('scroll', scrollListener); 1012 element.__resizeTriggers__ = !element.removeChild(element.__resizeTriggers__); 1013 } 1014 } 1015 } 1016})(); 1017/** 1018* @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 1019*/ 1020;(function(window, document) { 1021/*jshint evil:true */ 1022 /** version */ 1023 var version = '3.7.3'; 1024 1025 /** Preset options */ 1026 var options = window.html5 || {}; 1027 1028 /** Used to skip problem elements */ 1029 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 1030 1031 /** Not all elements can be cloned in IE **/ 1032 var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; 1033 1034 /** Detect whether the browser supports default html5 styles */ 1035 var supportsHtml5Styles; 1036 1037 /** Name of the expando, to work with multiple documents or to re-shiv one document */ 1038 var expando = '_html5shiv'; 1039 1040 /** The id for the the documents expando */ 1041 var expanID = 0; 1042 1043 /** Cached data for each document */ 1044 var expandoData = {}; 1045 1046 /** Detect whether the browser supports unknown elements */ 1047 var supportsUnknownElements; 1048 1049 (function() { 1050 try { 1051 var a = document.createElement('a'); 1052 a.innerHTML = '<xyz></xyz>'; 1053 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 1054 supportsHtml5Styles = ('hidden' in a); 1055 1056 supportsUnknownElements = a.childNodes.length == 1 || (function() { 1057 // assign a false positive if unable to shiv 1058 (document.createElement)('a'); 1059 var frag = document.createDocumentFragment(); 1060 return ( 1061 typeof frag.cloneNode == 'undefined' || 1062 typeof frag.createDocumentFragment == 'undefined' || 1063 typeof frag.createElement == 'undefined' 1064 ); 1065 }()); 1066 } catch(e) { 1067 // assign a false positive if detection fails => unable to shiv 1068 supportsHtml5Styles = true; 1069 supportsUnknownElements = true; 1070 } 1071 1072 }()); 1073 1074 /*--------------------------------------------------------------------------*/ 1075 1076 /** 1077 * Creates a style sheet with the given CSS text and adds it to the document. 1078 * @private 1079 * @param {Document} ownerDocument The document. 1080 * @param {String} cssText The CSS text. 1081 * @returns {StyleSheet} The style element. 1082 */ 1083 function addStyleSheet(ownerDocument, cssText) { 1084 var p = ownerDocument.createElement('p'), 1085 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 1086 1087 p.innerHTML = 'x<style>' + cssText + '</style>'; 1088 return parent.insertBefore(p.lastChild, parent.firstChild); 1089 } 1090 1091 /** 1092 * Returns the value of `html5.elements` as an array.
1093 * @private 1094 * @returns {Array} An array of shived element node names. 1095 */ 1096 function getElements() { 1097 var elements = html5.elements; 1098 return typeof elements == 'string' ? elements.split(' ') : elements; 1099 } 1100 1101 /** 1102 * Extends the built-in list of html5 elements 1103 * @memberOf html5 1104 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 1105 * @param {Document} ownerDocument The context document. 1106 */ 1107 function addElements(newElements, ownerDocument) { 1108 var elements = html5.elements; 1109 if(typeof elements != 'string'){ 1110 elements = elements.join(' '); 1111 } 1112 if(typeof newElements != 'string'){ 1113 newElements = newElements.join(' '); 1114 } 1115 html5.elements = elements +' '+ newElements; 1116 shivDocument(ownerDocument); 1117 } 1118 1119 /** 1120 * Returns the data associated to the given document 1121 * @private 1122 * @param {Document} ownerDocument The document. 1123 * @returns {Object} An object of data. 1124 */ 1125 function getExpandoData(ownerDocument) { 1126 var data = expandoData[ownerDocument[expando]]; 1127 if (!data) { 1128 data = {}; 1129 expanID++; 1130 ownerDocument[expando] = expanID; 1131 expandoData[expanID] = data; 1132 } 1133 return data; 1134 } 1135 1136 /** 1137 * returns a shived element for the given nodeName and document 1138 * @memberOf html5 1139 * @param {String} nodeName name of the element 1140 * @param {Document|DocumentFrag
vendor: 6,335 bytes, lines 1140-1342
1140ment} ownerDocument The context document. 1141 * @returns {Object} The shived element. 1142 */ 1143 function createElement(nodeName, ownerDocument, data){ 1144 if (!ownerDocument) { 1145 ownerDocument = document; 1146 } 1147 if(supportsUnknownElements){ 1148 return ownerDocument.createElement(nodeName); 1149 } 1150 if (!data) { 1151 data = getExpandoData(ownerDocument); 1152 } 1153 var node; 1154 1155 if (data.cache[nodeName]) { 1156 node = data.cache[nodeName].cloneNode(); 1157 } else if (saveClones.test(nodeName)) { 1158 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 1159 } else { 1160 node = data.createElem(nodeName); 1161 } 1162 1163 // Avoid adding some elements to fragments in IE < 9 because 1164 // * Attributes like `name` or `type` cannot be set/changed once an element 1165 // is inserted into a document/fragment 1166 // * Link elements with `src` attributes that are inaccessible, as with 1167 // a 403 response, will cause the tab/window to crash 1168 // * Script elements appended to fragments will execute when their `src` 1169 // or `text` property is set 1170 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 1171 } 1172 1173 /** 1174 * returns a shived DocumentFragment for the given document 1175 * @memberOf html5 1176 * @param {Document} ownerDocument The context document. 1177 * @returns {Object} The shived DocumentFragment. 1178 */ 1179 function createDocumentFragment(ownerDocument, data){ 1180 if (!ownerDocument) { 1181 ownerDocument = document; 1182 } 1183 if(supportsUnknownElements){ 1184 return ownerDocument.createDocumentFragment(); 1185 } 1186 data = data || getExpandoData(ownerDocument); 1187 var clone = data.frag.cloneNode(), 1188 i = 0, 1189 elems = getElements(), 1190 l = elems.length; 1191 for(;i<l;i++){ 1192 clone.createElement(elems[i]); 1193 } 1194 return clone; 1195 } 1196 1197 /** 1198 * Shivs the `createElement` and `createDocumentFragment` methods of the document. 1199 * @private 1200 * @param {Document|DocumentFragment} ownerDocument The document. 1201 * @param {Object} data of the document. 1202 */ 1203 function shivMethods(ownerDocument, data) { 1204 if (!data.cache) { 1205 data.cache = {}; 1206 data.createElem = ownerDocument.createElement; 1207 data.createFrag = ownerDocument.createDocumentFragment; 1208 data.frag = data.createFrag(); 1209 } 1210 1211 1212 ownerDocument.createElement = function(nodeName) { 1213 //abort shiv 1214 if (!html5.shivMethods) { 1215 return data.createElem(nodeName); 1216 } 1217 return createElement(nodeName, ownerDocument, data); 1218 }; 1219 1220 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' + 1221 'var n=f.cloneNode(),c=n.createElement;' + 1222 'h.shivMethods&&(' + 1223 // unroll the `createElement` calls 1224 getElements().join().replace(/[\w\-:]+/g, function(nodeName) { 1225 data.createElem(nodeName); 1226 data.frag.createElement(nodeName); 1227 return 'c("' + nodeName + '")'; 1228 }) + 1229 ');return n}' 1230 )(html5, data.frag); 1231 } 1232 1233 /*--------------------------------------------------------------------------*/ 1234 1235 /** 1236 * Shivs the given document. 1237 * @memberOf html5 1238 * @param {Document} ownerDocument The document to shiv. 1239 * @returns {Document} The shived document. 1240 */ 1241 function shivDocument(ownerDocument) { 1242 if (!ownerDocument) { 1243 ownerDocument = document; 1244 } 1245 var data = getExpandoData(ownerDocument); 1246 1247 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) { 1248 data.hasCSS = !!addStyleSheet(ownerDocument, 1249 // corrects block display not defined in IE6/7/8/9 1250 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' + 1251 // adds styling not present in IE6/7/8/9 1252 'mark{background:#FF0;color:#000}' + 1253 // hides non-rendered elements 1254 'template{display:none}' 1255 ); 1256 } 1257 if (!supportsUnknownElements) { 1258 shivMethods(ownerDocument, data); 1259 } 1260 return ownerDocument; 1261 } 1262 1263 /*--------------------------------------------------------------------------*/ 1264 1265 /** 1266 * The `html5` object is exposed so that more elements can be shived and 1267 * existing shiving can be detected on iframes. 1268 * @type Object 1269 * @example 1270 * 1271 * // options can be changed before the script is included 1272 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false }; 1273 */ 1274 var html5 = { 1275 1276 /** 1277 * An array or space separated string of node names of the elements to shiv. 1278 * @memberOf html5 1279 * @type Array|String 1280 */ 1281 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video', 1282 1283 /** 1284 * current version of html5shiv 1285 */ 1286 'version': version, 1287 1288 /** 1289 * A flag to indicate that the HTML5 style sheet should be inserted. 1290 * @memberOf html5 1291 * @type Boolean 1292 */ 1293 'shivCSS': (options.shivCSS !== false), 1294 1295 /** 1296 * Is equal to true if a browser supports creating unknown/HTML5 elements 1297 * @memberOf html5 1298 * @type boolean 1299 */ 1300 'supportsUnknownElements': supportsUnknownElements, 1301 1302 /** 1303 * A flag to indicate that the document's `createElement` and `createDocumentFragment` 1304 * methods should be overwritten. 1305 * @memberOf html5 1306 * @type Boolean 1307 */ 1308 'shivMethods': (options.shivMethods !== false), 1309 1310 /** 1311 * A string to describe the type of `html5` object ("default" or "default print"). 1312 * @memberOf html5 1313 * @type String 1314 */ 1315 'type': 'default', 1316 1317 // shivs the document according to the specified `html5` object options 1318 'shivDocument': shivDocument, 1319 1320 //creates a shived element 1321 createElement: createElement, 1322 1323 //creates a shived documentFragment 1324 createDocumentFragment: createDocumentFragment, 1325 1326 //extends list of elements 1327 addElements: addElements 1328 }; 1329 1330 /*--------------------------------------------------------------------------*/ 1331 1332 // expose html5 1333 window.html5 = html5; 1334 1335 // shiv the document 1336 shivDocument(document); 1337 1338 if(typeof module == 'object' && module.exports){ 1339 module.exports = html5; 1340 } 1341 1342}(typeof window !== "undefined" ? window : this, document));
1343/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license */ 1344 1345window.matchMedia || (window.matchMedia = function() { 1346 "use strict"; 1347 1348 // For browsers that support matchMedium api such as IE 9 and webkit 1349 var styleMedia = (window.styleMedia || window.media); 1350 1351 // For those that don't support matchMedium 1352 if (!styleMedia) { 1353 var style = document.createElement('style'), 1354 script = document.getElementsByTagName('script')[0], 1355 info = null; 1356 1357 style.type = 'text/css'; 1358 style.id = 'matchmediajs-test'; 1359 1360 script.parentNode.insertBefore(style, script); 1361 1362 // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers 1363 info = ('getComputedStyle' in window) && window.getComputedStyle(style, null) || style.currentStyle; 1364 1365 styleMedia = { 1366 matchMedium: function(media) { 1367 var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; 1368 1369 // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers 1370 if (style.styleSheet) { 1371 style.styleSheet.cssText = text; 1372 } else { 1373 style.textContent = text; 1374 } 1375 1376 // Test if media query is true or false 1377 return info.width === '1px'; 1378 } 1379 }; 1380 } 1381 1382 return function(media) { 1383 return { 1384 matches: styleMedia.matchMedium(media || 'all'), 1385 media: media || 'all' 1386 }; 1387 }; 1388}()); 1389 1390/*! 1391 * The MIT License 1392 * 1393 * Copyright (c) 2012 James Allardice 1394 * 1395 * Permission is hereby granted, free of charge, to any person obtaining a copy 1396 * of this software and associated documentation files (the "Software"), to 1397 * deal in the Software without restriction, including without limitation the 1398 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 1399 * sell copies of the Software, and to permit persons to whom the Software is 1400 * furnished to do so, subject to the following conditions: 1401 * 1402 * The above copyright notice and this permission notice shall be included in 1403 * all copies or substantial portions of the Software. 1404 * 1405 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1406 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1407 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1408 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1409 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 1410 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 1411 * IN THE SOFTWARE. 1412 */ 1413 1414( function ( global ) { 1415 1416 'use strict'; 1417 1418 // 1419 // Test for support. We do this as early as possible to optimise for browsers 1420 // that have native support for the attribute. 1421 // 1422 1423 var test = document.createElement('input'); 1424 var nativeSupport = test.placeholder !== void 0; 1425 1426 global.Placeholders = { 1427 nativeSupport: nativeSupport, 1428 disable: nativeSupport ? noop : disablePlaceholders, 1429 enable: nativeSupport ? noop : enablePlaceholders 1430 }; 1431 1432 if ( nativeSupport ) { 1433 return; 1434 } 1435 1436 // 1437 // If we reach this point then the browser does not have native support for 1438 // the attribute. 1439 // 1440 1441 // The list of input element types that support the placeholder attribute.
1442 var validTypes = [ 1443 'text', 1444 'search', 1445 'url', 1446 'tel', 1447 'email', 1448 'password', 1449 'number', 1450 'textarea' 1451 ]; 1452 1453 // The list of keycodes that are not allowed when the polyfill is configured 1454 // to hide-on-input. 1455 var badKeys = [ 1456 1457 // The following keys all cause the caret to jump to the end of the input 1458 // value. 1459 1460 27, // Escape 1461 33, // Page up 1462 34, // Page down 1463 35, // End 1464 36, // Home 1465 1466 // Arrow keys allow you to move the caret manually, which should be 1467 // prevented when the placeholder is visible. 1468 1469 37, // Left 1470 38, // Up 1471 39, // Right 1472 40, // Down 1473 1474 // The following keys allow you to modify the placeholder text by removing 1475 // characters, which should be prevented when the placeholder is visible. 1476 1477 8, // Backspace 1478 46 // Delete 1479 ]; 1480 1481 // Styling variables. 1482 var placeholderStyleColor = '#ccc'; 1483 var placeholderClassName = 'placeholdersjs'; 1484 var classNameRegExp = new RegExp('(?:^|\\s)' + placeholderClassName + '(?!\\S)'); 1485 1486 // The various data-* attributes used by the polyfill. 1487 var ATTR_CURRENT_VAL = 'data-placeholder-value'; 1488 var ATTR_ACTIVE = 'data-placeholder-active'; 1489 var ATTR_INPUT_TYPE = 'data-placeholder-type'; 1490 var ATTR_FORM_HANDLED = 'data-placeholder-submit'; 1491 var ATTR_EVENTS_BOUND = 'data-placeholder-bound'; 1492 var ATTR_OPTION_FOCUS = 'data-placeholder-focus'; 1493 var ATTR_OPTION_LIVE = 'data-placeholder-live'; 1494 var ATTR_MAXLENGTH = 'data-placeholder-maxlength'; 1495 1496 // Various other variables used throughout the rest of the script. 1497 var UPDATE_INTERVAL = 100; 1498 var head = document.getElementsByTagName('head')[ 0 ]; 1499 var root = document.documentElement; 1500 var Placeholders = global.Placeholders; 1501 var keydownVal; 1502 1503 // Get references to all the input and textarea elements currently in the DOM 1504 // (live NodeList objects to we only need to do this once). 1505 var inputs = document.getElementsByTagName('input'); 1506 var textareas = document.getElementsByTagName('textarea'); 1507 1508 // Get any settings declared as data-* attributes on the root element. 1509 // Currently the only options are whether to hide the placeholder on focus 1510 // or input and whether to auto-update. 1511 var hideOnInput = root.getAttribute(ATTR_OPTION_FOCUS) === 'false'; 1512 var liveUpdates = root.getAttribute(ATTR_OPTION_LIVE) !== 'false'; 1513 1514 // Create style element for placeholder styles (instead of directly setting 1515 // style properties on elements - allows for better flexibility alongside 1516 // user-defined styles). 1517 var styleElem = document.createElement('style'); 1518 styleElem.type = 'text/css'; 1519 1520 // Create style rules as text node. 1521 var styleRules = document.createTextNode( 1522 '.' + placeholderClassName + ' {' + 1523 'color:' + placeholderStyleColor + ';' + 1524 '}' 1525 ); 1526 1527 // Append style rules to newly created stylesheet. 1528 if ( styleElem.styleSheet ) { 1529 styleElem.styleSheet.cssText = styleRules.nodeValue; 1530 } else { 1531 styleElem.appendChild(styleRules); 1532 } 1533 1534 // Prepend new style element to the head (before any existing stylesheets, 1535 // so user-defined rules take precedence). 1536 head.insertBefore(styleElem, head.firstChild); 1537 1538 // Set up the placeholders. 1539 var placeholder; 1540 var elem; 1541 1542 for ( var i = 0, len = inputs.length + textareas.length; i < len; i++ ) { 1543 1544 // Find the next element. If we've already done all the inputs we move on 1545 // to the textareas. 1546 elem = i < inputs.length ? inputs[ i ] : textareas[ i - inputs.length ]; 1547 1548 // Get the value of the placeholder attribute, if any. IE10 emulating IE7 1549 // fails with getAttribute, hence the use of the attributes node. 1550 placeholder = elem.attributes.placeholder; 1551 1552 // If the element has a placeholder attribute we need to modify it. 1553 if ( placeholder ) { 1554 1555 // IE returns an empty object instead of undefined if the attribute is 1556 // not present. 1557 placeholder = placeholder.nodeValue; 1558 1559 // Only apply the polyfill if this element is of a type that supports 1560 // placeholders and has a placeholder attribute with a non-empty value. 1561 if ( placeholder && inArray(validTypes, elem.type) ) { 1562 newElement(elem); 1563 } 1564 } 1565 } 1566 1567 // If enabled, the polyfill will repeatedly check for changed/added elements 1568 // and apply to those as well. 1569 var timer = setInterval(function () {
1570 for ( var i = 0, len = inputs.length + textareas.length; i < len; i++ ) { 1571 elem = i < inputs.length ? inputs[ i ] : textareas[ i - inputs.length ]; 1572 1573 // Only apply the polyfill if this element is of a type that supports 1574 // placeholders, and has a placeholder attribute with a non-empty value. 1575 placeholder = elem.attributes.placeholder; 1576 1577 if ( placeholder ) { 1578 1579 placeholder = placeholder.nodeValue; 1580 1581 if ( placeholder && inArray(validTypes, elem.type) ) { 1582 1583 // If the element hasn't had event handlers bound to it then add 1584 // them. 1585 if ( !elem.getAttribute(ATTR_EVENTS_BOUND) ) { 1586 newElement(elem); 1587 } 1588 1589 // If the placeholder value has changed or not been initialised yet 1590 // we need to update the display. 1591 if ( 1592 placeholder !== elem.getAttribute(ATTR_CURRENT_VAL) || 1593 ( elem.type === 'password' && !elem.getAttribute(ATTR_INPUT_TYPE) ) 1594 ) { 1595 1596 // Attempt to change the type of password inputs (fails in IE < 9). 1597 if ( 1598 elem.type === 'password' && 1599 !elem.getAttribute(ATTR_INPUT_TYPE) && 1600 changeType(elem, 'text') 1601 ) { 1602 elem.setAttribute(ATTR_INPUT_TYPE, 'password'); 1603 } 1604 1605 // If the placeholder value has changed and the placeholder is 1606 // currently on display we need to change it. 1607 if ( elem.value === elem.getAttribute(ATTR_CURRENT_VAL) ) { 1608 elem.value = placeholder; 1609 } 1610 1611 // Keep a reference to the current placeholder value in case it 1612 // changes via another script. 1613 elem.setAttribute(ATTR_CURRENT_VAL, placeholder); 1614 } 1615 } 1616 } else if ( elem.getAttribute(ATTR_ACTIVE) ) { 1617 hidePlaceholder(elem); 1618 elem.removeAttribute(ATTR_CURRENT_VAL); 1619 } 1620 } 1621 1622 // If live updates are not enabled cancel the timer. 1623 if ( !liveUpdates ) { 1624 clearInterval(timer); 1625 } 1626 }, UPDATE_INTERVAL); 1627 1628 // Disabling placeholders before unloading the page prevents flash of 1629 // unstyled placeholders on load if the page was refreshed. 1630 addEventListener(global, 'beforeunload', function () { 1631 Placeholders.disable(); 1632 }); 1633 1634 // 1635 // Utility functions 1636 // 1637 1638 // No-op (used in place of public methods when native support is detected). 1639 function noop() {} 1640 1641 // Avoid IE9 activeElement of death when an iframe is used. 1642 // 1643 // More info: 1644 // - http://bugs.jquery.com/ticket/13393 1645 // - https://github.com/jquery/jquery/commit/85fc5878b3c6af73f42d61eedf73013e7faae408 1646 function safeActiveElement() { 1647 try { 1648 return document.activeElement; 1649 } catch ( err ) {} 1650 } 1651 1652 // Check whether an item is in an array. We don't use Array.prototype.indexOf 1653 // so we don't clobber any existing polyfills. This is a really simple 1654 // alternative. 1655 function inArray( arr, item ) { 1656 for ( var i = 0, len = arr.length; i < len; i++ ) { 1657 if ( arr[ i ] === item ) { 1658 return true; 1659 } 1660 } 1661 return false; 1662 } 1663 1664 // Cross-browser DOM event binding 1665 function addEventListener( elem, event, fn ) { 1666 if ( elem.addEventListener ) { 1667 return elem.addEventListener(event, fn, false); 1668 } 1669 if ( elem.attachEvent ) { 1670 return elem.attachEvent('on' + event, fn); 1671 } 1672 } 1673 1674 // Move the caret to the index position specified. Assumes that the element 1675 // has focus. 1676 function moveCaret( elem, index ) { 1677 var range; 1678 if ( elem.createTextRange ) { 1679 range = elem.createTextRange(); 1680 range.move('character', index); 1681 range.select(); 1682 } else if ( elem.selectionStart ) { 1683 elem.focus(); 1684 elem.setSelectionRange(index, index); 1685 } 1686 } 1687 1688 // Attempt to change the type property of an input element. 1689 function changeType( elem, type ) { 1690 try { 1691 elem.type = type; 1692 return true; 1693 } catch ( e ) { 1694 // You can't change input type in IE8 and below. 1695 return false; 1696 } 1697 } 1698 1699 function handleElem( node, callback ) { 1700 1701 // Check if the passed in node is an input/textarea (in which case it can't 1702 // have any affected descendants). 1703 if ( node && node.getAttribute(ATTR_CURRENT_VAL) ) { 1704 callback(node); 1705 } else { 1706 1707 // If an element was passed in, get all affected descendants. Otherwise, 1708 // get all affected elements in document. 1709 var handleInputs = node ? node.getElementsByTagName('input') : input
1709s; 1710 var handleTextareas = node ? node.getElementsByTagName('textarea') : textareas; 1711 1712 var handleInputsLength = handleInputs ? handleInputs.length : 0; 1713 var handleTextareasLength = handleTextareas ? handleTextareas.length : 0; 1714 1715 // Run the callback for each element. 1716 var len = handleInputsLength + handleTextareasLength; 1717 var elem; 1718 for ( var i = 0; i < len; i++ ) { 1719 1720 elem = i < handleInputsLength ? 1721 handleInputs[ i ] : 1722 handleTextareas[ i - handleInputsLength ]; 1723 1724 callback(elem); 1725 } 1726 } 1727 } 1728 1729 // Return all affected elements to their normal state (remove placeholder 1730 // value if present). 1731 function disablePlaceholders( node ) { 1732 handleElem(node, hidePlaceholder); 1733 } 1734 1735 // Show the placeholder value on all appropriate elements. 1736 function enablePlaceholders( node ) { 1737 handleElem(node, showPlaceholder); 1738 } 1739 1740 // Hide the placeholder value on a single element. Returns true if the 1741 // placeholder was hidden and false if it was not (because it wasn't visible 1742 // in the first place). 1743 function hidePlaceholder( elem, keydownValue ) { 1744 1745 var valueChanged = !!keydownValue && elem.value !== keydownValue; 1746 var isPlaceholderValue = elem.value === elem.getAttribute(ATTR_CURRENT_VAL); 1747 1748 if ( 1749 ( valueChanged || isPlaceholderValue ) && 1750 elem.getAttribute(ATTR_ACTIVE) === 'true' 1751 ) { 1752 1753 elem.removeAttribute(ATTR_ACTIVE); 1754 elem.value = elem.value.replace(elem.getAttribute(ATTR_CURRENT_VAL), ''); 1755 elem.className = elem.className.replace(classNameRegExp, ''); 1756 1757 // Restore the maxlength value. Old FF returns -1 if attribute not set. 1758 // See GH-56. 1759 var maxLength = elem.getAttribute(ATTR_MAXLENGTH); 1760 if ( parseInt(maxLength, 10) >= 0 ) { 1761 elem.setAttribute('maxLength', maxLength); 1762 elem.removeAttribute(ATTR_MAXLENGTH); 1763 } 1764 1765 // If the polyfill has changed the type of the element we need to change 1766 // it back. 1767 var type = elem.getAttribute(ATTR_INPUT_TYPE); 1768 if ( type ) { 1769 elem.type = type; 1770 } 1771 1772 return true; 1773 } 1774 1775 return false; 1776 } 1777 1778 // Show the placeholder value on a single element. Returns true if the 1779 // placeholder was shown and false if it was not (because it was already 1780 // visible). 1781 function showPlaceholder( elem ) { 1782 1783 var val = elem.getAttribute(ATTR_CURRENT_VAL); 1784 1785 if ( elem.value === '' && val ) { 1786 1787 elem.setAttribute(ATTR_ACTIVE, 'true'); 1788 elem.value = val; 1789 elem.className += ' ' + placeholderClassName; 1790 1791 // Store and remove the maxlength value. 1792 var maxLength = elem.getAttribute(ATTR_MAXLENGTH); 1793 if ( !maxLength ) { 1794 elem.setAttribute(ATTR_MAXLENGTH, elem.maxLength); 1795 elem.removeAttribute('maxLength'); 1796 } 1797 1798 // If the type of element needs to change, change it (e.g. password 1799 // inputs). 1800 var type = elem.getAttribute(ATTR_INPUT_TYPE); 1801 if ( type ) { 1802 elem.type = 'text'; 1803 } else if ( elem.type === 'password' && changeType(elem, 'text') ) { 1804 elem.setAttribute(ATTR_INPUT_TYPE, 'password'); 1805 } 1806 1807 return true; 1808 } 1809 1810 return false; 1811 } 1812 1813 // Returns a function that is used as a focus event handler. 1814 function makeFocusHandler( elem ) { 1815 return function () { 1816 1817 // Only hide the placeholder value if the (default) hide-on-focus 1818 // behaviour is enabled. 1819 if ( 1820 hideOnInput && 1821 elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && 1822 elem.getAttribute(ATTR_ACTIVE) === 'true' 1823 ) { 1824 1825 // Move the caret to the start of the input (this mimics the behaviour 1826 // of all browsers that do not hide the placeholder on focus). 1827 moveCaret(elem, 0); 1828 } else { 1829 1830 // Remove the placeholder. 1831 hidePlaceholder(elem); 1832 } 1833 }; 1834 } 1835 1836 // Returns a function that is used as a blur event handler. 1837 function makeBlurHandler( elem ) { 1838 return function () { 1839 showPlaceholder(elem); 1840 }; 1841 } 1842 1843 // Returns a function that is used as a submit event handler on form elements 1844 // that have children affected by this polyfill. 1845 function makeSubmitHandler( form ) { 1846 return function () { 1847 1848 // Turn off placeholders on all appropriate descendant elements. 1849 disablePlaceholders(form); 1850 }; 1851 } 1852 1853 // Functions that are used as a event handlers when the hide-on-input 1854 // behaviour has been activated - very basic implementation of the 'input' 1855 // event. 1856 function makeKeydownHandler( elem ) { 1857 return function ( e ) { 1858 keydownVal = elem.value; 1859 1860 // Prevent the use of the arrow keys (try to keep the cursor before the 1861 // placeholder). 1862 if ( 1863 elem.getAttribute(ATTR_ACTIVE) === 'true' && 1864 keydownVal === elem.getAttribute(ATTR_CURRENT_VAL) && 1865 inArray(badKeys, e.keyCode) 1866 ) { 1867 if ( e.preventDefault ) { 1868 e.preventDefault(); 1869 } 1870 return false;
1871 } 1872 }; 1873 } 1874 1875 function makeKeyupHandler(elem) { 1876 return function () { 1877 hidePlaceholder(elem, keydownVal); 1878 1879 // If the element is now empty we need to show the placeholder 1880 if ( elem.value === '' ) { 1881 elem.blur(); 1882 moveCaret(elem, 0); 1883 } 1884 }; 1885 } 1886 1887 function makeClickHandler(elem) { 1888 return function () { 1889 if ( 1890 elem === safeActiveElement() && 1891 elem.value === elem.getAttribute(ATTR_CURRENT_VAL) && 1892 elem.getAttribute(ATTR_ACTIVE) === 'true' 1893 ) { 1894 moveCaret(elem, 0); 1895 } 1896 }; 1897 } 1898 1899 // Bind event handlers to an element that we need to affect with the 1900 // polyfill. 1901 function newElement( elem ) { 1902 1903 // If the element is part of a form, make sure the placeholder string is 1904 // not submitted as a value. 1905 var form = elem.form; 1906 if ( form && typeof form === 'string' ) { 1907 1908 // Get the real form. 1909 form = document.getElementById(form); 1910 1911 // Set a flag on the form so we know it's been handled (forms can contain 1912 // multiple inputs). 1913 if ( !form.getAttribute(ATTR_FORM_HANDLED) ) { 1914 addEventListener(form, 'submit', makeSubmitHandler(form)); 1915 form.setAttribute(ATTR_FORM_HANDLED, 'true'); 1916 } 1917 } 1918 1919 // Bind event handlers to the element so we can hide/show the placeholder 1920 // as appropriate. 1921 addEventListener(elem, 'focus', makeFocusHandler(elem)); 1922 addEventListener(elem, 'blur', makeBlurHandler(elem)); 1923 1924 // If the placeholder should hide on input rather than on focus we need 1925 // additional event handlers 1926 if (hideOnInput) { 1927 addEventListener(elem, 'keydown', makeKeydownHandler(elem)); 1928 addEventListener(elem, 'keyup', makeKeyupHandler(elem)); 1929 addEventListener(elem, 'click', makeClickHandler(elem)); 1930 } 1931 1932 // Remember that we've bound event handlers to this element. 1933 elem.setAttribute(ATTR_EVENTS_BOUND, 'true'); 1934 elem.setAttribute(ATTR_CURRENT_VAL, placeholder); 1935 1936 // If the element doesn't have a value and is not focussed, set it to the 1937 // placeholder string. 1938 if ( hideOnInput || elem !== safeActiveElement() ) { 1939 showPlaceholder(elem); 1940 } 1941 } 1942 1943}(this) ); 1944// IE spupport comes in IE10 1945(function rAFPolyfill() { 1946 var lastTime, vendors, x; 1947 lastTime = 0; 1948 vendors = ["webkit", "moz"]; 1949 x = 0; 1950 while (x < vendors.length && !window.requestAnimationFrame) { 1951 window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"]; 1952 window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"]; 1953 ++x; 1954 } 1955 if (!window.requestAnimationFrame) { 1956 window.requestAnimationFrame = function(callback, element) { 1957 var currTime, id, timeToCall; 1958 currTime = new Date().getTime(); 1959 timeToCall = Math.max(0, 16 - (currTime - lastTime)); 1960 id = window.setTimeout(function() { 1961 callback(currTime + timeToCall); 1962 }, timeToCall); 1963 lastTime = currTime + timeToCall; 1964 return id; 1965 }; 1966 } 1967 if (!window.cancelAnimationFrame) { 1968 window.cancelAnimationFrame = function(id) { 1969 clearTimeout(id); 1970 }; 1971 } 1972})(); 1973 1974 1975 1976(function($) { 1977 'use strict'; 1978 1979 var MK = window.MK || {}; 1980 1981 /** 1982 * MK.core holds most important methods that bootstraps whole application 1983 * 1984 * @type {Object} 1985 */ 1986 MK.core = {}; 1987 1988 1989 1990 /** 1991 * State for referance of already loaded script files 1992 * @type {Array} 1993 */ 1994 var _loadedDependencies = []; 1995 1996 /** 1997 * State of queue represented as pairs of script ref => callback 1998 * @type {Object} 1999 */ 2000 var _inQueue = {}; 2001 2002 /** 2003 * Initializes all components in given scope (object or DOM reference) based on data attribute and 'pointer' css class '.js-el'. 2004 * DOM work is reduced by single traversing for pointer class and later filtering through cached object. It expects init() method 2005 * on every component. Component itself should be defined in MK.component namespace and assign to DOM element via data-mk-component. 2006 * Use it once on DOM ready with document as a scope. For partial initialization after aja
2006x operations pass as a scope element 2007 * where new DOM was inserted. 2008 * 2009 * @param {string|object} 2010 */ 2011 MK.core.initAll = function( scope ) { 2012 var $el = $( scope ).find( '.js-el' ), // single traversing 2013 $components = $el.filter( '[data-mk-component]' ), 2014 component = null; 2015 2016 2017 // initialize component 2018 var init = function init(name, el) { 2019 var $el = $(el); 2020 2021 if ( $el.data('init-' + name) ) return; // do not initialize the same module twice 2022 2023 if ( typeof MK.component[ name ] !== 'function' ) console.log('Component init error: ', name); 2024 else { 2025 component = new MK.component[ name ]( el ); 2026 component.init(); 2027 $el.data('init-' + name, true); // mark as initialised 2028 // TODO add name 2029 MK.utils.eventManager.publish('component-inited'); 2030 } 2031 }; 2032 2033 $components.each( function() { 2034 var self = this, 2035 $this = $( this ), 2036 names = $this.data( 'mk-component' ); 2037 2038 if( typeof names === 'string' ) { 2039 var name = names; // containes only single name. Keep it transparent. 2040 init(name, self); 2041 } else { 2042 names.forEach( function( name ) { 2043 init(name, self); 2044 }); 2045 } 2046 }); 2047 }; 2048 2049 /** 2050 * Async loader for 3rd party plugins available from within theme or external CDNs / APIs. 2051 * Take one argument as callback which is run when loading is finished. Also keeps track of already loaded scripts 2052 * and prevent duplication. Holds in queue multiple callbacks that where defined in different places but depend on the 2053 * same plugin. 2054 * 2055 * TODO: heavy test for multiple dependencies and crosssharing one dependency and different one dependency in queue, 2056 * bulletproof with single dependency 2057 * 2058 * @example MK.core.loadDependencies([MK.core.path.plugins + 'plugin.js'], function() { 2059 * // do something when plugin is loaded 2060 * }) 2061 * 2062 * @param {array} 2063 * @param {function} 2064 */ 2065 MK.core.loadDependencies = function( dependencies, callback ) { 2066 var _callback = callback || function() {}; 2067 2068 if( !dependencies ) { 2069 // If no dependencies defined then run _callback imidietelly 2070 _callback(); 2071 return; 2072 } 2073 2074 // Check for new dependencies 2075 var newDeps = dependencies.map( function( dep ) { 2076 if( _loadedDependencies.indexOf( dep ) === -1 ) { 2077 if( typeof _inQueue[ dep ] === 'undefined' ) { 2078 // console.log( dep ); 2079 return dep; 2080 } else { 2081 _inQueue[ dep ].push( _callback ); 2082 return true; 2083 } 2084 } else { 2085 return false; 2086 } 2087 }); 2088 2089 // The dependency is not new but it's not resolved yet 2090 // Callback is added to queue that will be run after the script is loaded 2091 // Don't run callback just yet. 2092 if( newDeps[0] === true ) {
2093 // console.log('Waiting for ' + dependencies[0]); 2094 return; 2095 } 2096 2097 // Dependency was loaded previously. We can run callback safely 2098 if( newDeps[0] === false ) { 2099 _callback(); 2100 return; 2101 } 2102 2103 // Create queue and relationship script -> callback array to track 2104 // all callbacks that waits for ths script 2105 var queue = newDeps.map( function( script ) { 2106 // console.log( script ); 2107 _inQueue[ script ] = [ _callback ]; 2108 return $.getCachedScript( script ); 2109 }); 2110 2111 // Callbacks invoking 2112 var onLoad = function onLoad() { 2113 var index; 2114 newDeps.map( function( loaded ) { 2115 _inQueue[ loaded ].forEach( function( callback ) { 2116 callback(); 2117 }); 2118 delete _inQueue[ loaded ]; 2119 _loadedDependencies.push( loaded ); 2120 }); 2121 }; 2122 2123 // Run callbacks when promise is resolved 2124 $.when.apply( null, queue ).done( onLoad ); 2125 }; 2126 2127 /** 2128 * Single namespace for all paths recuired in application. 2129 * @type {Object} 2130 */ 2131 MK.core.path = { 2132 theme : mk_theme_dir, 2133 plugins : mk_theme_js_path + '/plugins/async/min/', 2134 ajaxUrl : window.PHP.ajax 2135 }; 2136 2137 2138})(jQuery); 2139(function($) { 2140 'use strict'; 2141 2142 var MK = window.MK || {}; 2143 MK.utils = window.MK.utils || {}; 2144 2145 /** 2146 * Enables to evaluate common methods through DOM JSON references by invoking from object with bracket notation MK.utils[var][var] 2147 * @type {Object} 2148 */ 2149 MK.utils.actions = {}; 2150 2151 MK.utils.actions.activate = function (el) { 2152 $(el).addClass('is-active'); 2153 }; 2154 2155 MK.utils.actions.deactivate = function (el) { 2156 $(el).removeClass('is-active'); 2157 }; 2158 2159}(jQuery)); 2160(function($) { 2161 'use strict'; 2162 2163 var MK = window.MK || {}; 2164 MK.utils = window.MK.utils || {}; 2165 2166 /** 2167 * Gets user browser and its version 2168 * @return {Object} => {name, version} 2169 */ 2170 MK.utils.browser = (function() { 2171 var dataBrowser = [ 2172 {string: navigator.userAgent, subString: "Edge", identity: "Edge"}, 2173 {string: navigator.userAgent, subString: "Chrome", identity: "Chrome"}, 2174 {string: navigator.userAgent, subString: "MSIE", identity: "IE"}, 2175 {string: navigator.userAgent, subString: "Trident", identity: "IE"}, 2176 {string: navigator.userAgent, subString: "Firefox", identity: "Firefox"}, 2177 {string: navigator.userAgent, subString: "Safari", identity: "Safari"}, 2178 {string: navigator.userAgent, subString: "Opera", identity: "Opera"} 2179 ]; 2180 2181 var versionSearchString = null; 2182 var searchString = function (data) { 2183 for (var i = 0; i < data.length; i++) { 2184 var dataString = data[i].string; 2185 versionSearchString = data[i].subString; 2186 2187 if (dataString.indexOf(data[i].subString) !== -1) { 2188 return data[i].identity; 2189 } 2190 } 2191 }; 2192 2193 var searchVersion = function (dataString) { 2194 var index = dataString.indexOf(versionSearchString); 2195 if (index === -1) { 2196 return; 2197 } 2198 2199 var rv = dataString.indexOf("rv:"); 2200 if (versionSearchString === "Trident" && rv !== -1) { 2201 return parseFloat(dataString.substring(rv + 3)); 2202 } else { 2203 return parseFloat(dataString.substring(index + versionSearchString.length + 1)); 2204 } 2205 }; 2206 2207 var name = searchString(dataBrowser) || "Other"; 2208 var version = searchVersion(navigator.userAgent) || searchVersion(navigator.appVersion) || "Unknown"; 2209 2210 // Expose for css 2211 $('html').addClass(name).addClass(name + version); 2212 2213 2214 return { 2215 name : name, 2216 version : version 2217 }; 2218 2219 })(); 2220 2221 /** 2222 * Gets user operating system 2223 * @return {String} 2224 */ 2225 MK.utils.OS = (function() { 2226 if (navigator.appVersion.indexOf("Win")!=-1) return "Windows"; 2227 if (navigator.appVersion.indexOf("Mac")!=-1) return "OSX"; 2228 if (navigator.appVersion.indexOf("X11")!=-1) return "UNIX"; 2229 if (navigator.appVersion.indexOf("Linux")!=-1) return "Linux"; 2230 })(); 2231 2232 /** 2233 * Check if mobile device. 2234 * @return {Boolean} 2235 */ 2236 MK.utils.isMobile = function() { 2237 // Problems with bigger tablets as users raport differences with behaviour. Switch to navigator sniffing 2238 // return ('ontouchstart' in document.documentElement) && matchMedia( '(max-width: 1024px)' ).matches; 2239 2240 // http://www.abeautifulsite.net/detecting-mobile-devices-with-javascript/ 2241 // if it still brings problem try to move to more sophisticated solution like 2242 // apachemobilefilter.org 2243 // detectright.com 2244 // web.wurfl.io 2245 // 2246 // Seems as best solution here: 2247 // hgoebl.github.io/mobile-detect.js 2248 2249 function android() { 2250 return navigator.userAgent.match(/Android/i); 2251 } 2252 2253 function blackBerry() { 2254 return navigator.userAgent.match(/BlackBerry/i); 2255 } 2256 2257 function iOS() { 2258 return navigator.userAgent.match(/iPhone|iPad|iPod/i); 2259 } 2260 2261 function opera() { 2262 return navigator.userAgent.match(/Opera Mini/i); 2263 } 2264 2265 function windows() { 2266 return navigator.userAgent.match(/IEMobile/i); 2267 } 2268 2269 return (android() || blackBerry() || iOS() || opera() || windows() || matchMedia( '(max-width: 1024px)' ).matches); 2270 2271 }; 2272 2273 /** 2274 * Check if menu is switched to responsive state based on user width settings 2275 * @return {Boolean} 2276 */ 2277 MK.utils.isResponsiveMenuState = function() { 2278 return window.matchMedia( '(max-width: '+ mk_responsive_nav_width +'px)').matches; 2279 }; 2280 2281 2282 2283 MK.utils.getUrlParameter = function getUrlParameter(sParam) { 2284 var sPageURL = decodeURIComponent(window.location.search.substring(1)), 2285 sURLVariables = sPageURL.split('&'), 2286 sParameterName, 2287 i; 2288 2289 for (i = 0; i < sURLVariables.length; i++) { 2290 sParameterName = sURLVariables[i].split('='); 2291 2292 if (sParameterName[0] === sParam) { 2293 return sParameterName[1] === undefined ? true : sParameterName[1]; 2294 } 2295 } 2296 }; 2297 2298 2299 MK.utils.isSmoothScroll = (function() { 2300 var isUserChoice = (mk_smooth_scroll === 'true'); 2301 2302 // We notify our app about smooth scroll option when user choose it from admin panel. 2303 return isUserChoice; 2304 }()); 2305 2306}(jQuery)); 2307(function($) { 2308 'use strict'; 2309 2310 var MK = window.MK || {}; 2311 MK.utils = window.MK.utils || {}; 2312 2313 /** 2314 * Basic implementation of pub / sub pattern to avoid tight coupling with direct module communication 2315 * @type {Object} 2316 */ 2317 MK.utils.eventManager = {}; 2318 2319 /** 2320 * Subscribe to custom event and run callbacks 2321 * @param {String} 2322 * @param {Function} 2323 * 2324 * @usage MK.utils.eventManager.subscribe('event', function(e, params) {} ) 2325 */ 2326 MK.utils.eventManager.subscribe = function(evt, func) { 2327 $(this).on(evt, func); 2328 }; 2329 2330 /** 2331 * Unsubscribe from custom event 2332 * @param {String} 2333 * @param {Function} 2334 */ 2335 MK.utils.eventManager.unsubscribe = function(evt, func) { 2336 $(this).off(evt, func); 2337 }; 2338 2339 /** 2340 * Publish custom event to notify appliaction about state change 2341 * @param {String} 2342 * 2343 * @usage MK.utils.eventManager.publish('event', { 2344 * param: val 2345 * }) 2346 */ 2347 MK.utils.eventManager.publish = function(evt, params) { 2348 $(this).trigger(evt, [params]); 2349 }; 2350 2351}(jQuery)); 2352(function($) { 2353 'use strict'; 2354 2355 var MK = window.MK || {}; 2356 MK.utils = window.MK.utils || {}; 2357 2358 /** 2359 * Control browser fullscreen mode 2360 * @type {Object} 2361 */ 2362 MK.utils.fullscreen = {}; 2363 2364 // TODO: move to namespace 2365 MK.utils.launchIntoFullscreen = function ( element ) { 2366 if(element.requestFullscreen) { 2367 element.requestFullscreen(); 2368 } else if(element.mozRequestFullScreen) { 2369 element.mozRequestFullScreen(); 2370 } else if(element.webkitRequestFullscreen) { 2371 element.webkitRequestFullscreen(); 2372 } else if(element.msRequestFullscreen) { 2373 element.msRequestFullscreen(); 2374 } 2375 }; 2376 2377 MK.utils.exitFullscreen = function () { 2378 if(document.exitFullscreen) { 2379 document.exitFullscreen(); 2380 } else if(document.mozCancelFullScreen) { 2381 document.mozCancelFullScreen(); 2382 } else if(document.webkitExitFullscreen) { 2383 document.webkitExitFullscreen(); 2384 } 2385 }; 2386 2387}(jQuery)); 2388(function($) { 2389 'use strict'; 2390 2391 var MK = window.MK || {}; 2392 MK.utils = window.MK.utils || {}; 2393 2394 MK.utils.misc = {}; 2395 // TODO: move to namespace 2396 2397 /** 2398 * Get all top offsets from jQuery collection 2399 * 2400 * @param {$Objects} 2401 * @return {Aray} 2402 */ 2403 MK.utils.offsets = function( $els ) { 2404 return $.map( $els, function( el ) { 2405 return $( el ).offset().top; 2406 }); 2407 }; 2408 2409 /** 2410 * Retrive from array of numbers first number that is higher than given parameter 2411 * 2412 * @param {Number} 2413 * @param {Array} 2414 * @return {Number} 2415 */ 2416 MK.utils.nextHigherVal = function( val, arr ) { 2417 var i = 0, 2418 higher = null; 2419 2420 var check = function() { 2421 if( val > arr[ i ]) { 2422 i += 1; 2423 check(); 2424 } else { 2425 higher = arr[ i ]; 2426 } 2427 }; 2428 check(); 2429 2430 return higher; 2431 }; 2432 2433 2434 MK.utils.throttle = function( delay, fn ) { 2435 var last; 2436 var deferTimer; 2437 2438 return function() { 2439 var context = this; 2440 var args = arguments; 2441 var now = +new Date; 2442 if( last && now < last + delay ) { 2443 clearTimeout( deferTimer ); 2444 deferTimer = setTimeout( function() { 2445 last = now; fn.apply( context, args ); 2446 }, delay ); 2447 } else { 2448 last = now; 2449 fn.apply( context, args ); 2450 } 2451 }; 2452 }; 2453 2454 MK.utils.isElementInViewport = function( el ) { 2455 var elemTop = el.getBoundingClientRect().top; 2456 var isVisible = (elemTop < window.innerHeight); 2457 return isVisible; 2458 }; 2459 2460})(jQuery); 2461(function($) { 2462 'use strict'; 2463 2464 var MK = window.MK || {}; 2465 MK.utils = window.MK.utils || {}; 2466 2467 /** 2468 * Scrolls page to static pixel offset 2469 * @param {Number} 2470 */ 2471 MK.utils.scrollTo = function( offset ) { 2472 $('html, body').stop().animate({ 2473 scrollTop: offset 2474 }, { 2475 duration: 1200, 2476 easing: "easeInOutExpo" 2477 }); 2478 }; 2479 2480 /** 2481 * Scrolls to element passed in as object or DOM reference 2482 * @param {String|Object} 2483 */ 2484 MK.utils.scrollToAnchor = function( hash ) { 2485 // Escape meta-chars from hash name only. 2486 hash = hash.substring(1).replace(/[!"#$%&'()*+,./:;<=>?@[\]^`{|}~]/g, "\\$&"); 2487 hash = "#" + hash; 2488 var $target = $( hash ); 2489 // console.log( hash ); 2490 2491 if( ! $target.length ) return; 2492 2493 var offset = $target.offset().top; 2494 offset = offset - MK.val.offsetHeaderHeight( offset ); 2495 2496 if( hash === '#top-of-page' ) window.history.replaceState( undefined, undefined, ' ' ); 2497 else window.history.replaceState( undefined, undefined, hash ); 2498 2499 MK.utils.scrollTo( offset ); 2500 }; 2501 2502 /** 2503 * Controls native scroll behaviour 2504 * @return {Object} => {disable, enable} 2505 */ 2506 MK.utils.scroll = (function() { 2507 // 37 - left arror, 38 - up arrow, 39 right arrow, 40 down arrow 2508 var keys = [38, 40]; 2509 2510 function preventDefault(e) { 2511 e = e || window.event; 2512 e.preventDefault(); 2513 e.returnValue = false; 2514 } 2515 2516 function wheel(e) { 2517 preventDefault(e); 2518 } 2519 2520 function keydown(e) { 2521 for (var i = keys.length; i--;) { 2522 if (e.keyCode === keys[i]) { 2523 preventDefault(e); 2524 return; 2525 } 2526 } 2527 } 2528 2529 function disableScroll() { 2530 if (window.addEventListener) { 2531 window.addEventListener('DOMMouseScroll', wheel, false); 2532 } 2533 window.onmousewheel = document.onmousewheel = wheel; 2534 document.onkeydown = keydown; 2535 } 2536 2537 function enableScroll() { 2538 if (window.removeEventListener) { 2539 window.removeEventListener('DOMMouseScroll', wheel, false); 2540 } 2541 window.onmousewheel = document.onmousewheel = document.onkeydown = null; 2542 } 2543 2544 return { 2545 disable : disableScroll, 2546 enable : enableScroll 2547 }; 2548 2549 })(); 2550 2551 /** 2552 * Checks if passed link element has anchor inside current page. Returns string like '#anchor' if so or false 2553 * @param {String|Object} 2554 * @return {String|Boolean} 2555 */ 2556 MK.utils.detectAnchor = function( el ) { 2557 var $this = $( el ), 2558 loc = window.location, 2559 currentPage = loc.origin + loc.pathname, 2560 href = $this.attr( 'href' ), 2561 linkSplit = (href) ? href.split( '#' ) : '', 2562 hrefPage = linkSplit[0] ? linkSplit[0] : '', 2563 hrefHash = linkSplit[1] ? linkSplit[1] : ''; 2564 2565 if( typeof hrefHash !== 'undefined' && hrefHash !== '' ) { 2566 return '#' + hrefHash; 2567 } else { 2568 return false;
2569 } 2570 }; 2571 2572 /** 2573 * This should be invoked only on page load. 2574 * Scrolls to anchor from address bar 2575 */ 2576 MK.utils.scrollToURLHash = function() { 2577 var loc = window.location, 2578 hash = loc.hash; 2579 2580 if ( hash.length && hash.substring(1).length ) { 2581 // !loading is added early after DOM is ready to prevent native jump to anchor 2582 hash = hash.replace( '!loading', '' ); 2583 2584 // Wait for one second before animating 2585 // Most of UI animations should be done by then and async operations complited 2586 setTimeout( function() { 2587 MK.utils.scrollToAnchor( hash ); 2588 }, 1000 ); 2589 2590 // Right after reset back address bar 2591 setTimeout( function() { 2592 window.history.replaceState(undefined, undefined, hash); 2593 }, 1001); 2594 } 2595 }; 2596 2597 /** 2598 * Scroll Spy implementation. Spy dynamic offsets of elements or static pixel offset 2599 * @param {Number|Element} 2600 * @param {Object} => callback object {before, active, after} 2601 */ 2602 MK.utils.scrollSpy = function( toSpy, config ) { 2603 var $window = $( window ), 2604 container = document.getElementById( 'mk-theme-container' ), 2605 isObj = ( typeof toSpy === 'object' ), 2606 offset = (isObj) ? MK.val.dynamicOffset( toSpy, config.position, config.threshold ) : function() { return toSpy; }, 2607 height = (isObj) ? MK.val.dynamicHeight( toSpy ) : function() { return 0; }, 2608 cacheVals = {}, 2609 _p = 'before'; // current position 2610 2611 var checkPosition = function() { 2612 var s = MK.val.scroll(), 2613 o = offset(), 2614 h = height(); 2615 2616 if( s < o && _p !== 'before' ) { 2617 // console.log( toSpy, 'before' ); 2618 if( config.before ) config.before(); 2619 _p = 'before'; 2620 } 2621 else if( s >= o && s <= o + h && _p !== 'active' ) { 2622 // console.log( toSpy, 'active' ); 2623 if( config.active ) config.active( o ); 2624 _p = 'active'; 2625 } 2626 else if( s > o + h && _p !== 'after' ) { 2627 // console.log( toSpy, 'after' ); 2628 if( config.after) config.after( o + h ); 2629 _p = 'after'; 2630 } 2631 }; 2632 2633 var rAF = function() { 2634 window.requestAnimationFrame( checkPosition ); 2635 }; 2636 2637 var exportVals = function() { 2638 return cacheVals; 2639 }; 2640 2641 var updateCache = function() { 2642 var o = offset(), 2643 h = height(); 2644 2645 cacheVals = { 2646 before : o - $window.height(), 2647 active : o, 2648 after : o + h 2649 }; 2650 }; 2651 2652 if( config.cache ) { 2653 config.cache( exportVals ); 2654 } 2655 2656 checkPosition(); 2657 $window.on( 'load', checkPosition ); 2658 $window.on( 'resize', checkPosition ); 2659 $window.on( 'mouseup', checkPosition ); 2660 window.addResizeListener( container, checkPosition ); 2661 2662 $window.on( 'scroll', rAF ); 2663 2664 updateCache(); 2665 $window.on( 'load', updateCache ); 2666 $window.on( 'resize', updateCache ); 2667 window.addResizeListener( container, updateCache ); 2668 }; 2669 2670}(jQuery)); 2671(function($) { 2672 'use strict'; 2673 2674 // Create delagation event handler to behave as "live" listener. We may provide new elements with ajax etc later 2675 // Just add js-taphover class whatever element you'd like to immidietely bring hover on touch devices 2676 $("body").on("click touchend", '.js-taphover', function (e) { 2677 var $link = $(e.currentTarget); // grab target 2678 var $target = $(e.target); 2679 2680 // Rather than ":hover" state we operate on ".hover" class which gives us more control and chance to emulate it on click 2681 // yet it is easy to reason about in our CSS 2682 if ($link.hasClass('hover')) { 2683 return true; 2684 } else if ( MK.utils.isMobile() ) { 2685 if ( ($target.hasClass('hover-icon') || $target.closest('.hover-icon').length) && !$target.closest('.js-taphover').hasClass('hover') ) { 2686 e.preventDefault(); 2687 } 2688 $link.addClass('hover'); 2689 $('.js-taphover').not(e.currentTarget).removeClass('hover'); // remove it from previous element 2690 e.stopPropagation(); // do not leak to document root if expected element was touched 2691 } 2692 }); 2693 2694 // Whenever click leaks to the root romve all hover classes 2695 $(document).on("click", function(e) { 2696 $('.js-taphover').removeClass('hover'); 2697 }); 2698 2699}(jQuery)); 2700// (function() { 2701// 'use strict'; 2702 2703// // Make sure the video behaves like background-size: cover 2704// window.videoCover = function( holderSelector, videoSelector ) { 2705// var videos = document.querySelectorAll( videoSelector ), 2706// holder = document.querySelectorAll( holderSelector )[0]; 2707 2708// [].forEach.call(videos, function(video) { 2709 2710// var videoAspectRatio; 2711
2712// resizeBackground(); 2713 2714// video.onloadedmetadata = function() { 2715// // get images aspect ratio 2716// videoAspectRatio = this.height / this.width; 2717// // attach resize event and fire it once 2718// window.onresize = resizeBackground; 2719// resizeBackground(); 2720// }; 2721 2722// function resizeBackground() { 2723// // get window size and aspect ratio 2724// var holderWidth = holder.innerWidth, 2725// holderHeight = holder.innerHeight, 2726// holderAspectRatio = holderHeight / holderWidth; 2727 2728// //compare holder ratio to image ratio so you know which way the image should fill 2729// if ( holderAspectRatio < videoAspectRatio ) { 2730// // we are fill width 2731// video.style.width = holderWidth + "px"; 2732// // and applying the correct aspect to the height now 2733// video.style.height = (holderWidth * videoAspectRatio) + "px"; // this can be margin if your element is not positioned relatively, absolutely or fixed 2734// // make sure image is always centered 2735// video.style.left = "0px";
2736// video.style.top = (holderHeight - (holderWidth * videoAspectRatio)) / 2 + "px"; 2737// } else { // same thing as above but filling height instead 2738// video.style.height = holderHeight + "px"; 2739// video.style.width = (holderHeight / videoAspectRatio) + "px"; 2740// video.style.left = (holderWidth - (holderHeight / videoAspectRatio)) / 2 + "px"; 2741// video.style.top = "0px"; 2742// } 2743// } 2744 2745// }); 2746// }; 2747 2748// }()); 2749// 2750// 2751// 2752// TODO it is temp only. make it as a plugin 2753 2754(function($) { 2755 'use strict'; 2756 2757 var $videoHolder = $('.mk-center-video'), 2758 $wrapper = $videoHolder.parent(), 2759 baseAspectRatio = 56.25; 2760 2761 var wrapperHeight, 2762 wrapperWidth, 2763 wrapperAspectRatio; 2764 2765 function calc() { 2766 wrapperHeight = $wrapper.height(); 2767 wrapperWidth = $wrapper.width(); 2768 wrapperAspectRatio = (wrapperHeight / wrapperWidth) * 100; 2769 } 2770 2771 function apply() { 2772 var width = (wrapperAspectRatio / baseAspectRatio) * 100, 2773 widthOverflow = (width - 100); 2774 2775 $videoHolder.css({ 2776 'padding-top': wrapperAspectRatio + '%', 2777 'width': width + '%', 2778 'left': -(widthOverflow / 2) + '%' 2779 }); 2780 } 2781 2782 function reset() { 2783 $videoHolder.css({ 2784 'padding-top': baseAspectRatio + '%', 2785 'width': 100 + '%', 2786 'left': 0 2787 }); 2788 } 2789 2790 function setCover() { 2791 reset(); 2792 calc(); 2793 if(wrapperAspectRatio > baseAspectRatio) apply(); 2794 } 2795 2796 $(window).on('load', setCover); 2797 $(window).on('resize', setCover); 2798 2799 2800}(jQuery)); 2801(function($) { 2802 'use strict'; 2803 2804 var MK = window.MK || {}; 2805 2806 /** 2807 * MK.val is collection of Lambdas responsible for returning up to date values of method type like scrollY or el offset. 2808 * The Lambda is responsible for keeping track of value of a particular property, usually takes as argument an object 2809 * (or DOM reference) and internally creates and updates data that is returned as primitive value - through variable reference. 2810 * 2811 * Benefits of this approach: 2812 * - reduced DOM reads 2813 * - auto-updating values without need for additional logic where methods are called 2814 * - updating values when needed to be updated not read 2815 * 2816 * Downsides: 2817 * - Memory overhead with closures and keeping state in memory ( still beter than read state from DOM, but use wisely - 2818 * do not use it when you really need static value on runtime ) 2819 */ 2820 MK.val = {}; 2821 2822 /** 2823 * Current window offsetY position 2824 * 2825 * @uses MK.val.scroll() 2826 * @return {number} 2827 */ 2828 MK.val.scroll = (function() { 2829 var offset = 0, 2830 $window = $(window), 2831 hasPageYOffset = (window.pageYOffset !== undefined), 2832 body = (document.documentElement || document.body.parentNode || document.body); // cross browser handling 2833 2834 var update = function() { 2835 offset = hasPageYOffset ? window.pageYOffset : body.scrollTop; 2836 }; 2837 2838 var rAF = function() { 2839 window.requestAnimationFrame(update); 2840 }; 2841 2842 update(); 2843 $window.on('load', update); 2844 $window.on('resize', update); 2845 $window.on('scroll', rAF); 2846 2847 return function() { 2848 return offset; 2849 }; 2850 })(); 2851 2852 2853 /** 2854 * Changes number of percent to pixels based on viewport height 2855 * 2856 * @uses MK.val.viewportPercentHeight({percent val}) 2857 * @param {number} 2858 * @return {number} 2859 */ 2860 MK.val.viewportPercentHeight = function(percent) { 2861 return $(window).height() * (percent / 100); 2862 }; 2863 2864 2865 /** 2866 * Wordpress adminbar height based on wp media queries 2867 * @return {Number} 2868 */ 2869 MK.val.adminbarHeight = function() { 2870 if (php.hasAdminbar) { 2871 // apply WP native media-query and sizes 2872 return (window.matchMedia('( max-width: 782px )').matches) ? 46 : 32; 2873 } else { 2874 return 0; 2875 } 2876 }; 2877 2878 2879 /** 2880 * Offset when header becomes sticky. Evaluates viewport % and header height to pixels for according options 2881 * @return {Number} 2882 */ 2883 MK.val.stickyOffset = (function() { 2884 var $header = $('.mk-header').not('.js-header-shortcode').first(); 2885 2886 // We need to have returning function even when header is disabled 2887 if (!$header.length) { 2888 return function() { 2889 return 0; 2890 }; 2891 } 2892 2893 2894 2895 var $toolbar = $header.find('.mk-header-toolbar'), 2896 config = $header.data(), 2897 hasToolbar = $toolbar.length, 2898 toolbarHeight = (hasToolbar) ? $toolbar.height() : 0, 2899 isVertical = (config.headerStyle === 4), 2900 headerHeight = (isVertical) ? 0 : config.height; 2901 2902 var type = ((typeof config.stickyOffset === 'number') ? 'number' : false) || 2903 ((config.stickyOffset === 'header') ? 'header' : false) || 2904 'percent'; 2905 2906 var stickyOffset = 0; 2907 var setOffset = function() { 2908 2909 //we calculate toolbar height for When the device is changed Size 2910 //Toolbar height in responsive state is 0 2911 toolbarHeight = (hasToolbar) ? $toolbar.height() : 0; 2912 2913 if (MK.utils.isResponsiveMenuState()) { 2914 headerHeight = config.responsiveHeight; 2915 2916 if (hasToolbar) { 2917 if ($toolbar.is(':hidden')) { 2918 toolbarHeight = 0; 2919 } 2920 } 2921 } 2922 2923 if (type === 'number') { 2924 stickyOffset = config.stickyOffset; 2925 } else if (type === 'header') { 2926 2927 stickyOffset = headerHeight + toolbarHeight + MK.val.adminbarHeight(); // add all header components here, make them 0 if needed 2928 2929 } else if (type === 'percent') { 2930 stickyOffset = MK.val.viewportPercentHeight(parseInt(config.stickyOffset)); 2931 } 2932 }; 2933 2934 setOffset(); 2935 $(window).on('resize', setOffset); 2936 2937 return function() { 2938 return stickyOffset; 2939 }; 2940 }()); 2941 2942 2943 2944 /** 2945 * Gets header height on particular offsetY position. Use to determine logic for fullHeight, smooth scroll etc. 2946 * Takes one parameter which is offset position we're interested in. 2947 * 2948 * @uses MK.val.offsetHeaderHeight({offset val}) 2949 * @param {number} 2950 * @return {number} 2951 */ 2952 MK.val.offsetHeaderHeight = (function() { // Closure avoids multiple DOM reads. We need to fetch header config only once. 2953 var $header = $('.mk-header').not('.js-header-shortcode').first(); 2954 2955 // We need to have returning function even when header is disabled 2956 if (!$header.length) { 2957 return function() { 2958 return 0; 2959 }; 2960 } 2961 2962 var $toolbar = $header.find('.mk-header-toolbar'), 2963 config = $header.data(), 2964 stickyHeight = config.stickyHeight, 2965 desktopHeight = config.height, 2966 mobileHeight = config.responsiveHeight, 2967 isTransparent = $header.hasClass('transparent-header'), 2968 isSticky = config.stickyStyle.length, 2969 isStickyLazy = config.stickyStyle === 'lazy', 2970 isVertical = config.headerStyle === 4, 2971 hasToolbar = $toolbar.length, 2972 toolbarHeight = hasToolbar ? $toolbar.height() : 0, 2973 bufor = 5; 2974 2975 /**
2976 * The sticky section of header style 2 has fixed height. 2977 * The stickey height option does not affect this header style. 2978 */ 2979 if ( config.headerStyle === 2 ) { 2980 stickyHeight = $header.find( '.mk-header-nav-container' ).outerHeight(); 2981 } 2982 2983 // if header has border bottom we can calculate that (for responsive state) 2984 var $innerHeader = $header.find('.mk-header-inner'); 2985 var hasInnerHeader = $innerHeader.length; 2986 2987 var headerHeight = function(offset) { 2988 2989 toolbarHeight = hasToolbar ? $toolbar.height() : 0 2990 var stickyOffset = MK.val.stickyOffset(); 2991 2992 2993 if (MK.utils.isResponsiveMenuState()) { // Header avaible only on top for mobile 2994 2995 if (hasToolbar && $toolbar.is(':hidden')) { 2996 toolbarHeight = 0; 2997 } 2998 2999 //in responsive state , .mk-header-holder position's changed to "relative" 3000 //and header's border affected to offset,so borders must be calculated 3001 var headerBorder = 0; 3002 headerBorder = parseInt($innerHeader.css('border-bottom-width')); 3003 3004 var totalHeight = mobileHeight + MK.val.adminbarHeight() + toolbarHeight + headerBorder; 3005 3006 if (offset <= totalHeight) return totalHeight; 3007 else return MK.val.adminbarHeight(); 3008 } else { 3009 if (offset <= stickyOffset) { 3010 if (isVertical) { 3011 if (hasToolbar) { 3012 return toolbarHeight + MK.val.adminbarHeight(); 3013 } else { 3014 return MK.val.adminbarHeight(); 3015 } 3016 } else if (isTransparent) { 3017 return MK.val.adminbarHeight(); 3018 } else { 3019 return desktopHeight + toolbarHeight + MK.val.adminbarHeight(); 3020 } // For any other return regular desktop height 3021 } else if (offset > stickyOffset) { 3022 if (isVertical) { 3023 return MK.val.adminbarHeight(); 3024 } else if (!isSticky) { 3025 return MK.val.adminbarHeight(); 3026 } else if (isStickyLazy) { 3027 return MK.val.adminbarHeight(); 3028 } else if (isSticky) { 3029 return stickyHeight + MK.val.adminbarHeight(); 3030 } 3031 } 3032 } 3033 // default to 0 to prevent errors ( need to return number ) 3034 // Anyway make sure all scenarios are covered in IFs 3035 return 0; 3036 }; 3037 3038 return function(offset) { 3039 return headerHeight(offset - MK.val.adminbarHeight()); 3040 }; 3041 })(); 3042 3043 3044 /** 3045 * Gets current offset of given element (passed as object or DOM reference) from top or bottom (default to top) 3046 * of screen with possible threshold (default to 0) 3047 * 3048 * @uses MK.val.dynamicOffset({obj reference}, {'top'|'bottom'}, {threshold val}) 3049 * @param {string|object} 3050 * @param {string} 3051 * @param {number} 3052 * @return {number} 3053 */ 3054 MK.val.dynamicOffset = function(el, position, threshold) { 3055 var $window = $(window), 3056 $el = $(el), 3057 pos = position || 'top', 3058 thr = threshold || 0, 3059 container = document.getElementById('mk-theme-container'), 3060 currentPos = 0; 3061 3062 var offset = 0, 3063 winH = 0, 3064 rect = 0, 3065 x = 0; 3066 3067 var update = function() { 3068 winH = $window.height(); 3069 rect = $el[0].getBoundingClientRect(); 3070 offset = (rect.top + MK.val.scroll()); 3071 x = (pos === 'top') ? MK.val.offsetHeaderHeight(offset) : winH + (rect.height - thr); 3072 currentPos = offset - x - 1; 3073 }; 3074 3075 update(); 3076 $window.on('load', update); 3077 $window.on('resize', update); 3078 window.addResizeListener(container, update); 3079 3080 return function() { 3081 return currentPos; 3082 }; 3083 }; 3084 3085 /** 3086 * Gets current height of given element (passed as object or DOM reference) 3087 * 3088 * @uses MK.val.dynamicHeight({obj reference}) 3089 * @param {string|object} 3090 * @return {number} 3091 */ 3092 MK.val.dynamicHeight = function(el) { 3093 var $window = $(window), 3094 $el = $(el), 3095 container = document.getElementById('mk-theme-container'), 3096 currentHeight = 0; 3097 3098 var update = function() { 3099 currentHeight = $el.outerHeight(); 3100 }; 3101 3102 update(); 3103 $window.on('load', update); 3104 $window.on('resize', update); 3105 window.addResizeListener(container, update); 3106 3107 return function() { 3108 return currentHeight; 3109 }; 3110 }; 3111 3112})(jQuery); 3113 3114/* 3115 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3116 * 3117 */ 3118jQuery.easing["jswing"] = jQuery.easing["swing"]; 3119jQuery.extend(jQuery.easing, { 3120 def: "easeOutQuad", 3121 swing: function (a, b, c, d, e) { 3122 return jQuery.easing[jQuery.easing.def](a, b, c, d, e) 3123 }, 3124 easeInQuad: function (a, b, c, d, e) { 3125 return d * (b /= e) * b + c 3126 }, 3127 easeOutQuad: function (a, b, c, d, e) { 3128 return -d * (b /= e) * (b - 2) + c 3129 }, 3130 easeInOutQuad: function (a, b, c, d, e) { 3131 if ((b /= e / 2) < 1) return d / 2 * b * b + c; 3132 return -d / 2 * (--b * (b - 2) - 1) + c 3133 }, 3134 easeInCubic: function (a, b, c, d, e) { 3135 return d * (b /= e) * b * b + c 3136 }, 3137 easeOutCubic: function (a, b, c, d, e) { 3138 return d * ((b = b / e - 1) * b * b + 1) + c 3139 }, 3140 easeInOutCubic: function (a, b, c, d, e) { 3141 if ((b /= e / 2) < 1) return d / 2 * b * b * b + c; 3142 return d / 2 * ((b -= 2) * b * b + 2) + c 3143 }, 3144 easeInQuart: function (a, b, c, d, e) { 3145 return d * (b /= e) * b * b * b + c 3146 }, 3147 easeOutQuart: function (a, b, c, d, e) { 3148 return -d * ((b = b / e - 1) * b * b * b - 1) + c 3149 }, 3150 easeInOutQuart: function (a, b, c, d, e) { 3151 if ((b /= e / 2) < 1) return d / 2 * b * b * b * b + c; 3152 return -d / 2 * ((b -= 2) * b * b * b - 2) + c 3153 }, 3154 easeInQuint: function (a, b, c, d, e) { 3155 return d * (b /= e) * b * b * b * b + c 3156 }, 3157 easeOutQuint: function (a, b, c, d, e) { 3158 return d * ((b = b / e - 1) * b * b * b * b + 1) + c 3159 }, 3160 easeInOutQuint: function (a, b, c, d, e) { 3161 if ((b /= e / 2) < 1) return d / 2 * b * b * b * b * b + c; 3162 return d / 2 * ((b -= 2) * b * b * b * b + 2) + c 3163 }, 3164 easeInSine: function (a, b, c, d, e) {
vendor: 5,544 bytes, lines 3165-3304
3165 return -d * Math.cos(b / e * (Math.PI / 2)) + d + c 3166 }, 3167 easeOutSine: function (a, b, c, d, e) { 3168 return d * Math.sin(b / e * (Math.PI / 2)) + c 3169 }, 3170 easeInOutSine: function (a, b, c, d, e) { 3171 return -d / 2 * (Math.cos(Math.PI * b / e) - 1) + c 3172 }, 3173 easeInExpo: function (a, b, c, d, e) { 3174 return b == 0 ? c : d * Math.pow(2, 10 * (b / e - 1)) + c 3175 }, 3176 easeOutExpo: function (a, b, c, d, e) { 3177 return b == e ? c + d : d * (-Math.pow(2, -10 * b / e) + 1) + c 3178 }, 3179 easeInOutExpo: function (a, b, c, d, e) { 3180 if (b == 0) return c; 3181 if (b == e) return c + d; 3182 if ((b /= e / 2) < 1) return d / 2 * Math.pow(2, 10 * (b - 1)) + c; 3183 return d / 2 * (-Math.pow(2, -10 * --b) + 2) + c 3184 }, 3185 easeInCirc: function (a, b, c, d, e) { 3186 return -d * (Math.sqrt(1 - (b /= e) * b) - 1) + c 3187 }, 3188 easeOutCirc: function (a, b, c, d, e) { 3189 return d * Math.sqrt(1 - (b = b / e - 1) * b) + c 3190 }, 3191 easeInOutCirc: function (a, b, c, d, e) { 3192 if ((b /= e / 2) < 1) return -d / 2 * (Math.sqrt(1 - b * b) - 1) + c; 3193 return d / 2 * (Math.sqrt(1 - (b -= 2) * b) + 1) + c 3194 }, 3195 easeInElastic: function (a, b, c, d, e) { 3196 var f = 1.70158; 3197 var g = 0; 3198 var h = d; 3199 if (b == 0) return c; 3200 if ((b /= e) == 1) return c + d; 3201 if (!g) g = e * .3; 3202 if (h < Math.abs(d)) { 3203 h = d; 3204 var f = g / 4 3205 } else var f = g / (2 * Math.PI) * Math.asin(d / h); 3206 return -(h * Math.pow(2, 10 * (b -= 1)) * Math.sin((b * e - f) * 2 * Math.PI / g)) + c 3207 }, 3208 easeOutElastic: function (a, b, c, d, e) { 3209 var f = 1.70158; 3210 var g = 0; 3211 var h = d; 3212 if (b == 0) return c; 3213 if ((b /= e) == 1) return c + d; 3214 if (!g) g = e * .3; 3215 if (h < Math.abs(d)) { 3216 h = d; 3217 var f = g / 4 3218 } else var f = g / (2 * Math.PI) * Math.asin(d / h); 3219 return h * Math.pow(2, -10 * b) * Math.sin((b * e - f) * 2 * Math.PI / g) + d + c 3220 }, 3221 easeInOutElastic: function (a, b, c, d, e) { 3222 var f = 1.70158; 3223 var g = 0; 3224 var h = d; 3225 if (b == 0) return c; 3226 if ((b /= e / 2) == 2) return c + d; 3227 if (!g) g = e * .3 * 1.5; 3228 if (h < Math.abs(d)) { 3229 h = d; 3230 var f = g / 4 3231 } else var f = g / (2 * Math.PI) * Math.asin(d / h); 3232 if (b < 1) return -.5 * h * Math.pow(2, 10 * (b -= 1)) * Math.sin((b * e - f) * 2 * Math.PI / g) + c; 3233 return h * Math.pow(2, -10 * (b -= 1)) * Math.sin((b * e - f) * 2 * Math.PI / g) * .5 + d + c 3234 }, 3235 easeInBack: function (a, b, c, d, e, f) { 3236 if (f == undefined) f = 1.70158; 3237 return d * (b /= e) * b * ((f + 1) * b - f) + c 3238 }, 3239 easeOutBack: function (a, b, c, d, e, f) { 3240 if (f == undefined) f = 1.70158; 3241 return d * ((b = b / e - 1) * b * ((f + 1) * b + f) + 1) + c 3242 }, 3243 easeInOutBack: function (a, b, c, d, e, f) { 3244 if (f == undefined) f = 1.70158; 3245 if ((b /= e / 2) < 1) return d / 2 * b * b * (((f *= 1.525) + 1) * b - f) + c; 3246 return d / 2 * ((b -= 2) * b * (((f *= 1.525) + 1) * b + f) + 2) + c 3247 }, 3248 easeInBounce: function (a, b, c, d, e) { 3249 return d - jQuery.easing.easeOutBounce(a, e - b, 0, d, e) + c 3250 }, 3251 easeOutBounce: function (a, b, c, d, e) { 3252 if ((b /= e) < 1 / 2.75) { 3253 return d * 7.5625 * b * b + c 3254 } else if (b < 2 / 2.75) { 3255 return d * (7.5625 * (b -= 1.5 / 2.75) * b + .75) + c 3256 } else if (b < 2.5 / 2.75) { 3257 return d * (7.5625 * (b -= 2.25 / 2.75) * b + .9375) + c 3258 } else { 3259 return d * (7.5625 * (b -= 2.625 / 2.75) * b + .984375) + c 3260 } 3261 }, 3262 easeInOutBounce: function (a, b, c, d, e) { 3263 if (b < e / 2) return jQuery.easing.easeInBounce(a, b * 2, 0, d, e) * .5 + c; 3264 return jQuery.easing.easeOutBounce(a, b * 2 - e, 0, d, e) * .5 + d * .5 + c 3265 } 3266}); 3267// ================================================== 3268// fancyBox v3.5.7 3269// 3270// Licensed GPLv3 for open source use 3271// or fancyBox Commercial License for commercial use 3272// 3273// http://fancyapps.com/fancybox/ 3274// Copyright 2019 fancyApps 3275// 3276// ================================================== 3277(function (window, document, $, undefined) { 3278 "use strict"; 3279 3280 window.console = window.console || { 3281 info: function (stuff) {} 3282 }; 3283 3284 // If there's no jQuery, fancyBox can't work 3285 // ========================================= 3286 3287 if (!$) { 3288 return; 3289 } 3290 3291 // Check if fancyBox is already initialized 3292 // ======================================== 3293 3294 if ($.fn.fancybox) { 3295 console.info("fancyBox already initialized"); 3296 3297 return; 3298 } 3299 3300 // Private default settings 3301 // ======================== 3302 3303 var defaults = { 3304 // Close existing modals
vendor: 141,869 bytes, lines 3305-8841
3305 // Set this to false if you do not need to stack multiple instances 3306 closeExisting: false, 3307 3308 // Enable infinite gallery navigation 3309 loop: false, 3310 3311 // Horizontal space between slides 3312 gutter: 50, 3313 3314 // Enable keyboard navigation 3315 keyboard: true, 3316 3317 // Should allow caption to overlap the content 3318 preventCaptionOverlap: true, 3319 3320 // Should display navigation arrows at the screen edges 3321 arrows: true, 3322 3323 // Should display counter at the top left corner 3324 infobar: true, 3325 3326 // Should display close button (using `btnTpl.smallBtn` template) over the content 3327 // Can be true, false, "auto" 3328 // If "auto" - will be automatically enabled for "html", "inline" or "ajax" items 3329 smallBtn: "auto", 3330 3331 // Should display toolbar (buttons at the top) 3332 // Can be true, false, "auto" 3333 // If "auto" - will be automatically hidden if "smallBtn" is enabled 3334 toolbar: "auto", 3335 3336 // What buttons should appear in the top right corner. 3337 // Buttons will be created using templates from `btnTpl` option 3338 // and they will be placed into toolbar (class="fancybox-toolbar"` element) 3339 buttons: [ 3340 "zoom", 3341 //"share", 3342 "slideShow", 3343 //"fullScreen", 3344 //"download", 3345 "thumbs", 3346 "close" 3347 ], 3348 3349 // Detect "idle" time in seconds 3350 idleTime: 3, 3351 3352 // Disable right-click and use simple image protection for images 3353 protect: false, 3354 3355 // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc 3356 modal: false, 3357 3358 image: { 3359 // Wait for images to load before displaying 3360 // true - wait for image to load and then display; 3361 // false - display thumbnail and load the full-sized image over top, 3362 // requires predefined image dimensions (`data-width` and `data-height` attributes) 3363 preload: false 3364 }, 3365 3366 ajax: { 3367 // Object containing settings for ajax request 3368 settings: { 3369 // This helps to indicate that request comes from the modal 3370 // Feel free to change naming 3371 data: { 3372 fancybox: true 3373 } 3374 } 3375 }, 3376 3377 iframe: { 3378 // Iframe template 3379 tpl: '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" allowfullscreen="allowfullscreen" allow="autoplay; fullscreen" src=""></iframe>', 3380 3381 // Preload iframe before displaying it 3382 // This allows to calculate iframe content width and height 3383 // (note: Due to "Same Origin Policy", you can't get cross domain data). 3384 preload: true, 3385 3386 // Custom CSS styling for iframe wrapping element 3387 // You can use this to set custom iframe dimensions 3388 css: {}, 3389 3390 // Iframe tag attributes 3391 attr: { 3392 scrolling: "auto" 3393 } 3394 }, 3395 3396 // For HTML5 video only 3397 video: { 3398 tpl: '<video class="fancybox-video" controls controlsList="nodownload" poster="{{poster}}">' + 3399 '<source src="{{src}}" type="{{format}}" />' + 3400 'Sorry, your browser doesn\'t support embedded videos, <a href="{{src}}">download</a> and watch with your favorite video player!' + 3401 "</video>", 3402 format: "", // custom video format 3403 autoStart: true 3404 }, 3405 3406 // Default content type if cannot be detected automatically 3407 defaultType: "image", 3408 3409 // Open/close animation type 3410 // Possible values: 3411 // false - disable 3412 // "zoom" - zoom images from/to thumbnail 3413 // "fade" 3414 // "zoom-in-out" 3415 // 3416 animationEffect: "zoom", 3417 3418 // Duration in ms for open/close animation 3419 animationDuration: 366, 3420 3421 // Should image change opacity while zooming 3422 // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios 3423 zoomOpacity: "auto", 3424 3425 // Transition effect between slides 3426 // 3427 // Possible values: 3428 // false - disable 3429 // "fade' 3430 // "slide' 3431 // "circular' 3432 // "tube' 3433 // "zoom-in-out' 3434 // "rotate' 3435 // 3436 transitionEffect: "fade", 3437 3438 // Duration in ms for transition animation 3439 transitionDuration: 366, 3440 3441 // Custom CSS class for slide element 3442 slideClass: "", 3443 3444 // Custom CSS class for layout 3445 baseClass: "", 3446 3447 // Base template for layout 3448 baseTpl: '<div class="fancybox-container" role="dialog" tabindex="-1">' + 3449 '<div class="fancybox-bg"></div>' + 3450 '<div class="fancybox-inner">' + 3451 '<div class="fancybox-infobar"><span data-fancybox-index></span> / <span data-fancybox-count></span></div>' + 3452 '<div class="fancybox-toolbar">{{buttons}}</div>' + 3453 '<div class="fancybox-navigation">{{arrows}}</div>' + 3454 '<div class="fancybox-stage"></div>' + 3455 '<div class="fancybox-caption"><div class="fancybox-caption__body"></div></div>' + 3456 "</div>" + 3457 "</div>", 3458 3459 // Loading indicator template 3460 spinnerTpl: '<div class="fancybox-loading"></div>', 3461 3462 // Error message template 3463 errorTpl: '<div class="fancybox-error"><p>{{ERROR}}</p></div>', 3464 3465 btnTpl: { 3466 download: '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}" href="javascript:;">' + 3467 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.62 17.09V19H5.38v-1.91zm-2.97-6.96L17 11.45l-5 4.87-5-4.87 1.36-1.32 2.68 2.64V5h1.92v7.77z"/></svg>' + 3468 "</a>", 3469 3470 zoom: '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' + 3471 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.7 17.3l-3-3a5.9 5.9 0 0 0-.6-7.6 5.9 5.9 0 0 0-8.4 0 5.9 5.9 0 0 0 0 8.4 5.9 5.9 0 0 0 7.7.7l3 3a1 1 0 0 0 1.3 0c.4-.5.4-1 0-1.5zM8.1 13.8a4 4 0 0 1 0-5.7 4 4 0 0 1 5.7 0 4 4 0 0 1 0 5.7 4 4 0 0 1-5.7 0z"/></svg>' + 3472 "</button>", 3473 3474 close: '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' + 3475 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 10.6L6.6 5.2 5.2 6.6l5.4 5.4-5.4 5.4 1.4 1.4 5.4-5.4 5.4 5.4 1.4-1.4-5.4-5.4 5.4-5.4-1.4-1.4-5.4 5.4z"/></svg>' + 3476 "</button>", 3477 3478 // Arrows 3479 arrowLeft: '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' + 3480 '<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M11.28 15.7l-1.34 1.37L5 12l4.94-5.07 1.34 1.38-2.68 2.72H19v1.94H8.6z"/></svg></div>' + 3481 "</button>", 3482 3483 arrowRight: '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' + 3484 '<div><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.4 12.97l-2.68 2.72 1.34 1.38L19 12l-4.94-5.07-1.34 1.38 2.68 2.72H5v1.94z"/></svg></div>' + 3485 "</button>", 3486 3487 // This small close button will be appended to your html/inline/ajax content by default, 3488 // if "smallBtn" option is not set to false 3489 smallBtn: '<button type="button" data-fancybox-close class="fancybox-button fancybox-close-small" title="{{CLOSE}}">' + 3490 '<svg xmlns="http://www.w3.org/2000/svg" version="1" viewBox="0 0 24 24"><path d="M13 12l5-5-1-1-5 5-5-5-1 1 5 5-5 5 1 1 5-5 5 5 1-1z"/></svg>' + 3491 "</button>" 3492 }, 3493 3494 // Container is injected into this element 3495 parentEl: "body", 3496 3497 // Hide browser vertical scrollbars; use at your own risk 3498 hideScrollbar: true, 3499 3500 // Focus handling 3501 // ============== 3502 3503 // Try to focus on the first focusable element after opening 3504 autoFocus: true, 3505 3506 // Put focus back to active element after closing 3507 backFocus: true, 3508 3509 // Do not let user to focus on element outside modal content 3510 trapFocus: true, 3511 3512 // Module specific options 3513 // ======================= 3514 3515 fullScreen: { 3516 autoStart: false 3517 }, 3518 3519 // Set `touch: false` to disable panning/swiping 3520 touch: { 3521 vertical: true, // Allow to drag content vertically 3522 momentum: true // Continue movement after releasing mouse/touch when panning 3523 }, 3524 3525 // Hash value when initializing manually, 3526 // set `false` to disable hash change 3527 hash: null, 3528 3529 // Customize or add new media types 3530 // Example: 3531 /* 3532 media : { 3533 youtube : { 3534 params : { 3535 autoplay : 0 3536 } 3537 } 3538 } 3539 */ 3540 media: {}, 3541 3542 slideShow: { 3543 autoStart: false, 3544 speed: 3000 3545 }, 3546 3547 thumbs: { 3548 autoStart: false, // Display thumbnails on opening 3549 hideOnClose: true, // Hide thumbnail grid when closing animation starts 3550 parentEl: ".fancybox-container", // Container is injected into this element 3551 axis: "y" // Vertical (y) or horizontal (x) scrolling 3552 }, 3553 3554 // Use mousewheel to navigate gallery 3555 // If 'auto' - enabled for images only 3556 wheel: "auto", 3557 3558 // Callbacks 3559 //========== 3560 3561 // See Documentation/API/Events for more information 3562 // Example: 3563 /* 3564 afterShow: function( instance, current ) { 3565 console.info( 'Clicked element:' ); 3566 console.info( current.opts.$orig ); 3567 } 3568 */ 3569 3570 onInit: $.noop, // When instance has been initialized 3571 3572 beforeLoad: $.noop, // Before the content of a slide is being loaded 3573 afterLoad: $.noop, // When the content of a slide is done loading 3574 3575 beforeShow: $.noop, // Before open animation starts 3576 afterShow: $.noop, // When content is done loading and animating 3577 3578 beforeClose: $.noop, // Before the instance attempts to close. Return false to cancel the close. 3579 afterClose: $.noop, // After instance has been closed 3580 3581 onActivate: $.noop, // When instance is brought to front 3582 onDeactivate: $.noop, // When other instance has been activated 3583 3584 // Interaction 3585 // =========== 3586 3587 // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, 3588 // each option can be string or method that returns value. 3589 // 3590 // Possible values: 3591 // "close" - close instance 3592 // "next" - move to next gallery item 3593 // "nextOrClose" - move to next gallery item or close if gallery has only one item 3594 // "toggleControls" - show/hide controls 3595 // "zoom" - zoom image (if loaded) 3596 // false - do nothing 3597 3598 // Clicked on the content 3599 clickContent: function (current, event) { 3600 return current.type === "image" ? "zoom" : false; 3601 }, 3602 3603 // Clicked on the slide 3604 clickSlide: "close", 3605 3606 // Clicked on the background (backdrop) element; 3607 // if you have not changed the layout, then most likely you need to use `clickSlide` option 3608 clickOutside: "close", 3609 3610 // Same as previous two, but for double click 3611 dblclickContent: false, 3612 dblclickSlide: false, 3613 dblclickOutside: false, 3614 3615 // Custom options when mobile device is detected 3616 // ============================================= 3617 3618 mobile: { 3619 preventCaptionOverlap: false, 3620 idleTime: false, 3621 clickContent: function (current, event) { 3622 return current.type === "image" ? "toggleControls" : false; 3623 }, 3624 clickSlide: function (current, event) { 3625 return current.type === "image" ? "toggleControls" : "close"; 3626 }, 3627 dblclickContent: function (current, event) { 3628 return current.type === "image" ? "zoom" : false; 3629 }, 3630 dblclickSlide: function (current, event) { 3631 return current.type === "image" ? "zoom" : false; 3632 } 3633 }, 3634 3635 // Internationalization 3636 // ==================== 3637 3638 lang: "en", 3639 i18n: { 3640 en: { 3641 CLOSE: "Close", 3642 NEXT: "Next", 3643 PREV: "Previous", 3644 ERROR: "The requested content cannot be loaded. <br/> Please try again later.", 3645 PLAY_START: "Start slideshow", 3646 PLAY_STOP: "Pause slideshow", 3647 FULL_SCREEN: "Full screen", 3648 THUMBS: "Thumbnails", 3649 DOWNLOAD: "Download", 3650 SHARE: "Share", 3651 ZOOM: "Zoom" 3652 }, 3653 de: { 3654 CLOSE: "Schließen", 3655 NEXT: "Weiter", 3656 PREV: "Zurück", 3657 ERROR: "Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.", 3658 PLAY_START: "Diaschau starten", 3659 PLAY_STOP: "Diaschau beenden", 3660 FULL_SCREEN: "Vollbild", 3661 THUMBS: "Vorschaubilder", 3662 DOWNLOAD: "Herunterladen", 3663 SHARE: "Teilen", 3664 ZOOM: "Vergrößern" 3665 } 3666 } 3667 }; 3668 3669 // Few useful variables and methods 3670 // ================================ 3671 3672 var $W = $(window); 3673 var $D = $(document); 3674 3675 var called = 0; 3676 3677 // Check if an object is a jQuery object and not a native JavaScript object 3678 // ======================================================================== 3679 var isQuery = function (obj) { 3680 return obj && obj.hasOwnProperty && obj instanceof $; 3681 }; 3682 3683 // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" 3684 // =============================================================================== 3685 var requestAFrame = (function () { 3686 return ( 3687 window.requestAnimationFrame || 3688 window.webkitRequestAnimationFrame || 3689 window.mozRequestAnimationFrame || 3690 window.oRequestAnimationFrame || 3691 // if all else fails, use setTimeout 3692 function (callback) { 3693 return window.setTimeout(callback, 1000 / 60); 3694 } 3695 ); 3696 })(); 3697 3698 var cancelAFrame = (function () { 3699 return ( 3700 window.cancelAnimationFrame || 3701 window.webkitCancelAnimationFrame || 3702 window.mozCancelAnimationFrame || 3703 window.oCancelAnimationFrame || 3704 function (id) { 3705 window.clearTimeout(id); 3706 } 3707 ); 3708 })(); 3709 3710 // Detect the supported transition-end event property name 3711 // ======================================================= 3712 var transitionEnd = (function () { 3713 var el = document.createElement("fakeelement"), 3714 t; 3715 3716 var transitions = { 3717 transition: "transitionend", 3718 OTransition: "oTransitionEnd", 3719 MozTransition: "transitionend", 3720 WebkitTransition: "webkitTransitionEnd" 3721 }; 3722 3723 for (t in transitions) { 3724 if (el.style[t] !== undefined) { 3725 return transitions[t]; 3726 } 3727 } 3728 3729 return "transitionend"; 3730 })(); 3731 3732 // Force redraw on an element. 3733 // This helps in cases where the browser doesn't redraw an updated element properly 3734 // ================================================================================ 3735 var forceRedraw = function ($el) { 3736 return $el && $el.length && $el[0].offsetHeight; 3737 }; 3738 3739 // Exclude array (`buttons`) options from deep merging 3740 // =================================================== 3741 var mergeOpts = function (opts1, opts2) { 3742 var rez = $.extend(true, {}, opts1, opts2); 3743 3744 $.each(opts2, function (key, value) { 3745 if ($.isArray(value)) { 3746 rez[key] = value; 3747 } 3748 }); 3749 3750 return rez; 3751 }; 3752 3753 // How much of an element is visible in viewport 3754 // ============================================= 3755 3756 var inViewport = function (elem) { 3757 var elemCenter, rez; 3758 3759 if (!elem || elem.ownerDocument !== document) { 3760 return false; 3761 } 3762 3763 $(".fancybox-container").css("pointer-events", "none"); 3764 3765 elemCenter = { 3766 x: elem.getBoundingClientRect().left + elem.offsetWidth / 2, 3767 y: elem.getBoundingClientRect().top + elem.offsetHeight / 2 3768 }; 3769 3770 rez = document.elementFromPoint(elemCenter.x, elemCenter.y) === elem; 3771 3772 $(".fancybox-container").css("pointer-events", ""); 3773 3774 return rez; 3775 }; 3776 3777 // Class definition 3778 // ================ 3779 3780 var FancyBox = function (content, opts, index) { 3781 var self = this; 3782 3783 self.opts = mergeOpts({ 3784 index: index 3785 }, $.fancybox.defaults); 3786 3787 if ($.isPlainObject(opts)) { 3788 self.opts = mergeOpts(self.opts, opts); 3789 } 3790 3791 if ($.fancybox.isMobile) { 3792 self.opts = mergeOpts(self.opts, self.opts.mobile); 3793 } 3794 3795 self.id = self.opts.id || ++called; 3796 3797 self.currIndex = parseInt(self.opts.index, 10) || 0; 3798 self.prevIndex = null; 3799 3800 self.prevPos = null; 3801 self.currPos = 0; 3802 3803 self.firstRun = true; 3804 3805 // All group items 3806 self.group = []; 3807 3808 // Existing slides (for current, next and previous gallery items) 3809 self.slides = {}; 3810 3811 // Create group elements 3812 self.addContent(content); 3813 3814 if (!self.group.length) { 3815 return; 3816 } 3817 3818 self.init(); 3819 }; 3820 3821 $.extend(FancyBox.prototype, { 3822 // Create DOM structure 3823 // ==================== 3824 3825 init: function () { 3826 var self = this, 3827 firstItem = self.group[self.currIndex], 3828 firstItemOpts = firstItem.opts, 3829 $container, 3830 buttonStr; 3831 3832 if (firstItemOpts.closeExisting) { 3833 $.fancybox.close(true); 3834 } 3835 3836 // Hide scrollbars 3837 // =============== 3838 3839 $("body").addClass("fancybox-active"); 3840 3841 if ( 3842 !$.fancybox.getInstance() && 3843 firstItemOpts.hideScrollbar !== false && 3844 !$.fancybox.isMobile && 3845 document.body.scrollHeight > window.innerHeight 3846 ) { 3847 $("head").append( 3848 '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar{margin-right:' + 3849 (window.innerWidth - document.documentElement.clientWidth) + 3850 "px;}</style>" 3851 ); 3852 3853 $("body").addClass("compensate-for-scrollbar"); 3854 } 3855 3856 // Build html markup and set references 3857 // ==================================== 3858 3859 // Build html code for buttons and insert into main template 3860 buttonStr = ""; 3861 3862 $.each(firstItemOpts.buttons, function (index, value) { 3863 buttonStr += firstItemOpts.btnTpl[value] || ""; 3864 }); 3865 3866 // Create markup from base template, it will be initially hidden to 3867 // avoid unnecessary work like painting while initializing is not complete 3868 $container = $( 3869 self.translate( 3870 self, 3871 firstItemOpts.baseTpl 3872 .replace("{{buttons}}", buttonStr) 3873 .replace("{{arrows}}", firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight) 3874 ) 3875 ) 3876 .attr("id", "fancybox-container-" + self.id) 3877 .addClass(firstItemOpts.baseClass) 3878 .data("FancyBox", self) 3879 .appendTo(firstItemOpts.parentEl); 3880 3881 // Create object holding references to jQuery wrapped nodes 3882 self.$refs = { 3883 container: $container 3884 }; 3885 3886 ["bg", "inner", "infobar", "toolbar", "stage", "caption", "navigation"].forEach(function (item) { 3887 self.$refs[item] = $container.find(".fancybox-" + item); 3888 }); 3889 3890 self.trigger("onInit"); 3891 3892 // Enable events, deactive previous instances 3893 self.activate(); 3894 3895 // Build slides, load and reveal content 3896 self.jumpTo(self.currIndex); 3897 }, 3898 3899 // Simple i18n support - replaces object keys found in template 3900 // with corresponding values 3901 // ============================================================ 3902 3903 translate: function (obj, str) { 3904 var arr = obj.opts.i18n[obj.opts.lang] || obj.opts.i18n.en; 3905 3906 return str.replace(/\{\{(\w+)\}\}/g, function (match, n) { 3907 return arr[n] === undefined ? match : arr[n]; 3908 }); 3909 }, 3910 3911 // Populate current group with fresh content 3912 // Check if each object has valid type and content 3913 // =============================================== 3914 3915 addContent: function (content) { 3916 var self = this, 3917 items = $.makeArray(content), 3918 thumbs; 3919 3920 $.each(items, function (i, item) { 3921 var obj = {}, 3922 opts = {}, 3923 $item, 3924 type, 3925 found, 3926 src, 3927 srcParts; 3928 3929 // Step 1 - Make sure we have an object 3930 // ==================================== 3931 3932 if ($.isPlainObject(item)) { 3933 // We probably have manual usage here, something like 3934 // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) 3935 3936 obj = item; 3937 opts = item.opts || item; 3938 } else if ($.type(item) === "object" && $(item).length) { 3939 // Here we probably have jQuery collection returned by some selector 3940 $item = $(item); 3941 3942 // Support attributes like `data-options='{"touch" : false}'` and `data-touch='false'` 3943 opts = $item.data() || {}; 3944 opts = $.extend(true, {}, opts, opts.options); 3945 3946 // Here we store clicked element 3947 opts.$orig = $item; 3948 3949 obj.src = self.opts.src || opts.src || $item.attr("href"); 3950 3951 // Assume that simple syntax is used, for example: 3952 // `$.fancybox.open( $("#test"), {} );` 3953 if (!obj.type && !obj.src) { 3954 obj.type = "inline"; 3955 obj.src = item; 3956 } 3957 } else { 3958 // Assume we have a simple html code, for example: 3959 // $.fancybox.open( '<div><h1>Hi!</h1></div>' ); 3960 obj = { 3961 type: "html", 3962 src: item + "" 3963 }; 3964 } 3965 3966 // Each gallery object has full collection of options 3967 obj.opts = $.extend(true, {}, self.opts, opts); 3968 3969 // Do not merge buttons array 3970 if ($.isArray(opts.buttons)) { 3971 obj.opts.buttons = opts.buttons; 3972 } 3973 3974 if ($.fancybox.isMobile && obj.opts.mobile) { 3975 obj.opts = mergeOpts(obj.opts, obj.opts.mobile); 3976 } 3977 3978 // Step 2 - Make sure we have content type, if not - try to guess 3979 // ============================================================== 3980 3981 type = obj.type || obj.opts.type; 3982 src = obj.src || ""; 3983 3984 if (!type && src) { 3985 if ((found = src.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))) { 3986 type = "video"; 3987 3988 if (!obj.opts.video.format) { 3989 obj.opts.video.format = "video/" + (found[1] === "ogv" ? "ogg" : found[1]); 3990 } 3991 } else if (src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)) { 3992 type = "image"; 3993 } else if (src.match(/\.(pdf)((\?|#).*)?$/i)) { 3994 type = "iframe"; 3995 obj = $.extend(true, obj, { 3996 contentType: "pdf", 3997 opts: { 3998 iframe: { 3999 preload: false 4000 } 4001 } 4002 }); 4003 } else if (src.charAt(0) === "#") { 4004 type = "inline"; 4005 } 4006 } 4007 4008 if (type) { 4009 obj.type = type; 4010 } else { 4011 self.trigger("objectNeedsType", obj); 4012 } 4013 4014 if (!obj.contentType) { 4015 obj.contentType = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1 ? "html" : obj.type; 4016 } 4017 4018 // Step 3 - Some adjustments 4019 // ========================= 4020 4021 obj.index = self.group.length; 4022 4023 if (obj.opts.smallBtn == "auto") { 4024 obj.opts.smallBtn = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1; 4025 } 4026 4027 if (obj.opts.toolbar === "auto") { 4028 obj.opts.toolbar = !obj.opts.smallBtn; 4029 } 4030 4031 // Find thumbnail image, check if exists and if is in the viewport 4032 obj.$thumb = obj.opts.$thumb || null; 4033 4034 if (obj.opts.$trigger && obj.index === self.opts.index) { 4035 obj.$thumb = obj.opts.$trigger.find("img:first"); 4036 4037 if (obj.$thumb.length) { 4038 obj.opts.$orig = obj.opts.$trigger; 4039 } 4040 } 4041 4042 if (!(obj.$thumb && obj.$thumb.length) && obj.opts.$orig) { 4043 obj.$thumb = obj.opts.$orig.find("img:first"); 4044 } 4045 4046 if (obj.$thumb && !obj.$thumb.length) { 4047 obj.$thumb = null; 4048 } 4049 4050 obj.thumb = obj.opts.thumb || (obj.$thumb ? obj.$thumb[0].src : null); 4051 4052 // "caption" is a "special" option, it can be used to customize caption per gallery item 4053 if ($.type(obj.opts.caption) === "function") { 4054 obj.opts.caption = obj.opts.caption.apply(item, [self, obj]); 4055 } 4056 4057 if ($.type(self.opts.caption) === "function") { 4058 obj.opts.caption = self.opts.caption.apply(item, [self, obj]); 4059 } 4060 4061 // Make sure we have caption as a string or jQuery object 4062 if (!(obj.opts.caption instanceof $)) { 4063 obj.opts.caption = obj.opts.caption === undefined ? "" : obj.opts.caption + ""; 4064 } 4065 4066 // Check if url contains "filter" used to filter the content 4067 // Example: "ajax.html #something" 4068 if (obj.type === "ajax") { 4069 srcParts = src.split(/\s+/, 2); 4070 4071 if (srcParts.length > 1) { 4072 obj.src = srcParts.shift(); 4073 4074 obj.opts.filter = srcParts.shift(); 4075 } 4076 } 4077 4078 // Hide all buttons and disable interactivity for modal items 4079 if (obj.opts.modal) { 4080 obj.opts = $.extend(true, obj.opts, { 4081 trapFocus: true, 4082 // Remove buttons 4083 infobar: 0, 4084 toolbar: 0, 4085 4086 smallBtn: 0, 4087 4088 // Disable keyboard navigation 4089 keyboard: 0, 4090 4091 // Disable some modules 4092 slideShow: 0, 4093 fullScreen: 0, 4094 thumbs: 0, 4095 touch: 0, 4096 4097 // Disable click event handlers 4098 clickContent: false, 4099 clickSlide: false, 4100 clickOutside: false, 4101 dblclickContent: false, 4102 dblclickSlide: false, 4103 dblclickOutside: false 4104 }); 4105 } 4106 4107 // Step 4 - Add processed object to group 4108 // ====================================== 4109 4110 self.group.push(obj); 4111 }); 4112 4113 // Update controls if gallery is already opened 4114 if (Object.keys(self.slides).length) { 4115 self.updateControls(); 4116 4117 // Update thumbnails, if needed 4118 thumbs = self.Thumbs; 4119 4120 if (thumbs && thumbs.isActive) { 4121 thumbs.create(); 4122 4123 thumbs.focus(); 4124 } 4125 } 4126 }, 4127 4128 // Attach an event handler functions for: 4129 // - navigation buttons 4130 // - browser scrolling, resizing; 4131 // - focusing 4132 // - keyboard 4133 // - detecting inactivity 4134 // ====================================== 4135 4136 addEvents: function () { 4137 var self = this; 4138 4139 self.removeEvents(); 4140 4141 // Make navigation elements clickable 4142 // ================================== 4143 4144 self.$refs.container 4145 .on("click.fb-close", "[data-fancybox-close]", function (e) { 4146 e.stopPropagation(); 4147 e.preventDefault(); 4148 4149 self.close(e); 4150 }) 4151 .on("touchstart.fb-prev click.fb-prev", "[data-fancybox-prev]", function (e) { 4152 e.stopPropagation(); 4153 e.preventDefault(); 4154 4155 self.previous(); 4156 }) 4157 .on("touchstart.fb-next click.fb-next", "[data-fancybox-next]", function (e) { 4158 e.stopPropagation(); 4159 e.preventDefault(); 4160 4161 self.next(); 4162 }) 4163 .on("click.fb", "[data-fancybox-zoom]", function (e) { 4164 // Click handler for zoom button 4165 self[self.isScaledDown() ? "scaleToActual" : "scaleToFit"](); 4166 }); 4167 4168 // Handle page scrolling and browser resizing 4169 // ========================================== 4170 4171 $W.on("orientationchange.fb resize.fb", function (e) { 4172 if (e && e.originalEvent && e.originalEvent.type === "resize") { 4173 if (self.requestId) { 4174 cancelAFrame(self.requestId); 4175 } 4176 4177 self.requestId = requestAFrame(function () { 4178 self.update(e); 4179 }); 4180 } else { 4181 if (self.current && self.current.type === "iframe") { 4182 self.$refs.stage.hide(); 4183 } 4184 4185 setTimeout( 4186 function () { 4187 self.$refs.stage.show(); 4188 4189 self.update(e); 4190 }, 4191 $.fancybox.isMobile ? 600 : 250 4192 ); 4193 } 4194 }); 4195 4196 $D.on("keydown.fb", function (e) { 4197 var instance = $.fancybox ? $.fancybox.getInstance() : null, 4198 current = instance.current, 4199 keycode = e.keyCode || e.which; 4200 4201 // Trap keyboard focus inside of the modal 4202 // ======================================= 4203 4204 if (keycode == 9) { 4205 if (current.opts.trapFocus) { 4206 self.focus(e); 4207 } 4208 4209 return; 4210 } 4211 4212 // Enable keyboard navigation 4213 // ========================== 4214 4215 if (!current.opts.keyboard || e.ctrlKey || e.altKey || e.shiftKey || $(e.target).is("input,textarea,video,audio,select")) { 4216 return; 4217 } 4218 4219 // Backspace and Esc keys 4220 if (keycode === 8 || keycode === 27) { 4221 e.preventDefault(); 4222 4223 self.close(e); 4224 4225 return; 4226 } 4227 4228 // Left arrow and Up arrow 4229 if (keycode === 37 || keycode === 38) { 4230 e.preventDefault(); 4231 4232 self.previous(); 4233 4234 return; 4235 } 4236 4237 // Righ arrow and Down arrow 4238 if (keycode === 39 || keycode === 40) { 4239 e.preventDefault(); 4240 4241 self.next(); 4242 4243 return; 4244 } 4245 4246 self.trigger("afterKeydown", e, keycode); 4247 }); 4248 4249 // Hide controls after some inactivity period 4250 if (self.group[self.currIndex].opts.idleTime) { 4251 self.idleSecondsCounter = 0; 4252 4253 $D.on( 4254 "mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle", 4255 function (e) { 4256 self.idleSecondsCounter = 0; 4257 4258 if (self.isIdle) { 4259 self.showControls(); 4260 } 4261 4262 self.isIdle = false; 4263 } 4264 ); 4265 4266 self.idleInterval = window.setInterval(function () { 4267 self.idleSecondsCounter++; 4268 4269 if (self.idleSecondsCounter >= self.group[self.currIndex].opts.idleTime && !self.isDragging) { 4270 self.isIdle = true; 4271 self.idleSecondsCounter = 0; 4272 4273 self.hideControls(); 4274 } 4275 }, 1000); 4276 } 4277 }, 4278 4279 // Remove events added by the core 4280 // =============================== 4281 4282 removeEvents: function () { 4283 var self = this; 4284 4285 $W.off("orientationchange.fb resize.fb"); 4286 $D.off("keydown.fb .fb-idle"); 4287 4288 this.$refs.container.off(".fb-close .fb-prev .fb-next"); 4289 4290 if (self.idleInterval) { 4291 window.clearInterval(self.idleInterval); 4292 4293 self.idleInterval = null; 4294 } 4295 }, 4296 4297 // Change to previous gallery item 4298 // =============================== 4299 4300 previous: function (duration) { 4301 return this.jumpTo(this.currPos - 1, duration); 4302 }, 4303 4304 // Change to next gallery item 4305 // =========================== 4306 4307 next: function (duration) { 4308 return this.jumpTo(this.currPos + 1, duration); 4309 }, 4310 4311 // Switch to selected gallery item 4312 // =============================== 4313 4314 jumpTo: function (pos, duration) { 4315 var self = this, 4316 groupLen = self.group.length, 4317 firstRun, 4318 isMoved, 4319 loop, 4320 current, 4321 previous, 4322 slidePos, 4323 stagePos, 4324 prop, 4325 diff; 4326 4327 if (self.isDragging || self.isClosing || (self.isAnimating && self.firstRun)) { 4328 return; 4329 } 4330 4331 // Should loop? 4332 pos = parseInt(pos, 10); 4333 loop = self.current ? self.current.opts.loop : self.opts.loop; 4334 4335 if (!loop && (pos < 0 || pos >= groupLen)) { 4336 return false; 4337 } 4338 4339 // Check if opening for the first time; this helps to speed things up 4340 firstRun = self.firstRun = !Object.keys(self.slides).length; 4341 4342 // Create slides 4343 previous = self.current; 4344 4345 self.prevIndex = self.currIndex; 4346 self.prevPos = self.currPos; 4347 4348 current = self.createSlide(pos); 4349 4350 if (groupLen > 1) { 4351 if (loop || current.index < groupLen - 1) { 4352 self.createSlide(pos + 1); 4353 } 4354 4355 if (loop || current.index > 0) { 4356 self.createSlide(pos - 1); 4357 } 4358 } 4359 4360 self.current = current; 4361 self.currIndex = current.index; 4362 self.currPos = current.pos; 4363 4364 self.trigger("beforeShow", firstRun); 4365 4366 self.updateControls(); 4367 4368 // Validate duration length 4369 current.forcedDuration = undefined; 4370 4371 if ($.isNumeric(duration)) { 4372 current.forcedDuration = duration; 4373 } else { 4374 duration = current.opts[firstRun ? "animationDuration" : "transitionDuration"]; 4375 } 4376 4377 duration = parseInt(duration, 10); 4378 4379 // Check if user has swiped the slides or if still animating 4380 isMoved = self.isMoved(current); 4381 4382 // Make sure current slide is visible 4383 current.$slide.addClass("fancybox-slide--current"); 4384 4385 // Fresh start - reveal container, current slide and start loading content 4386 if (firstRun) { 4387 if (current.opts.animationEffect && duration) { 4388 self.$refs.container.css("transition-duration", duration + "ms"); 4389 } 4390 4391 self.$refs.container.addClass("fancybox-is-open").trigger("focus"); 4392 4393 // Attempt to load content into slide 4394 // This will later call `afterLoad` -> `revealContent` 4395 self.loadSlide(current); 4396 4397 self.preload("image"); 4398 4399 return; 4400 } 4401 4402 // Get actual slide/stage positions (before cleaning up) 4403 slidePos = $.fancybox.getTranslate(previous.$slide); 4404 stagePos = $.fancybox.getTranslate(self.$refs.stage); 4405 4406 // Clean up all slides 4407 $.each(self.slides, function (index, slide) { 4408 $.fancybox.stop(slide.$slide, true); 4409 }); 4410 4411 if (previous.pos !== current.pos) { 4412 previous.isComplete = false; 4413 } 4414 4415 previous.$slide.removeClass("fancybox-slide--complete fancybox-slide--current"); 4416 4417 // If slides are out of place, then animate them to correct position 4418 if (isMoved) { 4419 // Calculate horizontal swipe distance 4420 diff = slidePos.left - (previous.pos * slidePos.width + previous.pos * previous.opts.gutter); 4421 4422 $.each(self.slides, function (index, slide) { 4423 slide.$slide.removeClass("fancybox-animated").removeClass(function (index, className) { 4424 return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" "); 4425 }); 4426 4427 // Make sure that each slide is in equal distance 4428 // This is mostly needed for freshly added slides, because they are not yet positioned 4429 var leftPos = slide.pos * slidePos.width + slide.pos * slide.opts.gutter; 4430 4431 $.fancybox.setTranslate(slide.$slide, { 4432 top: 0, 4433 left: leftPos - stagePos.left + diff 4434 }); 4435 4436 if (slide.pos !== current.pos) { 4437 slide.$slide.addClass("fancybox-slide--" + (slide.pos > current.pos ? "next" : "previous")); 4438 } 4439 4440 // Redraw to make sure that transition will start 4441 forceRedraw(slide.$slide); 4442 4443 // Animate the slide 4444 $.fancybox.animate( 4445 slide.$slide, { 4446 top: 0, 4447 left: (slide.pos - current.pos) * slidePos.width + (slide.pos - current.pos) * slide.opts.gutter 4448 }, 4449 duration, 4450 function () { 4451 slide.$slide 4452 .css({ 4453 transform: "", 4454 opacity: "" 4455 }) 4456 .removeClass("fancybox-slide--next fancybox-slide--previous"); 4457 4458 if (slide.pos === self.currPos) { 4459 self.complete(); 4460 } 4461 } 4462 ); 4463 }); 4464 } else if (duration && current.opts.transitionEffect) { 4465 // Set transition effect for previously active slide 4466 prop = "fancybox-animated fancybox-fx-" + current.opts.transitionEffect; 4467 4468 previous.$slide.addClass("fancybox-slide--" + (previous.pos > current.pos ? "next" : "previous")); 4469 4470 $.fancybox.animate( 4471 previous.$slide, 4472 prop, 4473 duration, 4474 function () { 4475 previous.$slide.removeClass(prop).removeClass("fancybox-slide--next fancybox-slide--previous"); 4476 }, 4477 false 4478 ); 4479 } 4480 4481 if (current.isLoaded) { 4482 self.revealContent(current); 4483 } else { 4484 self.loadSlide(current); 4485 } 4486 4487 self.preload("image"); 4488 }, 4489 4490 // Create new "slide" element 4491 // These are gallery items that are actually added to DOM 4492 // ======================================================= 4493 4494 createSlide: function (pos) { 4495 var self = this, 4496 $slide, 4497 index; 4498 4499 index = pos % self.group.length; 4500 index = index < 0 ? self.group.length + index : index; 4501 4502 if (!self.slides[pos] && self.group[index]) { 4503 $slide = $('<div class="fancybox-slide"></div>').appendTo(self.$refs.stage); 4504 4505 self.slides[pos] = $.extend(true, {}, self.group[index], { 4506 pos: pos, 4507 $slide: $slide, 4508 isLoaded: false 4509 }); 4510 4511 self.updateSlide(self.slides[pos]); 4512 } 4513 4514 return self.slides[pos]; 4515 }, 4516 4517 // Scale image to the actual size of the image; 4518 // x and y values should be relative to the slide 4519 // ============================================== 4520 4521 scaleToActual: function (x, y, duration) { 4522 var self = this, 4523 current = self.current, 4524 $content = current.$content, 4525 canvasWidth = $.fancybox.getTranslate(current.$slide).width, 4526 canvasHeight = $.fancybox.getTranslate(current.$slide).height, 4527 newImgWidth = current.width, 4528 newImgHeight = current.height, 4529 imgPos, 4530 posX, 4531 posY, 4532 scaleX, 4533 scaleY; 4534 4535 if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) { 4536 return; 4537 } 4538 4539 self.isAnimating = true; 4540 4541 $.fancybox.stop($content); 4542 4543 x = x === undefined ? canvasWidth * 0.5 : x; 4544 y = y === undefined ? canvasHeight * 0.5 : y; 4545 4546 imgPos = $.fancybox.getTranslate($content); 4547 4548 imgPos.top -= $.fancybox.getTranslate(current.$slide).top; 4549 imgPos.left -= $.fancybox.getTranslate(current.$slide).left; 4550 4551 scaleX = newImgWidth / imgPos.width; 4552 scaleY = newImgHeight / imgPos.height; 4553 4554 // Get center position for original image 4555 posX = canvasWidth * 0.5 - newImgWidth * 0.5; 4556 posY = canvasHeight * 0.5 - newImgHeight * 0.5; 4557 4558 // Make sure image does not move away from edges 4559 if (newImgWidth > canvasWidth) { 4560 posX = imgPos.left * scaleX - (x * scaleX - x); 4561 4562 if (posX > 0) { 4563 posX = 0; 4564 } 4565 4566 if (posX < canvasWidth - newImgWidth) { 4567 posX = canvasWidth - newImgWidth; 4568 } 4569 } 4570 4571 if (newImgHeight > canvasHeight) { 4572 posY = imgPos.top * scaleY - (y * scaleY - y); 4573 4574 if (posY > 0) { 4575 posY = 0; 4576 } 4577 4578 if (posY < canvasHeight - newImgHeight) { 4579 posY = canvasHeight - newImgHeight; 4580 } 4581 } 4582 4583 self.updateCursor(newImgWidth, newImgHeight); 4584 4585 $.fancybox.animate( 4586 $content, { 4587 top: posY, 4588 left: posX, 4589 scaleX: scaleX, 4590 scaleY: scaleY 4591 }, 4592 duration || 366, 4593 function () { 4594 self.isAnimating = false; 4595 } 4596 ); 4597 4598 // Stop slideshow 4599 if (self.SlideShow && self.SlideShow.isActive) { 4600 self.SlideShow.stop(); 4601 } 4602 }, 4603 4604 // Scale image to fit inside parent element 4605 // ======================================== 4606 4607 scaleToFit: function (duration) { 4608 var self = this, 4609 current = self.current, 4610 $content = current.$content, 4611 end; 4612 4613 if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) { 4614 return; 4615 } 4616 4617 self.isAnimating = true; 4618 4619 $.fancybox.stop($content); 4620 4621 end = self.getFitPos(current); 4622 4623 self.updateCursor(end.width, end.height); 4624 4625 $.fancybox.animate( 4626 $content, { 4627 top: end.top, 4628 left: end.left, 4629 scaleX: end.width / $content.width(), 4630 scaleY: end.height / $content.height() 4631 }, 4632 duration || 366, 4633 function () { 4634 self.isAnimating = false; 4635 } 4636 ); 4637 }, 4638 4639 // Calculate image size to fit inside viewport 4640 // =========================================== 4641 4642 getFitPos: function (slide) { 4643 var self = this, 4644 $content = slide.$content, 4645 $slide = slide.$slide, 4646 width = slide.width || slide.opts.width, 4647 height = slide.height || slide.opts.height, 4648 maxWidth, 4649 maxHeight, 4650 minRatio, 4651 aspectRatio, 4652 rez = {}; 4653 4654 if (!slide.isLoaded || !$content || !$content.length) { 4655 return false; 4656 } 4657 4658 maxWidth = $.fancybox.getTranslate(self.$refs.stage).width; 4659 maxHeight = $.fancybox.getTranslate(self.$refs.stage).height; 4660 4661 maxWidth -= 4662 parseFloat($slide.css("paddingLeft")) + 4663 parseFloat($slide.css("paddingRight")) + 4664 parseFloat($content.css("marginLeft")) + 4665 parseFloat($content.css("marginRight")); 4666 4667 maxHeight -= 4668 parseFloat($slide.css("paddingTop")) + 4669 parseFloat($slide.css("paddingBottom")) + 4670 parseFloat($content.css("marginTop")) + 4671 parseFloat($content.css("marginBottom")); 4672 4673 if (!width || !height) { 4674 width = maxWidth; 4675 height = maxHeight; 4676 } 4677 4678 minRatio = Math.min(1, maxWidth / width, maxHeight / height); 4679 4680 width = minRatio * width; 4681 height = minRatio * height; 4682 4683 // Adjust width/height to precisely fit into container 4684 if (width > maxWidth - 0.5) { 4685 width = maxWidth; 4686 } 4687 4688 if (height > maxHeight - 0.5) { 4689 height = maxHeight; 4690 } 4691 4692 if (slide.type === "image") { 4693 rez.top = Math.floor((maxHeight - height) * 0.5) + parseFloat($slide.css("paddingTop")); 4694 rez.left = Math.floor((maxWidth - width) * 0.5) + parseFloat($slide.css("paddingLeft")); 4695 } else if (slide.contentType === "video") { 4696 // Force aspect ratio for the video 4697 // "I say the whole world must learn of our peaceful ways… by force!" 4698 aspectRatio = slide.opts.width && slide.opts.height ? width / height : slide.opts.ratio || 16 / 9; 4699 4700 if (height > width / aspectRatio) { 4701 height = width / aspectRatio; 4702 } else if (width > height * aspectRatio) { 4703 width = height * aspectRatio; 4704 } 4705 } 4706 4707 rez.width = width; 4708 rez.height = height; 4709 4710 return rez; 4711 }, 4712 4713 // Update content size and position for all slides 4714 // ============================================== 4715 4716 update: function (e) { 4717 var self = this; 4718 4719 $.each(self.slides, function (key, slide) { 4720 self.updateSlide(slide, e); 4721 }); 4722 }, 4723 4724 // Update slide content position and size 4725 // ====================================== 4726 4727 updateSlide: function (slide, e) { 4728 var self = this, 4729 $content = slide && slide.$content, 4730 width = slide.width || slide.opts.width, 4731 height = slide.height || slide.opts.height, 4732 $slide = slide.$slide; 4733 4734 // First, prevent caption overlap, if needed 4735 self.adjustCaption(slide); 4736 4737 // Then resize content to fit inside the slide 4738 if ($content && (width || height || slide.contentType === "video") && !slide.hasError) { 4739 $.fancybox.stop($content); 4740 4741 $.fancybox.setTranslate($content, self.getFitPos(slide)); 4742 4743 if (slide.pos === self.currPos) { 4744 self.isAnimating = false; 4745 4746 self.updateCursor(); 4747 } 4748 } 4749 4750 // Then some adjustments 4751 self.adjustLayout(slide); 4752 4753 if ($slide.length) { 4754 $slide.trigger("refresh"); 4755 4756 if (slide.pos === self.currPos) { 4757 self.$refs.toolbar 4758 .add(self.$refs.navigation.find(".fancybox-button--arrow_right")) 4759 .toggleClass("compensate-for-scrollbar", $slide.get(0).scrollHeight > $slide.get(0).clientHeight); 4760 } 4761 } 4762 4763 self.trigger("onUpdate", slide, e); 4764 }, 4765 4766 // Horizontally center slide 4767 // ========================= 4768 4769 centerSlide: function (duration) { 4770 var self = this, 4771 current = self.current, 4772 $slide = current.$slide; 4773 4774 if (self.isClosing || !current) { 4775 return; 4776 } 4777 4778 $slide.siblings().css({ 4779 transform: "", 4780 opacity: "" 4781 }); 4782 4783 $slide 4784 .parent() 4785 .children() 4786 .removeClass("fancybox-slide--previous fancybox-slide--next"); 4787 4788 $.fancybox.animate( 4789 $slide, { 4790 top: 0, 4791 left: 0, 4792 opacity: 1 4793 }, 4794 duration === undefined ? 0 : duration, 4795 function () { 4796 // Clean up 4797 $slide.css({ 4798 transform: "", 4799 opacity: "" 4800 }); 4801 4802 if (!current.isComplete) { 4803 self.complete(); 4804 } 4805 }, 4806 false 4807 ); 4808 }, 4809 4810 // Check if current slide is moved (swiped) 4811 // ======================================== 4812 4813 isMoved: function (slide) { 4814 var current = slide || this.current, 4815 slidePos, 4816 stagePos; 4817 4818 if (!current) { 4819 return false; 4820 } 4821 4822 stagePos = $.fancybox.getTranslate(this.$refs.stage); 4823 slidePos = $.fancybox.getTranslate(current.$slide); 4824 4825 return ( 4826 !current.$slide.hasClass("fancybox-animated") && 4827 (Math.abs(slidePos.top - stagePos.top) > 0.5 || Math.abs(slidePos.left - stagePos.left) > 0.5) 4828 ); 4829 }, 4830 4831 // Update cursor style depending if content can be zoomed 4832 // ====================================================== 4833 4834 updateCursor: function (nextWidth, nextHeight) { 4835 var self = this, 4836 current = self.current, 4837 $container = self.$refs.container, 4838 canPan, 4839 isZoomable; 4840 4841 if (!current || self.isClosing || !self.Guestures) { 4842 return; 4843 } 4844 4845 $container.removeClass("fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-zoomOut fancybox-can-swipe fancybox-can-pan"); 4846 4847 canPan = self.canPan(nextWidth, nextHeight); 4848 4849 isZoomable = canPan ? true : self.isZoomable(); 4850 4851 $container.toggleClass("fancybox-is-zoomable", isZoomable); 4852 4853 $("[data-fancybox-zoom]").prop("disabled", !isZoomable); 4854 4855 if (canPan) { 4856 $container.addClass("fancybox-can-pan"); 4857 } else if ( 4858 isZoomable && 4859 (current.opts.clickContent === "zoom" || ($.isFunction(current.opts.clickContent) && current.opts.clickContent(current) == "zoom")) 4860 ) { 4861 $container.addClass("fancybox-can-zoomIn"); 4862 } else if (current.opts.touch && (current.opts.touch.vertical || self.group.length > 1) && current.contentType !== "video") { 4863 $container.addClass("fancybox-can-swipe"); 4864 } 4865 }, 4866 4867 // Check if current slide is zoomable 4868 // ================================== 4869 4870 isZoomable: function () { 4871 var self = this, 4872 current = self.current, 4873 fitPos; 4874 4875 // Assume that slide is zoomable if: 4876 // - image is still loading 4877 // - actual size of the image is smaller than available area 4878 if (current && !self.isClosing && current.type === "image" && !current.hasError) { 4879 if (!current.isLoaded) { 4880 return true; 4881 } 4882 4883 fitPos = self.getFitPos(current); 4884 4885 if (fitPos && (current.width > fitPos.width || current.height > fitPos.height)) { 4886 return true; 4887 } 4888 } 4889 4890 return false; 4891 }, 4892 4893 // Check if current image dimensions are smaller than actual 4894 // ========================================================= 4895 4896 isScaledDown: function (nextWidth, nextHeight) { 4897 var self = this, 4898 rez = false, 4899 current = self.current, 4900 $content = current.$content; 4901 4902 if (nextWidth !== undefined && nextHeight !== undefined) { 4903 rez = nextWidth < current.width && nextHeight < current.height; 4904 } else if ($content) { 4905 rez = $.fancybox.getTranslate($content); 4906 rez = rez.width < current.width && rez.height < current.height; 4907 } 4908 4909 return rez; 4910 }, 4911 4912 // Check if image dimensions exceed parent element 4913 // =============================================== 4914 4915 canPan: function (nextWidth, nextHeight) { 4916 var self = this, 4917 current = self.current, 4918 pos = null, 4919 rez = false; 4920 4921 if (current.type === "image" && (current.isComplete || (nextWidth && nextHeight)) && !current.hasError) { 4922 rez = self.getFitPos(current); 4923 4924 if (nextWidth !== undefined && nextHeight !== undefined) { 4925 pos = { 4926 width: nextWidth, 4927 height: nextHeight 4928 }; 4929 } else if (current.isComplete) { 4930 pos = $.fancybox.getTranslate(current.$content); 4931 } 4932 4933 if (pos && rez) { 4934 rez = Math.abs(pos.width - rez.width) > 1.5 || Math.abs(pos.height - rez.height) > 1.5; 4935 } 4936 } 4937 4938 return rez; 4939 }, 4940 4941 // Load content into the slide 4942 // =========================== 4943 4944 loadSlide: function (slide) { 4945 var self = this, 4946 type, 4947 $slide, 4948 ajaxLoad; 4949 4950 if (slide.isLoading || slide.isLoaded) { 4951 return; 4952 } 4953 4954 slide.isLoading = true; 4955 4956 if (self.trigger("beforeLoad", slide) === false) { 4957 slide.isLoading = false; 4958 4959 return false; 4960 } 4961 4962 type = slide.type; 4963 $slide = slide.$slide; 4964 4965 $slide 4966 .off("refresh") 4967 .trigger("onReset") 4968 .addClass(slide.opts.slideClass); 4969 4970 // Create content depending on the type 4971 switch (type) { 4972 case "image": 4973 self.setImage(slide); 4974 4975 break; 4976 4977 case "iframe": 4978 self.setIframe(slide); 4979 4980 break; 4981 4982 case "html": 4983 self.setContent(slide, slide.src || slide.content); 4984 4985 break; 4986 4987 case "video": 4988 self.setContent( 4989 slide, 4990 slide.opts.video.tpl 4991 .replace(/\{\{src\}\}/gi, slide.src) 4992 .replace("{{format}}", slide.opts.videoFormat || slide.opts.video.format || "") 4993 .replace("{{poster}}", slide.thumb || "") 4994 ); 4995 4996 break; 4997 4998 case "inline": 4999 if ($(slide.src).length) { 5000 self.setContent(slide, $(slide.src)); 5001 } else { 5002 self.setError(slide); 5003 } 5004 5005 break; 5006 5007 case "ajax": 5008 self.showLoading(slide); 5009 5010 ajaxLoad = $.ajax( 5011 $.extend({}, slide.opts.ajax.settings, { 5012 url: slide.src, 5013 success: function (data, textStatus) { 5014 if (textStatus === "success") { 5015 self.setContent(slide, data); 5016 } 5017 }, 5018 error: function (jqXHR, textStatus) { 5019 if (jqXHR && textStatus !== "abort") { 5020 self.setError(slide); 5021 } 5022 } 5023 }) 5024 ); 5025 5026 $slide.one("onReset", function () { 5027 ajaxLoad.abort(); 5028 }); 5029 5030 break; 5031 5032 default: 5033 self.setError(slide); 5034 5035 break; 5036 } 5037 5038 return true; 5039 }, 5040 5041 // Use thumbnail image, if possible 5042 // ================================ 5043 5044 setImage: function (slide) { 5045 var self = this, 5046 ghost; 5047 5048 // Check if need to show loading icon 5049 setTimeout(function () { 5050 var $img = slide.$image; 5051 5052 if (!self.isClosing && slide.isLoading && (!$img || !$img.length || !$img[0].complete) && !slide.hasError) { 5053 self.showLoading(slide); 5054 } 5055 }, 50); 5056 5057 //Check if image has srcset 5058 self.checkSrcset(slide); 5059 5060 // This will be wrapper containing both ghost and actual image 5061 slide.$content = $('<div class="fancybox-content"></div>') 5062 .addClass("fancybox-is-hidden") 5063 .appendTo(slide.$slide.addClass("fancybox-slide--image")); 5064 5065 // If we have a thumbnail, we can display it while actual image is loading 5066 // Users will not stare at black screen and actual image will appear gradually 5067 if (slide.opts.preload !== false && slide.opts.width && slide.opts.height && slide.thumb) { 5068 slide.width = slide.opts.width; 5069 slide.height = slide.opts.height; 5070 5071 ghost = document.createElement("img"); 5072 5073 ghost.onerror = function () { 5074 $(this).remove(); 5075 5076 slide.$ghost = null; 5077 }; 5078 5079 ghost.onload = function () { 5080 self.afterLoad(slide); 5081 }; 5082 5083 slide.$ghost = $(ghost) 5084 .addClass("fancybox-image") 5085 .appendTo(slide.$content) 5086 .attr("src", slide.thumb); 5087 } 5088 5089 // Start loading actual image 5090 self.setBigImage(slide); 5091 }, 5092 5093 // Check if image has srcset and get the source 5094 // ============================================ 5095 checkSrcset: function (slide) { 5096 var srcset = slide.opts.srcset || slide.opts.image.srcset, 5097 found, 5098 temp, 5099 pxRatio, 5100 windowWidth; 5101 5102 // If we have "srcset", then we need to find first matching "src" value. 5103 // This is necessary, because when you set an src attribute, the browser will preload the image 5104 // before any javascript or even CSS is applied. 5105 if (srcset) { 5106 pxRatio = window.devicePixelRatio || 1; 5107 windowWidth = window.innerWidth * pxRatio; 5108 5109 temp = srcset.split(",").map(function (el) { 5110 var ret = {}; 5111 5112 el.trim() 5113 .split(/\s+/) 5114 .forEach(function (el, i) { 5115 var value = parseInt(el.substring(0, el.length - 1), 10); 5116 5117 if (i === 0) { 5118 return (ret.url = el); 5119 } 5120 5121 if (value) { 5122 ret.value = value; 5123 ret.postfix = el[el.length - 1]; 5124 } 5125 }); 5126 5127 return ret; 5128 }); 5129 5130 // Sort by value 5131 temp.sort(function (a, b) { 5132 return a.value - b.value; 5133 }); 5134 5135 // Ok, now we have an array of all srcset values 5136 for (var j = 0; j < temp.length; j++) { 5137 var el = temp[j]; 5138 5139 if ((el.postfix === "w" && el.value >= windowWidth) || (el.postfix === "x" && el.value >= pxRatio)) { 5140 found = el; 5141 break; 5142 } 5143 } 5144 5145 // If not found, take the last one 5146 if (!found && temp.length) { 5147 found = temp[temp.length - 1]; 5148 } 5149 5150 if (found) { 5151 slide.src = found.url; 5152 5153 // If we have default width/height values, we can calculate height for matching source 5154 if (slide.width && slide.height && found.postfix == "w") { 5155 slide.height = (slide.width / slide.height) * found.value; 5156 slide.width = found.value; 5157 } 5158 5159 slide.opts.srcset = srcset; 5160 } 5161 } 5162 }, 5163 5164 // Create full-size image 5165 // ====================== 5166 5167 setBigImage: function (slide) { 5168 var self = this, 5169 img = document.createElement("img"), 5170 $img = $(img); 5171 5172 slide.$image = $img 5173 .one("error", function () { 5174 self.setError(slide); 5175 }) 5176 .one("load", function () { 5177 var sizes; 5178 5179 if (!slide.$ghost) { 5180 self.resolveImageSlideSize(slide, this.naturalWidth, this.naturalHeight); 5181 5182 self.afterLoad(slide); 5183 } 5184 5185 if (self.isClosing) { 5186 return; 5187 } 5188 5189 if (slide.opts.srcset) { 5190 sizes = slide.opts.sizes; 5191 5192 if (!sizes || sizes === "auto") { 5193 sizes = 5194 (slide.width / slide.height > 1 && $W.width() / $W.height() > 1 ? "100" : Math.round((slide.width / slide.height) * 100)) + 5195 "vw"; 5196 } 5197 5198 $img.attr("sizes", sizes).attr("srcset", slide.opts.srcset); 5199 } 5200 5201 // Hide temporary image after some delay 5202 if (slide.$ghost) { 5203 setTimeout(function () { 5204 if (slide.$ghost && !self.isClosing) { 5205 slide.$ghost.hide(); 5206 } 5207 }, Math.min(300, Math.max(1000, slide.height / 1600))); 5208 } 5209 5210 self.hideLoading(slide); 5211 }) 5212 .addClass("fancybox-image") 5213 .attr("src", slide.src) 5214 .appendTo(slide.$content); 5215 5216 if ((img.complete || img.readyState == "complete") && $img.naturalWidth && $img.naturalHeight) { 5217 $img.trigger("load"); 5218 } else if (img.error) { 5219 $img.trigger("error"); 5220 } 5221 }, 5222 5223 // Computes the slide size from image size and maxWidth/maxHeight 5224 // ============================================================== 5225 5226 resolveImageSlideSize: function (slide, imgWidth, imgHeight) { 5227 var maxWidth = parseInt(slide.opts.width, 10), 5228 maxHeight = parseInt(slide.opts.height, 10); 5229 5230 // Sets the default values from the image 5231 slide.width = imgWidth; 5232 slide.height = imgHeight; 5233 5234 if (maxWidth > 0) { 5235 slide.width = maxWidth; 5236 slide.height = Math.floor((maxWidth * imgHeight) / imgWidth); 5237 } 5238 5239 if (maxHeight > 0) { 5240 slide.width = Math.floor((maxHeight * imgWidth) / imgHeight); 5241 slide.height = maxHeight; 5242 } 5243 }, 5244 5245 // Create iframe wrapper, iframe and bindings 5246 // ========================================== 5247 5248 setIframe: function (slide) { 5249 var self = this, 5250 opts = slide.opts.iframe, 5251 $slide = slide.$slide, 5252 $iframe; 5253 5254 slide.$content = $('<div class="fancybox-content' + (opts.preload ? " fancybox-is-hidden" : "") + '"></div>') 5255 .css(opts.css) 5256 .appendTo($slide); 5257 5258 $slide.addClass("fancybox-slide--" + slide.contentType); 5259 5260 slide.$iframe = $iframe = $(opts.tpl.replace(/\{rnd\}/g, new Date().getTime())) 5261 .attr(opts.attr) 5262 .appendTo(slide.$content); 5263 5264 if (opts.preload) { 5265 self.showLoading(slide); 5266 5267 // Unfortunately, it is not always possible to determine if iframe is successfully loaded 5268 // (due to browser security policy) 5269 5270 $iframe.on("load.fb error.fb", function (e) { 5271 this.isReady = 1; 5272 5273 slide.$slide.trigger("refresh"); 5274 5275 self.afterLoad(slide); 5276 }); 5277 5278 // Recalculate iframe content size 5279 // =============================== 5280 5281 $slide.on("refresh.fb", function () { 5282 var $content = slide.$content, 5283 frameWidth = opts.css.width, 5284 frameHeight = opts.css.height, 5285 $contents, 5286 $body; 5287 5288 if ($iframe[0].isReady !== 1) { 5289 return; 5290 } 5291 5292 try { 5293 $contents = $iframe.contents(); 5294 $body = $contents.find("body"); 5295 } catch (ignore) {} 5296 5297 // Calculate content dimensions, if it is accessible 5298 if ($body && $body.length && $body.children().length) { 5299 // Avoid scrolling to top (if multiple instances) 5300 $slide.css("overflow", "visible"); 5301 5302 $content.css({ 5303 width: "100%", 5304 "max-width": "100%", 5305 height: "9999px" 5306 }); 5307 5308 if (frameWidth === undefined) { 5309 frameWidth = Math.ceil(Math.max($body[0].clientWidth, $body.outerWidth(true))); 5310 } 5311 5312 $content.css("width", frameWidth ? frameWidth : "").css("max-width", ""); 5313 5314 if (frameHeight === undefined) { 5315 frameHeight = Math.ceil(Math.max($body[0].clientHeight, $body.outerHeight(true))); 5316 } 5317 5318 $content.css("height", frameHeight ? frameHeight : ""); 5319 5320 $slide.css("overflow", "auto"); 5321 } 5322 5323 $content.removeClass("fancybox-is-hidden"); 5324 }); 5325 } else { 5326 self.afterLoad(slide); 5327 } 5328 5329 $iframe.attr("src", slide.src); 5330 5331 // Remove iframe if closing or changing gallery item 5332 $slide.one("onReset", function () { 5333 // This helps IE not to throw errors when closing 5334 try { 5335 $(this) 5336 .find("iframe") 5337 .hide() 5338 .unbind() 5339 .attr("src", "//about:blank"); 5340 } catch (ignore) {} 5341 5342 $(this) 5343 .off("refresh.fb") 5344 .empty(); 5345 5346 slide.isLoaded = false; 5347 slide.isRevealed = false; 5348 }); 5349 }, 5350 5351 // Wrap and append content to the slide 5352 // ====================================== 5353 5354 setContent: function (slide, content) { 5355 var self = this; 5356 5357 if (self.isClosing) { 5358 return; 5359 } 5360 5361 self.hideLoading(slide); 5362 5363 if (slide.$content) { 5364 $.fancybox.stop(slide.$content); 5365 } 5366 5367 slide.$slide.empty(); 5368 5369 // If content is a jQuery object, then it will be moved to the slide. 5370 // The placeholder is created so we will know where to put it back. 5371 if (isQuery(content) && content.parent().length) { 5372 // Make sure content is not already moved to fancyBox 5373 if (content.hasClass("fancybox-content") || content.parent().hasClass("fancybox-content")) { 5374 content.parents(".fancybox-slide").trigger("onReset"); 5375 } 5376 5377 // Create temporary element marking original place of the content 5378 slide.$placeholder = $("<div>") 5379 .hide() 5380 .insertAfter(content); 5381 5382 // Make sure content is visible 5383 content.css("display", "inline-block"); 5384 } else if (!slide.hasError) { 5385 // If content is just a plain text, try to convert it to html 5386 if ($.type(content) === "string") { 5387 content = $("<div>") 5388 .append($.trim(content)) 5389 .contents(); 5390 } 5391 5392 // If "filter" option is provided, then filter content 5393 if (slide.opts.filter) { 5394 content = $("<div>") 5395 .html(content) 5396 .find(slide.opts.filter); 5397 } 5398 } 5399 5400 slide.$slide.one("onReset", function () { 5401 // Pause all html5 video/audio 5402 $(this) 5403 .find("video,audio") 5404 .trigger("pause"); 5405 5406 // Put content back 5407 if (slide.$placeholder) { 5408 slide.$placeholder.after(content.removeClass("fancybox-content").hide()).remove(); 5409 5410 slide.$placeholder = null; 5411 } 5412 5413 // Remove custom close button 5414 if (slide.$smallBtn) { 5415 slide.$smallBtn.remove(); 5416 5417 slide.$smallBtn = null; 5418 } 5419 5420 // Remove content and mark slide as not loaded 5421 if (!slide.hasError) { 5422 $(this).empty(); 5423 5424 slide.isLoaded = false; 5425 slide.isRevealed = false; 5426 } 5427 }); 5428 5429 $(content).appendTo(slide.$slide); 5430 5431 if ($(content).is("video,audio")) { 5432 $(content).addClass("fancybox-video"); 5433 5434 $(content).wrap("<div></div>"); 5435 5436 slide.contentType = "video"; 5437 5438 slide.opts.width = slide.opts.width || $(content).attr("width"); 5439 slide.opts.height = slide.opts.height || $(content).attr("height"); 5440 } 5441 5442 slide.$content = slide.$slide 5443 .children() 5444 .filter("div,form,main,video,audio,article,.fancybox-content") 5445 .first(); 5446 5447 slide.$content.siblings().hide(); 5448 5449 // Re-check if there is a valid content 5450 // (in some cases, ajax response can contain various elements or plain text) 5451 if (!slide.$content.length) { 5452 slide.$content = slide.$slide 5453 .wrapInner("<div></div>") 5454 .children() 5455 .first(); 5456 } 5457 5458 slide.$content.addClass("fancybox-content"); 5459 5460 slide.$slide.addClass("fancybox-slide--" + slide.contentType); 5461 5462 self.afterLoad(slide); 5463 }, 5464 5465 // Display error message 5466 // ===================== 5467 5468 setError: function (slide) { 5469 slide.hasError = true; 5470 5471 slide.$slide 5472 .trigger("onReset") 5473 .removeClass("fancybox-slide--" + slide.contentType) 5474 .addClass("fancybox-slide--error"); 5475 5476 slide.contentType = "html"; 5477 5478 this.setContent(slide, this.translate(slide, slide.opts.errorTpl)); 5479 5480 if (slide.pos === this.currPos) { 5481 this.isAnimating = false; 5482 } 5483 }, 5484 5485 // Show loading icon inside the slide 5486 // ================================== 5487 5488 showLoading: function (slide) { 5489 var self = this; 5490 5491 slide = slide || self.current; 5492 5493 if (slide && !slide.$spinner) { 5494 slide.$spinner = $(self.translate(self, self.opts.spinnerTpl)) 5495 .appendTo(slide.$slide) 5496 .hide() 5497 .fadeIn("fast"); 5498 } 5499 }, 5500 5501 // Remove loading icon from the slide 5502 // ================================== 5503 5504 hideLoading: function (slide) { 5505 var self = this; 5506 5507 slide = slide || self.current; 5508 5509 if (slide && slide.$spinner) { 5510 slide.$spinner.stop().remove(); 5511 5512 delete slide.$spinner; 5513 } 5514 }, 5515 5516 // Adjustments after slide content has been loaded 5517 // =============================================== 5518 5519 afterLoad: function (slide) { 5520 var self = this; 5521 5522 if (self.isClosing) { 5523 return; 5524 } 5525 5526 slide.isLoading = false; 5527 slide.isLoaded = true; 5528 5529 self.trigger("afterLoad", slide); 5530 5531 self.hideLoading(slide); 5532 5533 // Add small close button 5534 if (slide.opts.smallBtn && (!slide.$smallBtn || !slide.$smallBtn.length)) { 5535 slide.$smallBtn = $(self.translate(slide, slide.opts.btnTpl.smallBtn)).appendTo(slide.$content); 5536 } 5537 5538 // Disable right click 5539 if (slide.opts.protect && slide.$content && !slide.hasError) { 5540 slide.$content.on("contextmenu.fb", function (e) { 5541 if (e.button == 2) { 5542 e.preventDefault(); 5543 } 5544 5545 return true; 5546 }); 5547 5548 // Add fake element on top of the image 5549 // This makes a bit harder for user to select image 5550 if (slide.type === "image") { 5551 $('<div class="fancybox-spaceball"></div>').appendTo(slide.$content); 5552 } 5553 } 5554 5555 self.adjustCaption(slide); 5556 5557 self.adjustLayout(slide); 5558 5559 if (slide.pos === self.currPos) { 5560 self.updateCursor(); 5561 } 5562 5563 self.revealContent(slide); 5564 }, 5565 5566 // Prevent caption overlap, 5567 // fix css inconsistency across browsers 5568 // ===================================== 5569 5570 adjustCaption: function (slide) { 5571 var self = this, 5572 current = slide || self.current, 5573 caption = current.opts.caption, 5574 preventOverlap = current.opts.preventCaptionOverlap, 5575 $caption = self.$refs.caption, 5576 $clone, 5577 captionH = false; 5578 5579 $caption.toggleClass("fancybox-caption--separate", preventOverlap); 5580 5581 if (preventOverlap && caption && caption.length) { 5582 if (current.pos !== self.currPos) { 5583 $clone = $caption.clone().appendTo($caption.parent()); 5584 5585 $clone 5586 .children() 5587 .eq(0) 5588 .empty() 5589 .html(caption); 5590 5591 captionH = $clone.outerHeight(true); 5592 5593 $clone.empty().remove(); 5594 } else if (self.$caption) { 5595 captionH = self.$caption.outerHeight(true); 5596 } 5597 5598 current.$slide.css("padding-bottom", captionH || ""); 5599 } 5600 }, 5601 5602 // Simple hack to fix inconsistency across browsers, described here (affects Edge, too): 5603 // https://bugzilla.mozilla.org/show_bug.cgi?id=748518 5604 // ==================================================================================== 5605 5606 adjustLayout: function (slide) { 5607 var self = this, 5608 current = slide || self.current, 5609 scrollHeight, 5610 marginBottom, 5611 inlinePadding, 5612 actualPadding; 5613 5614 if (current.isLoaded && current.opts.disableLayoutFix !== true) { 5615 current.$content.css("margin-bottom", ""); 5616 5617 // If we would always set margin-bottom for the content, 5618 // then it would potentially break vertical align 5619 if (current.$content.outerHeight() > current.$slide.height() + 0.5) { 5620 inlinePadding = current.$slide[0].style["padding-bottom"]; 5621 actualPadding = current.$slide.css("padding-bottom"); 5622 5623 if (parseFloat(actualPadding) > 0) { 5624 scrollHeight = current.$slide[0].scrollHeight; 5625 5626 current.$slide.css("padding-bottom", 0); 5627 5628 if (Math.abs(scrollHeight - current.$slide[0].scrollHeight) < 1) { 5629 marginBottom = actualPadding; 5630 } 5631 5632 current.$slide.css("padding-bottom", inlinePadding); 5633 } 5634 } 5635 5636 current.$content.css("margin-bottom", marginBottom); 5637 } 5638 }, 5639 5640 // Make content visible 5641 // This method is called right after content has been loaded or 5642 // user navigates gallery and transition should start 5643 // ============================================================ 5644 5645 revealContent: function (slide) { 5646 var self = this, 5647 $slide = slide.$slide, 5648 end = false, 5649 start = false, 5650 isMoved = self.isMoved(slide), 5651 isRevealed = slide.isRevealed, 5652 effect, 5653 effectClassName, 5654 duration, 5655 opacity; 5656 5657 slide.isRevealed = true; 5658 5659 effect = slide.opts[self.firstRun ? "animationEffect" : "transitionEffect"]; 5660 duration = slide.opts[self.firstRun ? "animationDuration" : "transitionDuration"]; 5661 5662 duration = parseInt(slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10); 5663 5664 if (isMoved || slide.pos !== self.currPos || !duration) { 5665 effect = false; 5666 } 5667 5668 // Check if can zoom 5669 if (effect === "zoom") { 5670 if (slide.pos === self.currPos && duration && slide.type === "image" && !slide.hasError && (start = self.getThumbPos(slide))) { 5671 end = self.getFitPos(slide); 5672 } else { 5673 effect = "fade"; 5674 } 5675 } 5676 5677 // Zoom animation 5678 // ============== 5679 if (effect === "zoom") { 5680 self.isAnimating = true; 5681 5682 end.scaleX = end.width / start.width; 5683 end.scaleY = end.height / start.height; 5684 5685 // Check if we need to animate opacity 5686 opacity = slide.opts.zoomOpacity; 5687 5688 if (opacity == "auto") { 5689 opacity = Math.abs(slide.width / slide.height - start.width / start.height) > 0.1; 5690 } 5691 5692 if (opacity) { 5693 start.opacity = 0.1; 5694 end.opacity = 1; 5695 } 5696 5697 // Draw image at start position 5698 $.fancybox.setTranslate(slide.$content.removeClass("fancybox-is-hidden"), start); 5699 5700 forceRedraw(slide.$content); 5701 5702 // Start animation 5703 $.fancybox.animate(slide.$content, end, duration, function () { 5704 self.isAnimating = false; 5705 5706 self.complete(); 5707 }); 5708 5709 return; 5710 } 5711 5712 self.updateSlide(slide); 5713 5714 // Simply show content if no effect 5715 // ================================ 5716 if (!effect) { 5717 slide.$content.removeClass("fancybox-is-hidden"); 5718 5719 if (!isRevealed && isMoved && slide.type === "image" && !slide.hasError) { 5720 slide.$content.hide().fadeIn("fast"); 5721 } 5722 5723 if (slide.pos === self.currPos) { 5724 self.complete(); 5725 } 5726 5727 return; 5728 } 5729 5730 // Prepare for CSS transiton 5731 // ========================= 5732 $.fancybox.stop($slide); 5733 5734 //effectClassName = "fancybox-animated fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-fx-" + effect; 5735 effectClassName = "fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-animated fancybox-fx-" + effect; 5736 5737 $slide.addClass(effectClassName).removeClass("fancybox-slide--current"); //.addClass(effectClassName); 5738 5739 slide.$content.removeClass("fancybox-is-hidden"); 5740 5741 // Force reflow 5742 forceRedraw($slide); 5743 5744 if (slide.type !== "image") { 5745 slide.$content.hide().show(0); 5746 } 5747 5748 $.fancybox.animate( 5749 $slide, 5750 "fancybox-slide--current", 5751 duration, 5752 function () { 5753 $slide.removeClass(effectClassName).css({ 5754 transform: "", 5755 opacity: "" 5756 }); 5757 5758 if (slide.pos === self.currPos) { 5759 self.complete(); 5760 } 5761 }, 5762 true 5763 ); 5764 }, 5765 5766 // Check if we can and have to zoom from thumbnail 5767 //================================================ 5768 5769 getThumbPos: function (slide) { 5770 var rez = false, 5771 $thumb = slide.$thumb, 5772 thumbPos, 5773 btw, 5774 brw, 5775 bbw, 5776 blw; 5777 5778 if (!$thumb || !inViewport($thumb[0])) { 5779 return false; 5780 } 5781 5782 thumbPos = $.fancybox.getTranslate($thumb); 5783 5784 btw = parseFloat($thumb.css("border-top-width") || 0); 5785 brw = parseFloat($thumb.css("border-right-width") || 0); 5786 bbw = parseFloat($thumb.css("border-bottom-width") || 0); 5787 blw = parseFloat($thumb.css("border-left-width") || 0); 5788 5789 rez = { 5790 top: thumbPos.top + btw, 5791 left: thumbPos.left + blw, 5792 width: thumbPos.width - brw - blw, 5793 height: thumbPos.height - btw - bbw, 5794 scaleX: 1, 5795 scaleY: 1 5796 }; 5797 5798 return thumbPos.width > 0 && thumbPos.height > 0 ? rez : false; 5799 }, 5800 5801 // Final adjustments after current gallery item is moved to position 5802 // and it`s content is loaded 5803 // ================================================================== 5804 5805 complete: function () { 5806 var self = this, 5807 current = self.current, 5808 slides = {}, 5809 $el; 5810 5811 if (self.isMoved() || !current.isLoaded) { 5812 return; 5813 } 5814 5815 if (!current.isComplete) { 5816 current.isComplete = true; 5817 5818 current.$slide.siblings().trigger("onReset"); 5819 5820 self.preload("inline"); 5821 5822 // Trigger any CSS transiton inside the slide 5823 forceRedraw(current.$slide); 5824 5825 current.$slide.addClass("fancybox-slide--complete"); 5826 5827 // Remove unnecessary slides 5828 $.each(self.slides, function (key, slide) { 5829 if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) { 5830 slides[slide.pos] = slide; 5831 } else if (slide) { 5832 $.fancybox.stop(slide.$slide); 5833 5834 slide.$slide.off().remove(); 5835 } 5836 }); 5837 5838 self.slides = slides; 5839 } 5840 5841 self.isAnimating = false; 5842 5843 self.updateCursor(); 5844 5845 self.trigger("afterShow"); 5846 5847 // Autoplay first html5 video/audio 5848 if (!!current.opts.video.autoStart) { 5849 current.$slide 5850 .find("video,audio") 5851 .filter(":visible:first") 5852 .trigger("play") 5853 .one("ended", function () { 5854 if (Document.exitFullscreen) { 5855 Document.exitFullscreen(); 5856 } else if (this.webkitExitFullscreen) { 5857 this.webkitExitFullscreen(); 5858 } 5859 5860 self.next(); 5861 }); 5862 } 5863 5864 // Try to focus on the first focusable element 5865 if (current.opts.autoFocus && current.contentType === "html") { 5866 // Look for the first input with autofocus attribute 5867 $el = current.$content.find("input[autofocus]:enabled:visible:first"); 5868 5869 if ($el.length) { 5870 $el.trigger("focus"); 5871 } else { 5872 self.focus(null, true); 5873 } 5874 } 5875 5876 // Avoid jumping 5877 current.$slide.scrollTop(0).scrollLeft(0); 5878 }, 5879 5880 // Preload next and previous slides 5881 // ================================ 5882 5883 preload: function (type) { 5884 var self = this, 5885 prev, 5886 next; 5887 5888 if (self.group.length < 2) { 5889 return; 5890 } 5891 5892 next = self.slides[self.currPos + 1]; 5893 prev = self.slides[self.currPos - 1]; 5894 5895 if (prev && prev.type === type) { 5896 self.loadSlide(prev); 5897 } 5898 5899 if (next && next.type === type) { 5900 self.loadSlide(next); 5901 } 5902 }, 5903 5904 // Try to find and focus on the first focusable element 5905 // ==================================================== 5906 5907 focus: function (e, firstRun) { 5908 var self = this, 5909 focusableStr = [ 5910 "a[href]", 5911 "area[href]", 5912 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 5913 "select:not([disabled]):not([aria-hidden])", 5914 "textarea:not([disabled]):not([aria-hidden])", 5915 "button:not([disabled]):not([aria-hidden])", 5916 "iframe", 5917 "object", 5918 "embed", 5919 "video", 5920 "audio", 5921 "[contenteditable]", 5922 '[tabindex]:not([tabindex^="-"])' 5923 ].join(","), 5924 focusableItems, 5925 focusedItemIndex; 5926 5927 if (self.isClosing) { 5928 return; 5929 } 5930 5931 if (e || !self.current || !self.current.isComplete) { 5932 // Focus on any element inside fancybox 5933 focusableItems = self.$refs.container.find("*:visible"); 5934 } else { 5935 // Focus inside current slide 5936 focusableItems = self.current.$slide.find("*:visible" + (firstRun ? ":not(.fancybox-close-small)" : "")); 5937 } 5938 5939 focusableItems = focusableItems.filter(focusableStr).filter(function () { 5940 return $(this).css("visibility") !== "hidden" && !$(this).hasClass("disabled"); 5941 }); 5942 5943 if (focusableItems.length) { 5944 focusedItemIndex = focusableItems.index(document.activeElement); 5945 5946 if (e && e.shiftKey) { 5947 // Back tab 5948 if (focusedItemIndex < 0 || focusedItemIndex == 0) { 5949 e.preventDefault(); 5950 5951 focusableItems.eq(focusableItems.length - 1).trigger("focus"); 5952 } 5953 } else { 5954 // Outside or Forward tab 5955 if (focusedItemIndex < 0 || focusedItemIndex == focusableItems.length - 1) { 5956 if (e) { 5957 e.preventDefault(); 5958 } 5959 5960 focusableItems.eq(0).trigger("focus"); 5961 } 5962 } 5963 } else { 5964 self.$refs.container.trigger("focus"); 5965 } 5966 }, 5967 5968 // Activates current instance - brings container to the front and enables keyboard, 5969 // notifies other instances about deactivating 5970 // ================================================================================= 5971 5972 activate: function () { 5973 var self = this; 5974 5975 // Deactivate all instances 5976 $(".fancybox-container").each(function () { 5977 var instance = $(this).data("FancyBox"); 5978 5979 // Skip self and closing instances 5980 if (instance && instance.id !== self.id && !instance.isClosing) { 5981 instance.trigger("onDeactivate"); 5982 5983 instance.removeEvents(); 5984 5985 instance.isVisible = false; 5986 } 5987 }); 5988 5989 self.isVisible = true; 5990 5991 if (self.current || self.isIdle) { 5992 self.update(); 5993 5994 self.updateControls(); 5995 } 5996 5997 self.trigger("onActivate"); 5998 5999 self.addEvents(); 6000 }, 6001 6002 // Start closing procedure 6003 // This will start "zoom-out" animation if needed and clean everything up afterwards 6004 // ================================================================================= 6005 6006 close: function (e, d) { 6007 var self = this, 6008 current = self.current, 6009 effect, 6010 duration, 6011 $content, 6012 domRect, 6013 opacity, 6014 start, 6015 end; 6016 6017 var done = function () { 6018 self.cleanUp(e); 6019 }; 6020 6021 if (self.isClosing) { 6022 return false; 6023 } 6024 6025 self.isClosing = true; 6026 6027 // If beforeClose callback prevents closing, make sure content is centered 6028 if (self.trigger("beforeClose", e) === false) { 6029 self.isClosing = false; 6030 6031 requestAFrame(function () { 6032 self.update(); 6033 }); 6034 6035 return false; 6036 } 6037 6038 // Remove all events 6039 // If there are multiple instances, they will be set again by "activate" method 6040 self.removeEvents(); 6041 6042 $content = current.$content; 6043 effect = current.opts.animationEffect; 6044 duration = $.isNumeric(d) ? d : effect ? current.opts.animationDuration : 0; 6045 6046 current.$slide.removeClass("fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated"); 6047 6048 if (e !== true) { 6049 $.fancybox.stop(current.$slide); 6050 } else { 6051 effect = false; 6052 } 6053 6054 // Remove other slides 6055 current.$slide 6056 .siblings() 6057 .trigger("onReset") 6058 .remove(); 6059 6060 // Trigger animations 6061 if (duration) { 6062 self.$refs.container 6063 .removeClass("fancybox-is-open") 6064 .addClass("fancybox-is-closing") 6065 .css("transition-duration", duration + "ms"); 6066 } 6067 6068 // Clean up 6069 self.hideLoading(current); 6070 6071 self.hideControls(true); 6072 6073 self.updateCursor(); 6074 6075 // Check if possible to zoom-out 6076 if ( 6077 effect === "zoom" && 6078 !($content && duration && current.type === "image" && !self.isMoved() && !current.hasError && (end = self.getThumbPos(current))) 6079 ) { 6080 effect = "fade"; 6081 } 6082 6083 if (effect === "zoom") { 6084 $.fancybox.stop($content); 6085 6086 domRect = $.fancybox.getTranslate($content); 6087 6088 start = { 6089 top: domRect.top, 6090 left: domRect.left, 6091 scaleX: domRect.width / end.width, 6092 scaleY: domRect.height / end.height, 6093 width: end.width, 6094 height: end.height 6095 }; 6096 6097 // Check if we need to animate opacity 6098 opacity = current.opts.zoomOpacity; 6099 6100 if (opacity == "auto") { 6101 opacity = Math.abs(current.width / current.height - end.width / end.height) > 0.1; 6102 } 6103 6104 if (opacity) { 6105 end.opacity = 0; 6106 } 6107 6108 $.fancybox.setTranslate($content, start); 6109 6110 forceRedraw($content); 6111 6112 $.fancybox.animate($content, end, duration, done); 6113 6114 return true; 6115 } 6116 6117 if (effect && duration) { 6118 $.fancybox.animate( 6119 current.$slide.addClass("fancybox-slide--previous").removeClass("fancybox-slide--current"), 6120 "fancybox-animated fancybox-fx-" + effect, 6121 duration, 6122 done 6123 ); 6124 } else { 6125 // If skip animation 6126 if (e === true) { 6127 setTimeout(done, duration); 6128 } else { 6129 done(); 6130 } 6131 } 6132 6133 return true; 6134 }, 6135 6136 // Final adjustments after removing the instance 6137 // ============================================= 6138 6139 cleanUp: function (e) { 6140 var self = this, 6141 instance, 6142 $focus = self.current.opts.$orig, 6143 x, 6144 y; 6145 6146 self.current.$slide.trigger("onReset"); 6147 6148 self.$refs.container.empty().remove(); 6149 6150 self.trigger("afterClose", e); 6151 6152 // Place back focus 6153 if (!!self.current.opts.backFocus) { 6154 if (!$focus || !$focus.length || !$focus.is(":visible")) { 6155 $focus = self.$trigger; 6156 } 6157 6158 if ($focus && $focus.length) { 6159 x = window.scrollX; 6160 y = window.scrollY; 6161 6162 $focus.trigger("focus"); 6163 6164 $("html, body") 6165 .scrollTop(y) 6166 .scrollLeft(x); 6167 } 6168 } 6169 6170 self.current = null; 6171 6172 // Check if there are other instances 6173 instance = $.fancybox.getInstance(); 6174 6175 if (instance) { 6176 instance.activate(); 6177 } else { 6178 $("body").removeClass("fancybox-active compensate-for-scrollbar"); 6179 6180 $("#fancybox-style-noscroll").remove(); 6181 } 6182 }, 6183 6184 // Call callback and trigger an event 6185 // ================================== 6186 6187 trigger: function (name, slide) { 6188 var args = Array.prototype.slice.call(arguments, 1), 6189 self = this, 6190 obj = slide && slide.opts ? slide : self.current, 6191 rez; 6192 6193 if (obj) { 6194 args.unshift(obj); 6195 } else { 6196 obj = self; 6197 } 6198 6199 args.unshift(self); 6200 6201 if ($.isFunction(obj.opts[name])) { 6202 rez = obj.opts[name].apply(obj, args); 6203 } 6204 6205 if (rez === false) { 6206 return rez; 6207 } 6208 6209 if (name === "afterClose" || !self.$refs) { 6210 $D.trigger(name + ".fb", args); 6211 } else { 6212 self.$refs.container.trigger(name + ".fb", args); 6213 } 6214 }, 6215 6216 // Update infobar values, navigation button states and reveal caption 6217 // ================================================================== 6218 6219 updateControls: function () { 6220 var self = this, 6221 current = self.current, 6222 index = current.index, 6223 $container = self.$refs.container, 6224 $caption = self.$refs.caption, 6225 caption = current.opts.caption; 6226 6227 // Recalculate content dimensions 6228 current.$slide.trigger("refresh"); 6229 6230 // Set caption 6231 if (caption && caption.length) { 6232 self.$caption = $caption; 6233 6234 $caption 6235 .children() 6236 .eq(0) 6237 .html(caption); 6238 } else { 6239 self.$caption = null; 6240 } 6241 6242 if (!self.hasHiddenControls && !self.isIdle) { 6243 self.showControls(); 6244 } 6245 6246 // Update info and navigation elements 6247 $container.find("[data-fancybox-count]").html(self.group.length); 6248 $container.find("[data-fancybox-index]").html(index + 1); 6249 6250 $container.find("[data-fancybox-prev]").prop("disabled", !current.opts.loop && index <= 0); 6251 $container.find("[data-fancybox-next]").prop("disabled", !current.opts.loop && index >= self.group.length - 1); 6252 6253 if (current.type === "image") { 6254 // Re-enable buttons; update download button source 6255 $container 6256 .find("[data-fancybox-zoom]") 6257 .show() 6258 .end() 6259 .find("[data-fancybox-download]") 6260 .attr("href", current.opts.image.src || current.src) 6261 .show(); 6262 } else if (current.opts.toolbar) { 6263 $container.find("[data-fancybox-download],[data-fancybox-zoom]").hide(); 6264 } 6265 6266 // Make sure focus is not on disabled button/element 6267 if ($(document.activeElement).is(":hidden,[disabled]")) { 6268 self.$refs.container.trigger("focus"); 6269 } 6270 }, 6271 6272 // Hide toolbar and caption 6273 // ======================== 6274 6275 hideControls: function (andCaption) { 6276 var self = this, 6277 arr = ["infobar", "toolbar", "nav"]; 6278 6279 if (andCaption || !self.current.opts.preventCaptionOverlap) { 6280 arr.push("caption"); 6281 } 6282 6283 this.$refs.container.removeClass( 6284 arr 6285 .map(function (i) { 6286 return "fancybox-show-" + i; 6287 }) 6288 .join(" ") 6289 ); 6290 6291 this.hasHiddenControls = true; 6292 }, 6293 6294 showControls: function () { 6295 var self = this, 6296 opts = self.current ? self.current.opts : self.opts, 6297 $container = self.$refs.container; 6298 6299 self.hasHiddenControls = false; 6300 self.idleSecondsCounter = 0; 6301 6302 $container 6303 .toggleClass("fancybox-show-toolbar", !!(opts.toolbar && opts.buttons)) 6304 .toggleClass("fancybox-show-infobar", !!(opts.infobar && self.group.length > 1)) 6305 .toggleClass("fancybox-show-caption", !!self.$caption) 6306 .toggleClass("fancybox-show-nav", !!(opts.arrows && self.group.length > 1)) 6307 .toggleClass("fancybox-is-modal", !!opts.modal); 6308 }, 6309 6310 // Toggle toolbar and caption 6311 // ========================== 6312 6313 toggleControls: function () { 6314 if (this.hasHiddenControls) { 6315 this.showControls(); 6316 } else { 6317 this.hideControls(); 6318 } 6319 } 6320 }); 6321 6322 $.fancybox = { 6323 version: "3.5.7", 6324 defaults: defaults, 6325 6326 // Get current instance and execute a command. 6327 // 6328 // Examples of usage: 6329 // 6330 // $instance = $.fancybox.getInstance(); 6331 // $.fancybox.getInstance().jumpTo( 1 ); 6332 // $.fancybox.getInstance( 'jumpTo', 1 ); 6333 // $.fancybox.getInstance( function() { 6334 // console.info( this.currIndex ); 6335 // }); 6336 // ====================================================== 6337 6338 getInstance: function (command) { 6339 var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data("FancyBox"), 6340 args = Array.prototype.slice.call(arguments, 1); 6341 6342 if (instance instanceof FancyBox) { 6343 if ($.type(command) === "string") { 6344 instance[command].apply(instance, args); 6345 } else if ($.type(command) === "function") { 6346 command.apply(instance, args); 6347 } 6348 6349 return instance; 6350 } 6351 6352 return false; 6353 }, 6354 6355 // Create new instance 6356 // =================== 6357 6358 open: function (items, opts, index) { 6359 return new FancyBox(items, opts, index); 6360 }, 6361 6362 // Close current or all instances 6363 // ============================== 6364 6365 close: function (all) { 6366 var instance = this.getInstance(); 6367 6368 if (instance) { 6369 instance.close(); 6370 6371 // Try to find and close next instance 6372 if (all === true) { 6373 this.close(all); 6374 } 6375 } 6376 }, 6377 6378 // Close all instances and unbind all events 6379 // ========================================= 6380 6381 destroy: function () { 6382 this.close(true); 6383 6384 $D.add("body").off("click.fb-start", "**"); 6385 }, 6386 6387 // Try to detect mobile devices 6388 // ============================ 6389 6390 isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), 6391 6392 // Detect if 'translate3d' support is available 6393 // ============================================ 6394 6395 use3d: (function () { 6396 var div = document.createElement("div"); 6397 6398 return ( 6399 window.getComputedStyle && 6400 window.getComputedStyle(div) && 6401 window.getComputedStyle(div).getPropertyValue("transform") && 6402 !(document.documentMode && document.documentMode < 11) 6403 ); 6404 })(), 6405 6406 // Helper function to get current visual state of an element 6407 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] 6408 // ===================================================================== 6409 6410 getTranslate: function ($el) { 6411 var domRect; 6412 6413 if (!$el || !$el.length) { 6414 return false; 6415 } 6416 6417 domRect = $el[0].getBoundingClientRect(); 6418 6419 return { 6420 top: domRect.top || 0, 6421 left: domRect.left || 0, 6422 width: domRect.width, 6423 height: domRect.height, 6424 opacity: parseFloat($el.css("opacity")) 6425 }; 6426 }, 6427 6428 // Shortcut for setting "translate3d" properties for element 6429 // Can set be used to set opacity, too 6430 // ======================================================== 6431 6432 setTranslate: function ($el, props) { 6433 var str = "", 6434 css = {}; 6435 6436 if (!$el || !props) { 6437 return; 6438 } 6439 6440 if (props.left !== undefined || props.top !== undefined) { 6441 str = 6442 (props.left === undefined ? $el.position().left : props.left) + 6443 "px, " + 6444 (props.top === undefined ? $el.position().top : props.top) + 6445 "px"; 6446 6447 if (this.use3d) { 6448 str = "translate3d(" + str + ", 0px)"; 6449 } else { 6450 str = "translate(" + str + ")"; 6451 } 6452 } 6453 6454 if (props.scaleX !== undefined && props.scaleY !== undefined) { 6455 str += " scale(" + props.scaleX + ", " + props.scaleY + ")"; 6456 } else if (props.scaleX !== undefined) { 6457 str += " scaleX(" + props.scaleX + ")"; 6458 } 6459 6460 if (str.length) { 6461 css.transform = str; 6462 } 6463 6464 if (props.opacity !== undefined) { 6465 css.opacity = props.opacity; 6466 } 6467 6468 if (props.width !== undefined) { 6469 css.width = props.width; 6470 } 6471 6472 if (props.height !== undefined) { 6473 css.height = props.height; 6474 } 6475 6476 return $el.css(css); 6477 }, 6478 6479 // Simple CSS transition handler 6480 // ============================= 6481 6482 animate: function ($el, to, duration, callback, leaveAnimationName) { 6483 var self = this, 6484 from; 6485 6486 if ($.isFunction(duration)) { 6487 callback = duration; 6488 duration = null; 6489 } 6490 6491 self.stop($el); 6492 6493 from = self.getTranslate($el); 6494 6495 $el.on(transitionEnd, function (e) { 6496 // Skip events from child elements and z-index change 6497 if (e && e.originalEvent && (!$el.is(e.originalEvent.target) || e.originalEvent.propertyName == "z-index")) { 6498 return; 6499 } 6500 6501 self.stop($el); 6502 6503 if ($.isNumeric(duration)) { 6504 $el.css("transition-duration", ""); 6505 } 6506 6507 if ($.isPlainObject(to)) { 6508 if (to.scaleX !== undefined && to.scaleY !== undefined) { 6509 self.setTranslate($el, { 6510 top: to.top, 6511 left: to.left, 6512 width: from.width * to.scaleX, 6513 height: from.height * to.scaleY, 6514 scaleX: 1, 6515 scaleY: 1 6516 }); 6517 } 6518 } else if (leaveAnimationName !== true) { 6519 $el.removeClass(to); 6520 } 6521 6522 if ($.isFunction(callback)) { 6523 callback(e); 6524 } 6525 }); 6526 6527 if ($.isNumeric(duration)) { 6528 $el.css("transition-duration", duration + "ms"); 6529 } 6530 6531 // Start animation by changing CSS properties or class name 6532 if ($.isPlainObject(to)) { 6533 if (to.scaleX !== undefined && to.scaleY !== undefined) { 6534 delete to.width; 6535 delete to.height; 6536 6537 if ($el.parent().hasClass("fancybox-slide--image")) { 6538 $el.parent().addClass("fancybox-is-scaling"); 6539 } 6540 } 6541 6542 $.fancybox.setTranslate($el, to); 6543 } else { 6544 $el.addClass(to); 6545 } 6546 6547 // Make sure that `transitionend` callback gets fired 6548 $el.data( 6549 "timer", 6550 setTimeout(function () { 6551 $el.trigger(transitionEnd); 6552 }, duration + 33) 6553 ); 6554 }, 6555 6556 stop: function ($el, callCallback) { 6557 if ($el && $el.length) { 6558 clearTimeout($el.data("timer")); 6559 6560 if (callCallback) { 6561 $el.trigger(transitionEnd); 6562 } 6563 6564 $el.off(transitionEnd).css("transition-duration", ""); 6565 6566 $el.parent().removeClass("fancybox-is-scaling"); 6567 } 6568 } 6569 }; 6570 6571 // Default click handler for "fancyboxed" links 6572 // ============================================ 6573 6574 function _run(e, opts) { 6575 var items = [], 6576 index = 0, 6577 $target, 6578 value, 6579 instance; 6580 6581 // Avoid opening multiple times 6582 if (e && e.isDefaultPrevented()) { 6583 return; 6584 } 6585 6586 e.preventDefault(); 6587 6588 opts = opts || {}; 6589 6590 if (e && e.data) { 6591 opts = mergeOpts(e.data.options, opts); 6592 } 6593 6594 $target = opts.$target || $(e.currentTarget).trigger("blur"); 6595 instance = $.fancybox.getInstance(); 6596 6597 if (instance && instance.$trigger && instance.$trigger.is($target)) { 6598 return; 6599 } 6600 6601 if (opts.selector) { 6602 items = $(opts.selector); 6603 } else { 6604 // Get all related items and find index for clicked one 6605 value = $target.attr("data-fancybox") || ""; 6606 6607 if (value) { 6608 items = e.data ? e.data.items : []; 6609 items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox="' + value + '"]'); 6610 } else { 6611 items = [$target]; 6612 } 6613 } 6614 6615 index = $(items).index($target); 6616 6617 // Sometimes current item can not be found 6618 if (index < 0) { 6619 index = 0; 6620 } 6621 6622 instance = $.fancybox.open(items, opts, index); 6623 6624 // Save last active element 6625 instance.$trigger = $target; 6626 } 6627 6628 // Create a jQuery plugin 6629 // ====================== 6630 6631 $.fn.fancybox = function (options) { 6632 var selector; 6633 6634 options = options || {}; 6635 selector = options.selector || false; 6636 6637 if (selector) { 6638 // Use body element instead of document so it executes first 6639 $("body") 6640 .off("click.fb-start", selector) 6641 .on("click.fb-start", selector, { 6642 options: options 6643 }, _run); 6644 } else { 6645 this.off("click.fb-start").on( 6646 "click.fb-start", { 6647 items: this, 6648 options: options 6649 }, 6650 _run 6651 ); 6652 } 6653 6654 return this; 6655 }; 6656 6657 // Self initializing plugin for all elements having `data-fancybox` attribute 6658 // ========================================================================== 6659 6660 $D.on("click.fb-start", "[data-fancybox]", _run); 6661 6662 // Enable "trigger elements" 6663 // ========================= 6664 6665 $D.on("click.fb-start", "[data-fancybox-trigger]", function (e) { 6666 $('[data-fancybox="' + $(this).attr("data-fancybox-trigger") + '"]') 6667 .eq($(this).attr("data-fancybox-index") || 0) 6668 .trigger("click.fb-start", { 6669 $trigger: $(this) 6670 }); 6671 }); 6672 6673 // Track focus event for better accessibility styling 6674 // ================================================== 6675 (function () { 6676 var buttonStr = ".fancybox-button", 6677 focusStr = "fancybox-focus", 6678 $pressed = null; 6679 6680 $D.on("mousedown mouseup focus blur", buttonStr, function (e) { 6681 switch (e.type) { 6682 case "mousedown": 6683 $pressed = $(this); 6684 break; 6685 case "mouseup": 6686 $pressed = null; 6687 break; 6688 case "focusin": 6689 $(buttonStr).removeClass(focusStr); 6690 6691 if (!$(this).is($pressed) && !$(this).is("[disabled]")) { 6692 $(this).addClass(focusStr); 6693 } 6694 break; 6695 case "focusout": 6696 $(buttonStr).removeClass(focusStr); 6697 break; 6698 } 6699 }); 6700 })(); 6701 })(window, document, jQuery); 6702 // ========================================================================== 6703 // 6704 // Media 6705 // Adds additional media type support 6706 // 6707 // ========================================================================== 6708 (function ($) { 6709 "use strict"; 6710 6711 // Object containing properties for each media type 6712 var defaults = { 6713 youtube: { 6714 matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 6715 params: { 6716 autoplay: 1, 6717 autohide: 1, 6718 fs: 1, 6719 rel: 0, 6720 hd: 1, 6721 wmode: "transparent", 6722 enablejsapi: 1, 6723 html5: 1 6724 }, 6725 paramPlace: 8, 6726 type: "iframe", 6727 url: "https://www.youtube-nocookie.com/embed/$4", 6728 thumb: "https://img.youtube.com/vi/$4/hqdefault.jpg" 6729 }, 6730 6731 vimeo: { 6732 matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 6733 params: { 6734 autoplay: 1, 6735 hd: 1, 6736 show_title: 1, 6737 show_byline: 1, 6738 show_portrait: 0, 6739 fullscreen: 1 6740 }, 6741 paramPlace: 3, 6742 type: "iframe", 6743 url: "//player.vimeo.com/video/$2" 6744 }, 6745 6746 instagram: { 6747 matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 6748 type: "image", 6749 url: "//$1/p/$2/media/?size=l" 6750 }, 6751 6752 // Examples: 6753 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 6754 // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z 6755 // https://www.google.com/maps/@52.2111123,2.9237542,6.61z?hl=en 6756 // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 6757 gmap_place: { 6758 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 6759 type: "iframe", 6760 url: function (rez) { 6761 return ( 6762 "//maps.google." + 6763 rez[2] + 6764 "/?ll=" + 6765 (rez[9] ? rez[9] + "&z=" + Math.floor(rez[10]) + (rez[12] ? rez[12].replace(/^\//, "&") : "") : rez[12] + "").replace(/\?/, "&") + 6766 "&output=" + 6767 (rez[12] && rez[12].indexOf("layer=c") > 0 ? "svembed" : "embed") 6768 ); 6769 } 6770 }, 6771 6772 // Examples: 6773 // https://www.google.com/maps/search/Empire+State+Building/ 6774 // https://www.google.com/maps/search/?api=1&query=centurylink+field 6775 // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 6776 gmap_search: { 6777 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, 6778 type: "iframe", 6779 url: function (rez) { 6780 return "//maps.google." + rez[2] + "/maps?q=" + rez[5].replace("query=", "q=").replace("api=1", "") + "&output=embed"; 6781 } 6782 } 6783 }; 6784 6785 // Formats matching url to final form 6786 var format = function (url, rez, params) { 6787 if (!url) { 6788 return; 6789 } 6790 6791 params = params || ""; 6792 6793 if ($.type(params) === "object") { 6794 params = $.param(params, true); 6795 } 6796 6797 $.each(rez, function (key, value) { 6798 url = url.replace("$" + key, value || ""); 6799 }); 6800 6801 if (params.length) { 6802 url += (url.indexOf("?") > 0 ? "&" : "?") + params; 6803 } 6804 6805 return url; 6806 }; 6807 6808 $(document).on("objectNeedsType.fb", function (e, instance, item) { 6809 var url = item.src || "", 6810 type = false, 6811 media, 6812 thumb, 6813 rez, 6814 params, 6815 urlParams, 6816 paramObj, 6817 provider; 6818 6819 media = $.extend(true, {}, defaults, item.opts.media); 6820 6821 // Look for any matching media type 6822 $.each(media, function (providerName, providerOpts) { 6823 rez = url.match(providerOpts.matcher); 6824 6825 if (!rez) { 6826 return; 6827 } 6828 6829 type = providerOpts.type; 6830 provider = providerName; 6831 paramObj = {}; 6832 6833 if (providerOpts.paramPlace && rez[providerOpts.paramPlace]) { 6834 urlParams = rez[providerOpts.paramPlace]; 6835 6836 if (urlParams[0] == "?") { 6837 urlParams = urlParams.substring(1); 6838 } 6839 6840 urlParams = urlParams.split("&"); 6841 6842 for (var m = 0; m < urlParams.length; ++m) { 6843 var p = urlParams[m].split("=", 2); 6844 6845 if (p.length == 2) { 6846 paramObj[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " ")); 6847 } 6848 } 6849 } 6850 6851 params = $.extend(true, {}, providerOpts.params, item.opts[providerName], paramObj); 6852 6853 url = 6854 $.type(providerOpts.url) === "function" ? providerOpts.url.call(this, rez, params, item) : format(providerOpts.url, rez, params); 6855 6856 thumb = 6857 $.type(providerOpts.thumb) === "function" ? providerOpts.thumb.call(this, rez, params, item) : format(providerOpts.thumb, rez); 6858 6859 if (providerName === "youtube") { 6860 url = url.replace(/&t=((\d+)m)?(\d+)s/, function (match, p1, m, s) { 6861 return "&start=" + ((m ? parseInt(m, 10) * 60 : 0) + parseInt(s, 10)); 6862 }); 6863 } else if (providerName === "vimeo") { 6864 url = url.replace("&%23", "#"); 6865 } 6866 6867 return false; 6868 }); 6869 6870 // If it is found, then change content type and update the url 6871 6872 if (type) { 6873 if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) { 6874 item.opts.thumb = thumb; 6875 } 6876 6877 if (type === "iframe") { 6878 item.opts = $.extend(true, item.opts, { 6879 iframe: { 6880 preload: false, 6881 attr: { 6882 scrolling: "no" 6883 } 6884 } 6885 }); 6886 } 6887 6888 $.extend(item, { 6889 type: type, 6890 src: url, 6891 origSrc: item.src, 6892 contentSource: provider, 6893 contentType: type === "image" ? "image" : provider == "gmap_place" || provider == "gmap_search" ? "map" : "video" 6894 }); 6895 } else if (url) { 6896 item.type = item.opts.defaultType; 6897 } 6898 }); 6899 6900 // Load YouTube/Video API on request to detect when video finished playing 6901 var VideoAPILoader = { 6902 youtube: { 6903 src: "https://www.youtube.com/iframe_api", 6904 class: "YT", 6905 loading: false, 6906 loaded: false 6907 }, 6908 6909 vimeo: { 6910 src: "https://player.vimeo.com/api/player.js", 6911 class: "Vimeo", 6912 loading: false, 6913 loaded: false 6914 }, 6915 6916 load: function (vendor) { 6917 var _this = this, 6918 script; 6919 6920 if (this[vendor].loaded) { 6921 setTimeout(function () { 6922 _this.done(vendor); 6923 }); 6924 return; 6925 } 6926 6927 if (this[vendor].loading) { 6928 return; 6929 } 6930 6931 this[vendor].loading = true; 6932 6933 script = document.createElement("script"); 6934 script.type = "text/javascript"; 6935 script.src = this[vendor].src; 6936 6937 if (vendor === "youtube") { 6938 window.onYouTubeIframeAPIReady = function () { 6939 _this[vendor].loaded = true; 6940 _this.done(vendor); 6941 }; 6942 } else { 6943 script.onload = function () { 6944 _this[vendor].loaded = true; 6945 _this.done(vendor); 6946 }; 6947 } 6948 6949 document.body.appendChild(script); 6950 }, 6951 done: function (vendor) { 6952 var instance, $el, player; 6953 6954 if (vendor === "youtube") { 6955 delete window.onYouTubeIframeAPIReady; 6956 } 6957 6958 instance = $.fancybox.getInstance(); 6959 6960 if (instance) { 6961 $el = instance.current.$content.find("iframe"); 6962 6963 if (vendor === "youtube" && YT !== undefined && YT) { 6964 player = new YT.Player($el.attr("id"), { 6965 events: { 6966 onStateChange: function (e) { 6967 if (e.data == 0) { 6968 instance.next(); 6969 } 6970 } 6971 } 6972 }); 6973 } else if (vendor === "vimeo" && Vimeo !== undefined && Vimeo) { 6974 player = new Vimeo.Player($el); 6975 6976 player.on("ended", function () { 6977 instance.next(); 6978 }); 6979 } 6980 } 6981 } 6982 }; 6983 6984 $(document).on({ 6985 "afterShow.fb": function (e, instance, current) { 6986 if (instance.group.length > 1 && (current.contentSource === "youtube" || current.contentSource === "vimeo")) { 6987 VideoAPILoader.load(current.contentSource); 6988 } 6989 } 6990 }); 6991 })(jQuery); 6992 // ========================================================================== 6993 // 6994 // Guestures 6995 // Adds touch guestures, handles click and tap events 6996 // 6997 // ========================================================================== 6998 (function (window, document, $) { 6999 "use strict"; 7000 7001 var requestAFrame = (function () { 7002 return ( 7003 window.requestAnimationFrame || 7004 window.webkitRequestAnimationFrame || 7005 window.mozRequestAnimationFrame || 7006 window.oRequestAnimationFrame || 7007 // if all else fails, use setTimeout 7008 function (callback) { 7009 return window.setTimeout(callback, 1000 / 60); 7010 } 7011 ); 7012 })(); 7013 7014 var cancelAFrame = (function () { 7015 return ( 7016 window.cancelAnimationFrame || 7017 window.webkitCancelAnimationFrame || 7018 window.mozCancelAnimationFrame || 7019 window.oCancelAnimationFrame || 7020 function (id) { 7021 window.clearTimeout(id); 7022 } 7023 ); 7024 })(); 7025 7026 var getPointerXY = function (e) { 7027 var result = []; 7028 7029 e = e.originalEvent || e || window.e; 7030 e = e.touches && e.touches.length ? e.touches : e.changedTouches && e.changedTouches.length ? e.changedTouches : [e]; 7031 7032 for (var key in e) { 7033 if (e[key].pageX) { 7034 result.push({ 7035 x: e[key].pageX, 7036 y: e[key].pageY 7037 }); 7038 } else if (e[key].clientX) { 7039 result.push({ 7040 x: e[key].clientX, 7041 y: e[key].clientY 7042 }); 7043 } 7044 } 7045 7046 return result; 7047 }; 7048 7049 var distance = function (point2, point1, what) { 7050 if (!point1 || !point2) { 7051 return 0; 7052 } 7053 7054 if (what === "x") { 7055 return point2.x - point1.x; 7056 } else if (what === "y") { 7057 return point2.y - point1.y; 7058 } 7059 7060 return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2)); 7061 }; 7062 7063 var isClickable = function ($el) { 7064 if ( 7065 $el.is('a,area,button,[role="button"],input,label,select,summary,textarea,video,audio,iframe') || 7066 $.isFunction($el.get(0).onclick) || 7067 $el.data("selectable") 7068 ) { 7069 return true; 7070 } 7071 7072 // Check for attributes like data-fancybox-next or data-fancybox-close 7073 for (var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++) { 7074 if (atts[i].nodeName.substr(0, 14) === "data-fancybox-") { 7075 return true; 7076 } 7077 } 7078 7079 return false; 7080 }; 7081 7082 var hasScrollbars = function (el) { 7083 var overflowY = window.getComputedStyle(el)["overflow-y"], 7084 overflowX = window.getComputedStyle(el)["overflow-x"], 7085 vertical = (overflowY === "scroll" || overflowY === "auto") && el.scrollHeight > el.clientHeight, 7086 horizontal = (overflowX === "scroll" || overflowX === "auto") && el.scrollWidth > el.clientWidth; 7087 7088 return vertical || horizontal; 7089 }; 7090 7091 var isScrollable = function ($el) { 7092 var rez = false; 7093 7094 while (true) { 7095 rez = hasScrollbars($el.get(0)); 7096 7097 if (rez) { 7098 break; 7099 } 7100 7101 $el = $el.parent(); 7102 7103 if (!$el.length || $el.hasClass("fancybox-stage") || $el.is("body")) { 7104 break; 7105 } 7106 } 7107 7108 return rez; 7109 }; 7110 7111 var Guestures = function (instance) { 7112 var self = this; 7113 7114 self.instance = instance; 7115 7116 self.$bg = instance.$refs.bg; 7117 self.$stage = instance.$refs.stage; 7118 self.$container = instance.$refs.container; 7119 7120 self.destroy(); 7121 7122 self.$container.on("touchstart.fb.touch mousedown.fb.touch", $.proxy(self, "ontouchstart")); 7123 }; 7124 7125 Guestures.prototype.destroy = function () { 7126 var self = this; 7127 7128 self.$container.off(".fb.touch"); 7129 7130 $(document).off(".fb.touch"); 7131 7132 if (self.requestId) { 7133 cancelAFrame(self.requestId); 7134 self.requestId = null; 7135 } 7136 7137 if (self.tapped) { 7138 clearTimeout(self.tapped); 7139 self.tapped = null; 7140 } 7141 }; 7142 7143 Guestures.prototype.ontouchstart = function (e) { 7144 var self = this, 7145 $target = $(e.target), 7146 instance = self.instance, 7147 current = instance.current, 7148 $slide = current.$slide, 7149 $content = current.$content, 7150 isTouchDevice = e.type == "touchstart"; 7151 7152 // Do not respond to both (touch and mouse) events 7153 if (isTouchDevice) { 7154 self.$container.off("mousedown.fb.touch"); 7155 } 7156 7157 // Ignore right click 7158 if (e.originalEvent && e.originalEvent.button == 2) { 7159 return; 7160 } 7161 7162 // Ignore taping on links, buttons, input elements 7163 if (!$slide.length || !$target.length || isClickable($target) || isClickable($target.parent())) { 7164 return; 7165 } 7166 // Ignore clicks on the scrollbar 7167 if (!$target.is("img") && e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left) { 7168 return; 7169 } 7170 7171 // Ignore clicks while zooming or closing 7172 if (!current || instance.isAnimating || current.$slide.hasClass("fancybox-animated")) { 7173 e.stopPropagation(); 7174 e.preventDefault(); 7175 7176 return; 7177 } 7178 7179 self.realPoints = self.startPoints = getPointerXY(e); 7180 7181 if (!self.startPoints.length) { 7182 return; 7183 } 7184 7185 // Allow other scripts to catch touch event if "touch" is set to false 7186 if (current.touch) { 7187 e.stopPropagation(); 7188 } 7189 7190 self.startEvent = e; 7191 7192 self.canTap = true; 7193 self.$target = $target; 7194 self.$content = $content; 7195 self.opts = current.opts.touch; 7196 7197 self.isPanning = false; 7198 self.isSwiping = false; 7199 self.isZooming = false; 7200 self.isScrolling = false; 7201 self.canPan = instance.canPan(); 7202 7203 self.startTime = new Date().getTime(); 7204 self.distanceX = self.distanceY = self.distance = 0; 7205 7206 self.canvasWidth = Math.round($slide[0].clientWidth); 7207 self.canvasHeight = Math.round($slide[0].clientHeight); 7208 7209 self.contentLastPos = null; 7210 self.contentStartPos = $.fancybox.getTranslate(self.$content) || { 7211 top: 0, 7212 left: 0 7213 }; 7214 self.sliderStartPos = $.fancybox.getTranslate($slide); 7215 7216 // Since position will be absolute, but we need to make it relative to the stage 7217 self.stagePos = $.fancybox.getTranslate(instance.$refs.stage); 7218 7219 self.sliderStartPos.top -= self.stagePos.top; 7220 self.sliderStartPos.left -= self.stagePos.left; 7221 7222 self.contentStartPos.top -= self.stagePos.top; 7223 self.contentStartPos.left -= self.stagePos.left; 7224 7225 $(document) 7226 .off(".fb.touch") 7227 .on(isTouchDevice ? "touchend.fb.touch touchcancel.fb.touch" : "mouseup.fb.touch mouseleave.fb.touch", $.proxy(self, "ontouchend")) 7228 .on(isTouchDevice ? "touchmove.fb.touch" : "mousemove.fb.touch", $.proxy(self, "ontouchmove")); 7229 7230 if ($.fancybox.isMobile) { 7231 document.addEventListener("scroll", self.onscroll, true); 7232 } 7233 7234 // Skip if clicked outside the sliding area 7235 if (!(self.opts || self.canPan) || !($target.is(self.$stage) || self.$stage.find($target).length)) { 7236 if ($target.is(".fancybox-image")) { 7237 e.preventDefault(); 7238 } 7239 7240 if (!($.fancybox.isMobile && $target.parents(".fancybox-caption").length)) { 7241 return; 7242 } 7243 } 7244 7245 self.isScrollable = isScrollable($target) || isScrollable($target.parent()); 7246 7247 // Check if element is scrollable and try to prevent default behavior (scrolling) 7248 if (!($.fancybox.isMobile && self.isScrollable)) { 7249 e.preventDefault(); 7250 } 7251 7252 // One finger or mouse click - swipe or pan an image 7253 if (self.startPoints.length === 1 || current.hasError) { 7254 if (self.canPan) { 7255 $.fancybox.stop(self.$content); 7256 7257 self.isPanning = true; 7258 } else { 7259 self.isSwiping = true; 7260 } 7261 7262 self.$container.addClass("fancybox-is-grabbing"); 7263 } 7264 7265 // Two fingers - zoom image 7266 if (self.startPoints.length === 2 && current.type === "image" && (current.isLoaded || current.$ghost)) { 7267 self.canTap = false; 7268 self.isSwiping = false; 7269 self.isPanning = false; 7270 7271 self.isZooming = true; 7272 7273 $.fancybox.stop(self.$content); 7274 7275 self.centerPointStartX = (self.startPoints[0].x + self.startPoints[1].x) * 0.5 - $(window).scrollLeft(); 7276 self.centerPointStartY = (self.startPoints[0].y + self.startPoints[1].y) * 0.5 - $(window).scrollTop(); 7277 7278 self.percentageOfImageAtPinchPointX = (self.centerPointStartX - self.contentStartPos.left) / self.contentStartPos.width; 7279 self.percentageOfImageAtPinchPointY = (self.centerPointStartY - self.contentStartPos.top) / self.contentStartPos.height; 7280 7281 self.startDistanceBetweenFingers = distance(self.startPoints[0], self.startPoints[1]); 7282 } 7283 }; 7284 7285 Guestures.prototype.onscroll = function (e) { 7286 var self = this; 7287 7288 self.isScrolling = true; 7289 7290 document.removeEventListener("scroll", self.onscroll, true); 7291 }; 7292 7293 Guestures.prototype.ontouchmove = function (e) { 7294 var self = this; 7295 7296 // Make sure user has not released over iframe or disabled element 7297 if (e.originalEvent.buttons !== undefined && e.originalEvent.buttons === 0) { 7298 self.ontouchend(e); 7299 return; 7300 } 7301 7302 if (self.isScrolling) { 7303 self.canTap = false; 7304 return; 7305 } 7306 7307 self.newPoints = getPointerXY(e); 7308 7309 if (!(self.opts || self.canPan) || !self.newPoints.length || !self.newPoints.length) { 7310 return; 7311 } 7312 7313 if (!(self.isSwiping && self.isSwiping === true)) { 7314 e.preventDefault(); 7315 } 7316 7317 self.distanceX = distance(self.newPoints[0], self.startPoints[0], "x"); 7318 self.distanceY = distance(self.newPoints[0], self.startPoints[0], "y"); 7319 7320 self.distance = distance(self.newPoints[0], self.startPoints[0]); 7321 7322 // Skip false ontouchmove events (Chrome) 7323 if (self.distance > 0) { 7324 if (self.isSwiping) { 7325 self.onSwipe(e); 7326 } else if (self.isPanning) { 7327 self.onPan(); 7328 } else if (self.isZooming) { 7329 self.onZoom(); 7330 } 7331 } 7332 }; 7333 7334 Guestures.prototype.onSwipe = function (e) { 7335 var self = this, 7336 instance = self.instance, 7337 swiping = self.isSwiping, 7338 left = self.sliderStartPos.left || 0, 7339 angle; 7340 7341 // If direction is not yet determined 7342 if (swiping === true) { 7343 // We need at least 10px distance to correctly calculate an angle 7344 if (Math.abs(self.distance) > 10) { 7345 self.canTap = false; 7346 7347 if (instance.group.length < 2 && self.opts.vertical) { 7348 self.isSwiping = "y"; 7349 } else if (instance.isDragging || self.opts.vertical === false || (self.opts.vertical === "auto" && $(window).width() > 800)) { 7350 self.isSwiping = "x"; 7351 } else { 7352 angle = Math.abs((Math.atan2(self.distanceY, self.distanceX) * 180) / Math.PI); 7353 7354 self.isSwiping = angle > 45 && angle < 135 ? "y" : "x"; 7355 } 7356 7357 if (self.isSwiping === "y" && $.fancybox.isMobile && self.isScrollable) { 7358 self.isScrolling = true; 7359 7360 return; 7361 } 7362 7363 instance.isDragging = self.isSwiping; 7364 7365 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 7366 self.startPoints = self.newPoints; 7367 7368 $.each(instance.slides, function (index, slide) { 7369 var slidePos, stagePos; 7370 7371 $.fancybox.stop(slide.$slide); 7372 7373 slidePos = $.fancybox.getTranslate(slide.$slide); 7374 stagePos = $.fancybox.getTranslate(instance.$refs.stage); 7375 7376 slide.$slide 7377 .css({ 7378 transform: "", 7379 opacity: "", 7380 "transition-duration": "" 7381 }) 7382 .removeClass("fancybox-animated") 7383 .removeClass(function (index, className) { 7384 return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" "); 7385 }); 7386 7387 if (slide.pos === instance.current.pos) { 7388 self.sliderStartPos.top = slidePos.top - stagePos.top; 7389 self.sliderStartPos.left = slidePos.left - stagePos.left; 7390 } 7391 7392 $.fancybox.setTranslate(slide.$slide, { 7393 top: slidePos.top - stagePos.top, 7394 left: slidePos.left - stagePos.left 7395 }); 7396 }); 7397 7398 // Stop slideshow 7399 if (instance.SlideShow && instance.SlideShow.isActive) { 7400 instance.SlideShow.stop(); 7401 } 7402 } 7403 7404 return; 7405 } 7406 7407 // Sticky edges 7408 if (swiping == "x") { 7409 if ( 7410 self.distanceX > 0 && 7411 (self.instance.group.length < 2 || (self.instance.current.index === 0 && !self.instance.current.opts.loop)) 7412 ) { 7413 left = left + Math.pow(self.distanceX, 0.8); 7414 } else if ( 7415 self.distanceX < 0 && 7416 (self.instance.group.length < 2 || 7417 (self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop)) 7418 ) { 7419 left = left - Math.pow(-self.distanceX, 0.8); 7420 } else { 7421 left = left + self.distanceX; 7422 } 7423 } 7424 7425 self.sliderLastPos = { 7426 top: swiping == "x" ? 0 : self.sliderStartPos.top + self.distanceY, 7427 left: left 7428 }; 7429 7430 if (self.requestId) { 7431 cancelAFrame(self.requestId); 7432 7433 self.requestId = null; 7434 } 7435 7436 self.requestId = requestAFrame(function () { 7437 if (self.sliderLastPos) { 7438 $.each(self.instance.slides, function (index, slide) { 7439 var pos = slide.pos - self.instance.currPos; 7440 7441 $.fancybox.setTranslate(slide.$slide, { 7442 top: self.sliderLastPos.top, 7443 left: self.sliderLastPos.left + pos * self.canvasWidth + pos * slide.opts.gutter 7444 }); 7445 }); 7446 7447 self.$container.addClass("fancybox-is-sliding"); 7448 } 7449 }); 7450 }; 7451 7452 Guestures.prototype.onPan = function () { 7453 var self = this; 7454 7455 // Prevent accidental movement (sometimes, when tapping casually, finger can move a bit) 7456 if (distance(self.newPoints[0], self.realPoints[0]) < ($.fancybox.isMobile ? 10 : 5)) { 7457 self.startPoints = self.newPoints; 7458 return; 7459 } 7460 7461 self.canTap = false; 7462 7463 self.contentLastPos = self.limitMovement(); 7464 7465 if (self.requestId) { 7466 cancelAFrame(self.requestId); 7467 } 7468 7469 self.requestId = requestAFrame(function () { 7470 $.fancybox.setTranslate(self.$content, self.contentLastPos); 7471 }); 7472 }; 7473 7474 // Make panning sticky to the edges 7475 Guestures.prototype.limitMovement = function () { 7476 var self = this; 7477 7478 var canvasWidth = self.canvasWidth; 7479 var canvasHeight = self.canvasHeight; 7480 7481 var distanceX = self.distanceX; 7482 var distanceY = self.distanceY; 7483 7484 var contentStartPos = self.contentStartPos; 7485 7486 var currentOffsetX = contentStartPos.left; 7487 var currentOffsetY = contentStartPos.top; 7488 7489 var currentWidth = contentStartPos.width; 7490 var currentHeight = contentStartPos.height; 7491 7492 var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY, newOffsetX, newOffsetY; 7493 7494 if (currentWidth > canvasWidth) { 7495 newOffsetX = currentOffsetX + distanceX; 7496 } else { 7497 newOffsetX = currentOffsetX; 7498 } 7499 7500 newOffsetY = currentOffsetY + distanceY; 7501 7502 // Slow down proportionally to traveled distance 7503 minTranslateX = Math.max(0, canvasWidth * 0.5 - currentWidth * 0.5); 7504 minTranslateY = Math.max(0, canvasHeight * 0.5 - currentHeight * 0.5); 7505 7506 maxTranslateX = Math.min(canvasWidth - currentWidth, canvasWidth * 0.5 - currentWidth * 0.5); 7507 maxTranslateY = Math.min(canvasHeight - currentHeight, canvasHeight * 0.5 - currentHeight * 0.5); 7508 7509 // -> 7510 if (distanceX > 0 && newOffsetX > minTranslateX) { 7511 newOffsetX = minTranslateX - 1 + Math.pow(-minTranslateX + currentOffsetX + distanceX, 0.8) || 0; 7512 } 7513 7514 // <- 7515 if (distanceX < 0 && newOffsetX < maxTranslateX) { 7516 newOffsetX = maxTranslateX + 1 - Math.pow(maxTranslateX - currentOffsetX - distanceX, 0.8) || 0; 7517 } 7518 7519 // \/ 7520 if (distanceY > 0 && newOffsetY > minTranslateY) { 7521 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8) || 0; 7522 } 7523 7524 // /\ 7525 if (distanceY < 0 && newOffsetY < maxTranslateY) { 7526 newOffsetY = maxTranslateY + 1 - Math.pow(maxTranslateY - currentOffsetY - distanceY, 0.8) || 0; 7527 } 7528 7529 return { 7530 top: newOffsetY, 7531 left: newOffsetX 7532 }; 7533 }; 7534 7535 Guestures.prototype.limitPosition = function (newOffsetX, newOffsetY, newWidth, newHeight) { 7536 var self = this; 7537 7538 var canvasWidth = self.canvasWidth; 7539 var canvasHeight = self.canvasHeight; 7540 7541 if (newWidth > canvasWidth) { 7542 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 7543 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 7544 } else { 7545 // Center horizontally 7546 newOffsetX = Math.max(0, canvasWidth / 2 - newWidth / 2); 7547 } 7548 7549 if (newHeight > canvasHeight) { 7550 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 7551 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 7552 } else { 7553 // Center vertically 7554 newOffsetY = Math.max(0, canvasHeight / 2 - newHeight / 2); 7555 } 7556 7557 return { 7558 top: newOffsetY, 7559 left: newOffsetX 7560 }; 7561 }; 7562 7563 Guestures.prototype.onZoom = function () { 7564 var self = this; 7565 7566 // Calculate current distance between points to get pinch ratio and new width and height 7567 var contentStartPos = self.contentStartPos; 7568 7569 var currentWidth = contentStartPos.width; 7570 var currentHeight = contentStartPos.height; 7571 7572 var currentOffsetX = contentStartPos.left; 7573 var currentOffsetY = contentStartPos.top; 7574 7575 var endDistanceBetweenFingers = distance(self.newPoints[0], self.newPoints[1]); 7576 7577 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 7578 7579 var newWidth = Math.floor(currentWidth * pinchRatio); 7580 var newHeight = Math.floor(currentHeight * pinchRatio); 7581 7582 // This is the translation due to pinch-zooming 7583 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 7584 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 7585 7586 // Point between the two touches 7587 var centerPointEndX = (self.newPoints[0].x + self.newPoints[1].x) / 2 - $(window).scrollLeft(); 7588 var centerPointEndY = (self.newPoints[0].y + self.newPoints[1].y) / 2 - $(window).scrollTop(); 7589 7590 // And this is the translation due to translation of the centerpoint 7591 // between the two fingers 7592 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 7593 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 7594 7595 // The new offset is the old/current one plus the total translation 7596 var newOffsetX = currentOffsetX + (translateFromZoomingX + translateFromTranslatingX); 7597 var newOffsetY = currentOffsetY + (translateFromZoomingY + translateFromTranslatingY); 7598 7599 var newPos = { 7600 top: newOffsetY, 7601 left: newOffsetX, 7602 scaleX: pinchRatio, 7603 scaleY: pinchRatio 7604 }; 7605 7606 self.canTap = false; 7607 7608 self.newWidth = newWidth; 7609 self.newHeight = newHeight; 7610 7611 self.contentLastPos = newPos; 7612 7613 if (self.requestId) { 7614 cancelAFrame(self.requestId); 7615 } 7616 7617 self.requestId = requestAFrame(function () { 7618 $.fancybox.setTranslate(self.$content, self.contentLastPos); 7619 }); 7620 }; 7621 7622 Guestures.prototype.ontouchend = function (e) { 7623 var self = this; 7624 7625 var swiping = self.isSwiping; 7626 var panning = self.isPanning; 7627 var zooming = self.isZooming; 7628 var scrolling = self.isScrolling; 7629 7630 self.endPoints = getPointerXY(e); 7631 self.dMs = Math.max(new Date().getTime() - self.startTime, 1); 7632 7633 self.$container.removeClass("fancybox-is-grabbing"); 7634 7635 $(document).off(".fb.touch"); 7636 7637 document.removeEventListener("scroll", self.onscroll, true); 7638 7639 if (self.requestId) { 7640 cancelAFrame(self.requestId); 7641 7642 self.requestId = null; 7643 } 7644 7645 self.isSwiping = false; 7646 self.isPanning = false; 7647 self.isZooming = false; 7648 self.isScrolling = false; 7649 7650 self.instance.isDragging = false; 7651 7652 if (self.canTap) { 7653 return self.onTap(e); 7654 } 7655 7656 self.speed = 100; 7657 7658 // Speed in px/ms 7659 self.velocityX = (self.distanceX / self.dMs) * 0.5; 7660 self.velocityY = (self.distanceY / self.dMs) * 0.5; 7661 7662 if (panning) { 7663 self.endPanning(); 7664 } else if (zooming) { 7665 self.endZooming(); 7666 } else { 7667 self.endSwiping(swiping, scrolling); 7668 } 7669 7670 return; 7671 }; 7672 7673 Guestures.prototype.endSwiping = function (swiping, scrolling) { 7674 var self = this, 7675 ret = false, 7676 len = self.instance.group.length, 7677 distanceX = Math.abs(self.distanceX), 7678 canAdvance = swiping == "x" && len > 1 && ((self.dMs > 130 && distanceX > 10) || distanceX > 50), 7679 speedX = 300; 7680 7681 self.sliderLastPos = null; 7682 7683 // Close if swiped vertically / navigate if horizontally 7684 if (swiping == "y" && !scrolling && Math.abs(self.distanceY) > 50) { 7685 // Continue vertical movement 7686 $.fancybox.animate( 7687 self.instance.current.$slide, { 7688 top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150, 7689 opacity: 0 7690 }, 7691 200 7692 ); 7693 ret = self.instance.close(true, 250); 7694 } else if (canAdvance && self.distanceX > 0) { 7695 ret = self.instance.previous(speedX); 7696 } else if (canAdvance && self.distanceX < 0) { 7697 ret = self.instance.next(speedX); 7698 } 7699 7700 if (ret === false && (swiping == "x" || swiping == "y")) { 7701 self.instance.centerSlide(200); 7702 } 7703 7704 self.$container.removeClass("fancybox-is-sliding"); 7705 }; 7706 7707 // Limit panning from edges 7708 // ======================== 7709 Guestures.prototype.endPanning = function () { 7710 var self = this, 7711 newOffsetX, 7712 newOffsetY, 7713 newPos; 7714 7715 if (!self.contentLastPos) { 7716 return; 7717 } 7718 7719 if (self.opts.momentum === false || self.dMs > 350) { 7720 newOffsetX = self.contentLastPos.left; 7721 newOffsetY = self.contentLastPos.top; 7722 } else { 7723 // Continue movement 7724 newOffsetX = self.contentLastPos.left + self.velocityX * 500; 7725 newOffsetY = self.contentLastPos.top + self.velocityY * 500; 7726 } 7727 7728 newPos = self.limitPosition(newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height); 7729 7730 newPos.width = self.contentStartPos.width; 7731 newPos.height = self.contentStartPos.height; 7732 7733 $.fancybox.animate(self.$content, newPos, 366); 7734 }; 7735 7736 Guestures.prototype.endZooming = function () { 7737 var self = this; 7738 7739 var current = self.instance.current; 7740 7741 var newOffsetX, newOffsetY, newPos, reset; 7742 7743 var newWidth = self.newWidth; 7744 var newHeight = self.newHeight; 7745 7746 if (!self.contentLastPos) { 7747 return; 7748 } 7749 7750 newOffsetX = self.contentLastPos.left; 7751 newOffsetY = self.contentLastPos.top; 7752 7753 reset = { 7754 top: newOffsetY, 7755 left: newOffsetX, 7756 width: newWidth, 7757 height: newHeight, 7758 scaleX: 1, 7759 scaleY: 1 7760 }; 7761 7762 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 7763 $.fancybox.setTranslate(self.$content, reset); 7764 7765 if (newWidth < self.canvasWidth && newHeight < self.canvasHeight) { 7766 self.instance.scaleToFit(150); 7767 } else if (newWidth > current.width || newHeight > current.height) { 7768 self.instance.scaleToActual(self.centerPointStartX, self.centerPointStartY, 150); 7769 } else { 7770 newPos = self.limitPosition(newOffsetX, newOffsetY, newWidth, newHeight); 7771 7772 $.fancybox.animate(self.$content, newPos, 150); 7773 } 7774 }; 7775 7776 Guestures.prototype.onTap = function (e) { 7777 var self = this; 7778 var $target = $(e.target); 7779 7780 var instance = self.instance; 7781 var current = instance.current; 7782 7783 var endPoints = (e && getPointerXY(e)) || self.startPoints; 7784 7785 var tapX = endPoints[0] ? endPoints[0].x - $(window).scrollLeft() - self.stagePos.left : 0; 7786 var tapY = endPoints[0] ? endPoints[0].y - $(window).scrollTop() - self.stagePos.top : 0; 7787 7788 var where; 7789 7790 var process = function (prefix) { 7791 var action = current.opts[prefix]; 7792 7793 if ($.isFunction(action)) { 7794 action = action.apply(instance, [current, e]); 7795 } 7796 7797 if (!action) { 7798 return; 7799 } 7800 7801 switch (action) { 7802 case "close": 7803 instance.close(self.startEvent); 7804 7805 break; 7806 7807 case "toggleControls": 7808 instance.toggleControls(); 7809 7810 break; 7811 7812 case "next": 7813 instance.next(); 7814 7815 break; 7816 7817 case "nextOrClose": 7818 if (instance.group.length > 1) { 7819 instance.next(); 7820 } else { 7821 instance.close(self.startEvent); 7822 } 7823 7824 break; 7825 7826 case "zoom": 7827 if (current.type == "image" && (current.isLoaded || current.$ghost)) { 7828 if (instance.canPan()) { 7829 instance.scaleToFit(); 7830 } else if (instance.isScaledDown()) { 7831 instance.scaleToActual(tapX, tapY); 7832 } else if (instance.group.length < 2) { 7833 instance.close(self.startEvent); 7834 } 7835 } 7836 7837 break; 7838 } 7839 }; 7840 7841 // Ignore right click 7842 if (e.originalEvent && e.originalEvent.button == 2) { 7843 return; 7844 } 7845 7846 // Skip if clicked on the scrollbar 7847 if (!$target.is("img") && tapX > $target[0].clientWidth + $target.offset().left) { 7848 return; 7849 } 7850 7851 // Check where is clicked 7852 if ($target.is(".fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container")) { 7853 where = "Outside"; 7854 } else if ($target.is(".fancybox-slide")) { 7855 where = "Slide"; 7856 } else if ( 7857 instance.current.$content && 7858 instance.current.$content 7859 .find($target) 7860 .addBack() 7861 .filter($target).length 7862 ) { 7863 where = "Content"; 7864 } else { 7865 return; 7866 } 7867 7868 // Check if this is a double tap 7869 if (self.tapped) { 7870 // Stop previously created single tap 7871 clearTimeout(self.tapped); 7872 self.tapped = null; 7873 7874 // Skip if distance between taps is too big 7875 if (Math.abs(tapX - self.tapX) > 50 || Math.abs(tapY - self.tapY) > 50) { 7876 return this; 7877 } 7878 7879 // OK, now we assume that this is a double-tap 7880 process("dblclick" + where); 7881 } else { 7882 // Single tap will be processed if user has not clicked second time within 300ms 7883 // or there is no need to wait for double-tap 7884 self.tapX = tapX; 7885 self.tapY = tapY; 7886 7887 if (current.opts["dblclick" + where] && current.opts["dblclick" + where] !== current.opts["click" + where]) { 7888 self.tapped = setTimeout(function () { 7889 self.tapped = null; 7890 7891 if (!instance.isAnimating) { 7892 process("click" + where); 7893 } 7894 }, 500); 7895 } else { 7896 process("click" + where); 7897 } 7898 } 7899 7900 return this; 7901 }; 7902 7903 $(document) 7904 .on("onActivate.fb", function (e, instance) { 7905 if (instance && !instance.Guestures) { 7906 instance.Guestures = new Guestures(instance); 7907 } 7908 }) 7909 .on("beforeClose.fb", function (e, instance) { 7910 if (instance && instance.Guestures) { 7911 instance.Guestures.destroy(); 7912 } 7913 }); 7914 })(window, document, jQuery); 7915 // ========================================================================== 7916 // 7917 // SlideShow 7918 // Enables slideshow functionality 7919 // 7920 // Example of usage: 7921 // $.fancybox.getInstance().SlideShow.start() 7922 // 7923 // ========================================================================== 7924 (function (document, $) { 7925 "use strict"; 7926 7927 $.extend(true, $.fancybox.defaults, { 7928 btnTpl: { 7929 slideShow: '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' + 7930 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 5.4v13.2l11-6.6z"/></svg>' + 7931 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8.33 5.75h2.2v12.5h-2.2V5.75zm5.15 0h2.2v12.5h-2.2V5.75z"/></svg>' + 7932 "</button>" 7933 }, 7934 slideShow: { 7935 autoStart: false, 7936 speed: 3000, 7937 progress: true 7938 } 7939 }); 7940 7941 var SlideShow = function (instance) { 7942 this.instance = instance; 7943 this.init(); 7944 }; 7945 7946 $.extend(SlideShow.prototype, { 7947 timer: null, 7948 isActive: false, 7949 $button: null, 7950 7951 init: function () { 7952 var self = this, 7953 instance = self.instance, 7954 opts = instance.group[instance.currIndex].opts.slideShow; 7955 7956 self.$button = instance.$refs.toolbar.find("[data-fancybox-play]").on("click", function () { 7957 self.toggle(); 7958 }); 7959 7960 if (instance.group.length < 2 || !opts) { 7961 self.$button.hide(); 7962 } else if (opts.progress) { 7963 self.$progress = $('<div class="fancybox-progress"></div>').appendTo(instance.$refs.inner); 7964 } 7965 }, 7966 7967 set: function (force) { 7968 var self = this, 7969 instance = self.instance, 7970 current = instance.current; 7971 7972 // Check if reached last element 7973 if (current && (force === true || current.opts.loop || instance.currIndex < instance.group.length - 1)) { 7974 if (self.isActive && current.contentType !== "video") { 7975 if (self.$progress) { 7976 $.fancybox.animate(self.$progress.show(), { 7977 scaleX: 1 7978 }, current.opts.slideShow.speed); 7979 } 7980 7981 self.timer = setTimeout(function () { 7982 if (!instance.current.opts.loop && instance.current.index == instance.group.length - 1) { 7983 instance.jumpTo(0); 7984 } else { 7985 instance.next(); 7986 } 7987 }, current.opts.slideShow.speed); 7988 } 7989 } else { 7990 self.stop(); 7991 instance.idleSecondsCounter = 0; 7992 instance.showControls(); 7993 } 7994 }, 7995 7996 clear: function () { 7997 var self = this; 7998 7999 clearTimeout(self.timer); 8000 8001 self.timer = null; 8002 8003 if (self.$progress) { 8004 self.$progress.removeAttr("style").hide(); 8005 } 8006 }, 8007 8008 start: function () { 8009 var self = this, 8010 current = self.instance.current; 8011 8012 if (current) { 8013 self.$button 8014 .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_STOP) 8015 .removeClass("fancybox-button--play") 8016 .addClass("fancybox-button--pause"); 8017 8018 self.isActive = true; 8019 8020 if (current.isComplete) { 8021 self.set(true); 8022 } 8023 8024 self.instance.trigger("onSlideShowChange", true); 8025 } 8026 }, 8027 8028 stop: function () { 8029 var self = this, 8030 current = self.instance.current; 8031 8032 self.clear(); 8033 8034 self.$button 8035 .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_START) 8036 .removeClass("fancybox-button--pause") 8037 .addClass("fancybox-button--play"); 8038 8039 self.isActive = false; 8040 8041 self.instance.trigger("onSlideShowChange", false); 8042 8043 if (self.$progress) { 8044 self.$progress.removeAttr("style").hide(); 8045 } 8046 }, 8047 8048 toggle: function () { 8049 var self = this; 8050 8051 if (self.isActive) { 8052 self.stop(); 8053 } else { 8054 self.start(); 8055 } 8056 } 8057 }); 8058 8059 $(document).on({ 8060 "onInit.fb": function (e, instance) { 8061 if (instance && !instance.SlideShow) { 8062 instance.SlideShow = new SlideShow(instance); 8063 } 8064 }, 8065 8066 "beforeShow.fb": function (e, instance, current, firstRun) { 8067 var SlideShow = instance && instance.SlideShow; 8068 8069 if (firstRun) { 8070 if (SlideShow && current.opts.slideShow.autoStart) { 8071 SlideShow.start(); 8072 } 8073 } else if (SlideShow && SlideShow.isActive) { 8074 SlideShow.clear(); 8075 } 8076 }, 8077 8078 "afterShow.fb": function (e, instance, current) { 8079 var SlideShow = instance && instance.SlideShow; 8080 8081 if (SlideShow && SlideShow.isActive) { 8082 SlideShow.set(); 8083 } 8084 }, 8085 8086 "afterKeydown.fb": function (e, instance, current, keypress, keycode) { 8087 var SlideShow = instance && instance.SlideShow; 8088 8089 // "P" or Spacebar 8090 if (SlideShow && current.opts.slideShow && (keycode === 80 || keycode === 32) && !$(document.activeElement).is("button,a,input")) { 8091 keypress.preventDefault(); 8092 8093 SlideShow.toggle(); 8094 } 8095 }, 8096 8097 "beforeClose.fb onDeactivate.fb": function (e, instance) { 8098 var SlideShow = instance && instance.SlideShow; 8099 8100 if (SlideShow) { 8101 SlideShow.stop(); 8102 } 8103 } 8104 }); 8105 8106 // Page Visibility API to pause slideshow when window is not active 8107 $(document).on("visibilitychange", function () { 8108 if ( $.fancybox.version != '3.5.7' ) { 8109 return; 8110 } 8111 8112 var instance = $.fancybox.getInstance(), 8113 SlideShow = instance && instance.SlideShow; 8114 8115 if (SlideShow && SlideShow.isActive) { 8116 if (document.hidden) { 8117 SlideShow.clear(); 8118 } else { 8119 SlideShow.set(); 8120 } 8121 } 8122 }); 8123 })(document, jQuery); 8124 // ========================================================================== 8125 // 8126 // FullScreen 8127 // Adds fullscreen functionality 8128 // 8129 // ========================================================================== 8130 (function (document, $) { 8131 "use strict"; 8132 8133 // Collection of methods supported by user browser 8134 var fn = (function () { 8135 var fnMap = [ 8136 ["requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror"], 8137 // new WebKit 8138 [ 8139 "webkitRequestFullscreen", 8140 "webkitExitFullscreen", 8141 "webkitFullscreenElement", 8142 "webkitFullscreenEnabled", 8143 "webkitfullscreenchange", 8144 "webkitfullscreenerror" 8145 ], 8146 // old WebKit (Safari 5.1) 8147 [ 8148 "webkitRequestFullScreen", 8149 "webkitCancelFullScreen", 8150 "webkitCurrentFullScreenElement", 8151 "webkitCancelFullScreen", 8152 "webkitfullscreenchange", 8153 "webkitfullscreenerror" 8154 ], 8155 [ 8156 "mozRequestFullScreen", 8157 "mozCancelFullScreen", 8158 "mozFullScreenElement", 8159 "mozFullScreenEnabled", 8160 "mozfullscreenchange", 8161 "mozfullscreenerror" 8162 ], 8163 ["msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError"] 8164 ]; 8165 8166 var ret = {}; 8167 8168 for (var i = 0; i < fnMap.length; i++) { 8169 var val = fnMap[i]; 8170 8171 if (val && val[1] in document) { 8172 for (var j = 0; j < val.length; j++) { 8173 ret[fnMap[0][j]] = val[j]; 8174 } 8175 8176 return ret; 8177 } 8178 } 8179 8180 return false; 8181 })(); 8182 8183 if (fn) { 8184 var FullScreen = { 8185 request: function (elem) { 8186 elem = elem || document.documentElement; 8187 8188 elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT); 8189 }, 8190 exit: function () { 8191 document[fn.exitFullscreen](); 8192 }, 8193 toggle: function (elem) { 8194 elem = elem || document.documentElement; 8195 8196 if (this.isFullscreen()) { 8197 this.exit(); 8198 } else { 8199 this.request(elem); 8200 } 8201 }, 8202 isFullscreen: function () { 8203 return Boolean(document[fn.fullscreenElement]); 8204 }, 8205 enabled: function () { 8206 return Boolean(document[fn.fullscreenEnabled]); 8207 } 8208 }; 8209 8210 $.extend(true, $.fancybox.defaults, { 8211 btnTpl: { 8212 fullScreen: '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fsenter" title="{{FULL_SCREEN}}">' + 8213 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>' + 8214 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5 16h3v3h2v-5H5zm3-8H5v2h5V5H8zm6 11h2v-3h3v-2h-5zm2-11V5h-2v5h5V8z"/></svg>' + 8215 "</button>" 8216 }, 8217 fullScreen: { 8218 autoStart: false 8219 } 8220 }); 8221 8222 $(document).on(fn.fullscreenchange, function () { 8223 var isFullscreen = FullScreen.isFullscreen(), 8224 instance = $.fancybox.getInstance(); 8225 8226 if (instance) { 8227 // If image is zooming, then force to stop and reposition properly 8228 if (instance.current && instance.current.type === "image" && instance.isAnimating) { 8229 instance.isAnimating = false; 8230 8231 instance.update(true, true, 0); 8232 8233 if (!instance.isComplete) { 8234 instance.complete(); 8235 } 8236 } 8237 8238 instance.trigger("onFullscreenChange", isFullscreen); 8239 8240 instance.$refs.container.toggleClass("fancybox-is-fullscreen", isFullscreen); 8241 8242 instance.$refs.toolbar 8243 .find("[data-fancybox-fullscreen]") 8244 .toggleClass("fancybox-button--fsenter", !isFullscreen) 8245 .toggleClass("fancybox-button--fsexit", isFullscreen); 8246 } 8247 }); 8248 } 8249 8250 $(document).on({ 8251 "onInit.fb": function (e, instance) { 8252 var $container; 8253 8254 if (!fn) { 8255 instance.$refs.toolbar.find("[data-fancybox-fullscreen]").remove(); 8256 8257 return; 8258 } 8259 8260 if (instance && instance.group[instance.currIndex].opts.fullScreen) { 8261 $container = instance.$refs.container; 8262 8263 $container.on("click.fb-fullscreen", "[data-fancybox-fullscreen]", function (e) { 8264 e.stopPropagation(); 8265 e.preventDefault(); 8266 8267 FullScreen.toggle(); 8268 }); 8269 8270 if (instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true) { 8271 FullScreen.request(); 8272 } 8273 8274 // Expose API 8275 instance.FullScreen = FullScreen; 8276 } else if (instance) { 8277 instance.$refs.toolbar.find("[data-fancybox-fullscreen]").hide(); 8278 } 8279 }, 8280 8281 "afterKeydown.fb": function (e, instance, current, keypress, keycode) { 8282 // "F" 8283 if (instance && instance.FullScreen && keycode === 70) { 8284 keypress.preventDefault(); 8285 8286 instance.FullScreen.toggle(); 8287 } 8288 }, 8289 8290 "beforeClose.fb": function (e, instance) { 8291 if (instance && instance.FullScreen && instance.$refs.container.hasClass("fancybox-is-fullscreen")) { 8292 FullScreen.exit(); 8293 } 8294 } 8295 }); 8296 })(document, jQuery); 8297 // ========================================================================== 8298 // 8299 // Thumbs 8300 // Displays thumbnails in a grid 8301 // 8302 // ========================================================================== 8303 (function (document, $) { 8304 "use strict"; 8305 8306 var CLASS = "fancybox-thumbs", 8307 CLASS_ACTIVE = CLASS + "-active"; 8308 8309 // Make sure there are default values 8310 $.fancybox.defaults = $.extend( 8311 true, { 8312 btnTpl: { 8313 thumbs: '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' + 8314 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14.59 14.59h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76h-3.76v-3.76zm-4.47 0h3.76v3.76H5.65v-3.76zm8.94-4.47h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76h-3.76V5.65zm-4.47 0h3.76v3.76H5.65V5.65z"/></svg>' + 8315 "</button>" 8316 }, 8317 thumbs: { 8318 autoStart: false, // Display thumbnails on opening 8319 hideOnClose: true, // Hide thumbnail grid when closing animation starts 8320 parentEl: ".fancybox-container", // Container is injected into this element 8321 axis: "y" // Vertical (y) or horizontal (x) scrolling 8322 } 8323 }, 8324 $.fancybox.defaults 8325 ); 8326 8327 var FancyThumbs = function (instance) { 8328 this.init(instance); 8329 }; 8330 8331 $.extend(FancyThumbs.prototype, { 8332 $button: null, 8333 $grid: null, 8334 $list: null, 8335 isVisible: false, 8336 isActive: false, 8337 8338 init: function (instance) { 8339 var self = this, 8340 group = instance.group, 8341 enabled = 0; 8342 8343 self.instance = instance; 8344 self.opts = group[instance.currIndex].opts.thumbs; 8345 8346 instance.Thumbs = self; 8347 8348 self.$button = instance.$refs.toolbar.find("[data-fancybox-thumbs]"); 8349 8350 // Enable thumbs if at least two group items have thumbnails 8351 for (var i = 0, len = group.length; i < len; i++) { 8352 if (group[i].thumb) { 8353 enabled++; 8354 } 8355 8356 if (enabled > 1) { 8357 break; 8358 } 8359 } 8360 8361 if (enabled > 1 && !!self.opts) { 8362 self.$button.removeAttr("style").on("click", function () { 8363 self.toggle(); 8364 }); 8365 8366 self.isActive = true; 8367 } else { 8368 self.$button.hide(); 8369 } 8370 }, 8371 8372 create: function () { 8373 var self = this, 8374 instance = self.instance, 8375 parentEl = self.opts.parentEl, 8376 list = [], 8377 src; 8378 8379 if (!self.$grid) { 8380 // Create main element 8381 self.$grid = $('<div class="' + CLASS + " " + CLASS + "-" + self.opts.axis + '"></div>').appendTo( 8382 instance.$refs.container 8383 .find(parentEl) 8384 .addBack() 8385 .filter(parentEl) 8386 ); 8387 8388 // Add "click" event that performs gallery navigation 8389 self.$grid.on("click", "a", function () { 8390 instance.jumpTo($(this).attr("data-index")); 8391 }); 8392 } 8393 8394 // Build the list 8395 if (!self.$list) { 8396 self.$list = $('<div class="' + CLASS + '__list">').appendTo(self.$grid); 8397 } 8398 8399 $.each(instance.group, function (i, item) { 8400 src = item.thumb; 8401 8402 if (!src && item.type === "image") { 8403 src = item.src; 8404 } 8405 8406 list.push( 8407 '<a href="javascript:;" tabindex="0" data-index="' + 8408 i + 8409 '"' + 8410 (src && src.length ? ' style="background-image:url(' + src + ')"' : 'class="fancybox-thumbs-missing"') + 8411 "></a>" 8412 ); 8413 }); 8414 8415 self.$list[0].innerHTML = list.join(""); 8416 8417 if (self.opts.axis === "x") { 8418 // Set fixed width for list element to enable horizontal scrolling 8419 self.$list.width( 8420 parseInt(self.$grid.css("padding-right"), 10) + 8421 instance.group.length * 8422 self.$list 8423 .children() 8424 .eq(0) 8425 .outerWidth(true) 8426 ); 8427 } 8428 }, 8429 8430 focus: function (duration) { 8431 var self = this, 8432 $list = self.$list, 8433 $grid = self.$grid, 8434 thumb, 8435 thumbPos; 8436 8437 if (!self.instance.current) { 8438 return; 8439 } 8440 8441 thumb = $list 8442 .children() 8443 .removeClass(CLASS_ACTIVE) 8444 .filter('[data-index="' + self.instance.current.index + '"]') 8445 .addClass(CLASS_ACTIVE); 8446 8447 thumbPos = thumb.position(); 8448 8449 // Check if need to scroll to make current thumb visible 8450 if (self.opts.axis === "y" && (thumbPos.top < 0 || thumbPos.top > $list.height() - thumb.outerHeight())) { 8451 $list.stop().animate({ 8452 scrollTop: $list.scrollTop() + thumbPos.top 8453 }, 8454 duration 8455 ); 8456 } else if ( 8457 self.opts.axis === "x" && 8458 (thumbPos.left < $grid.scrollLeft() || thumbPos.left > $grid.scrollLeft() + ($grid.width() - thumb.outerWidth())) 8459 ) { 8460 $list 8461 .parent() 8462 .stop() 8463 .animate({ 8464 scrollLeft: thumbPos.left 8465 }, 8466 duration 8467 ); 8468 } 8469 }, 8470 8471 update: function () { 8472 var that = this; 8473 that.instance.$refs.container.toggleClass("fancybox-show-thumbs", this.isVisible); 8474 8475 if (that.isVisible) { 8476 if (!that.$grid) { 8477 that.create(); 8478 } 8479 8480 that.instance.trigger("onThumbsShow"); 8481 8482 that.focus(0); 8483 } else if (that.$grid) { 8484 that.instance.trigger("onThumbsHide"); 8485 } 8486 8487 // Update content position 8488 that.instance.update(); 8489 }, 8490 8491 hide: function () { 8492 this.isVisible = false; 8493 this.update(); 8494 }, 8495 8496 show: function () { 8497 this.isVisible = true; 8498 this.update(); 8499 }, 8500 8501 toggle: function () { 8502 this.isVisible = !this.isVisible; 8503 this.update(); 8504 } 8505 }); 8506 8507 $(document).on({ 8508 "onInit.fb": function (e, instance) { 8509 var Thumbs; 8510 8511 if (instance && !instance.Thumbs) { 8512 Thumbs = new FancyThumbs(instance); 8513 8514 if (Thumbs.isActive && Thumbs.opts.autoStart === true) { 8515 Thumbs.show(); 8516 } 8517 } 8518 }, 8519 8520 "beforeShow.fb": function (e, instance, item, firstRun) { 8521 var Thumbs = instance && instance.Thumbs; 8522 8523 if (Thumbs && Thumbs.isVisible) { 8524 Thumbs.focus(firstRun ? 0 : 250); 8525 } 8526 }, 8527 8528 "afterKeydown.fb": function (e, instance, current, keypress, keycode) { 8529 var Thumbs = instance && instance.Thumbs; 8530 8531 // "G" 8532 if (Thumbs && Thumbs.isActive && keycode === 71) { 8533 keypress.preventDefault(); 8534 8535 Thumbs.toggle(); 8536 } 8537 }, 8538 8539 "beforeClose.fb": function (e, instance) { 8540 var Thumbs = instance && instance.Thumbs; 8541 8542 if (Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false) { 8543 Thumbs.$grid.hide(); 8544 } 8545 } 8546 }); 8547 })(document, jQuery); 8548 //// ========================================================================== 8549 // 8550 // Share 8551 // Displays simple form for sharing current url 8552 // 8553 // ========================================================================== 8554 (function (document, $) { 8555 "use strict"; 8556 8557 $.extend(true, $.fancybox.defaults, { 8558 btnTpl: { 8559 share: '<button data-fancybox-share class="fancybox-button fancybox-button--share" title="{{SHARE}}">' + 8560 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2.55 19c1.4-8.4 9.1-9.8 11.9-9.8V5l7 7-7 6.3v-3.5c-2.8 0-10.5 2.1-11.9 4.2z"/></svg>' + 8561 "</button>" 8562 }, 8563 share: { 8564 url: function (instance, item) { 8565 return ( 8566 (!instance.currentHash && !(item.type === "inline" || item.type === "html") ? item.origSrc || item.src : false) || window.location 8567 ); 8568 }, 8569 tpl: '<div class="fancybox-share">' + 8570 "<h1>{{SHARE}}</h1>" + 8571 "<p>" + 8572 '<a class="fancybox-share__button fancybox-share__button--fb" href="https://www.facebook.com/sharer/sharer.php?u={{url}}">' + 8573 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m287 456v-299c0-21 6-35 35-35h38v-63c-7-1-29-3-55-3-54 0-91 33-91 94v306m143-254h-205v72h196" /></svg>' + 8574 "<span>Facebook</span>" + 8575 "</a>" + 8576 '<a class="fancybox-share__button fancybox-share__button--tw" href="https://twitter.com/intent/tweet?url={{url}}&text={{descr}}">' + 8577 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m456 133c-14 7-31 11-47 13 17-10 30-27 37-46-15 10-34 16-52 20-61-62-157-7-141 75-68-3-129-35-169-85-22 37-11 86 26 109-13 0-26-4-37-9 0 39 28 72 65 80-12 3-25 4-37 2 10 33 41 57 77 57-42 30-77 38-122 34 170 111 378-32 359-208 16-11 30-25 41-42z" /></svg>' + 8578 "<span>Twitter</span>" + 8579 "</a>" + 8580 '<a class="fancybox-share__button fancybox-share__button--pt" href="https://www.pinterest.com/pin/create/button/?url={{url}}&description={{descr}}&media={{media}}">' + 8581 '<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m265 56c-109 0-164 78-164 144 0 39 15 74 47 87 5 2 10 0 12-5l4-19c2-6 1-8-3-13-9-11-15-25-15-45 0-58 43-110 113-110 62 0 96 38 96 88 0 67-30 122-73 122-24 0-42-19-36-44 6-29 20-60 20-81 0-19-10-35-31-35-25 0-44 26-44 60 0 21 7 36 7 36l-30 125c-8 37-1 83 0 87 0 3 4 4 5 2 2-3 32-39 42-75l16-64c8 16 31 29 56 29 74 0 124-67 124-157 0-69-58-132-146-132z" fill="#fff"/></svg>' + 8582 "<span>Pinterest</span>" + 8583 "</a>" + 8584 "</p>" + 8585 '<p><input class="fancybox-share__input" type="text" value="{{url_raw}}" onclick="select()" /></p>' + 8586 "</div>" 8587 } 8588 }); 8589 8590 function escapeHtml(string) { 8591 var entityMap = { 8592 "&": "&", 8593 "<": "<", 8594 ">": ">", 8595 '"': """, 8596 "'": "'", 8597 "/": "/", 8598 "`": "`", 8599 "=": "=" 8600 }; 8601 8602 return String(string).replace(/[&<>"'`=\/]/g, function (s) { 8603 return entityMap[s]; 8604 }); 8605 } 8606 8607 $(document).on("click", "[data-fancybox-share]", function () { 8608 var instance = $.fancybox.getInstance(), 8609 current = instance.current || null, 8610 url, 8611 tpl; 8612 8613 if (!current) { 8614 return; 8615 } 8616 8617 if ($.type(current.opts.share.url) === "function") { 8618 url = current.opts.share.url.apply(current, [instance, current]); 8619 } 8620 8621 tpl = current.opts.share.tpl 8622 .replace(/\{\{media\}\}/g, current.type === "image" ? encodeURIComponent(current.src) : "") 8623 .replace(/\{\{url\}\}/g, encodeURIComponent(url)) 8624 .replace(/\{\{url_raw\}\}/g, escapeHtml(url)) 8625 .replace(/\{\{descr\}\}/g, instance.$caption ? encodeURIComponent(instance.$caption.text()) : ""); 8626 8627 $.fancybox.open({ 8628 src: instance.translate(instance, tpl), 8629 type: "html", 8630 opts: { 8631 touch: false, 8632 animationEffect: false, 8633 afterLoad: function (shareInstance, shareCurrent) { 8634 // Close self if parent instance is closing 8635 instance.$refs.container.one("beforeClose.fb", function () { 8636 shareInstance.close(null, 0); 8637 }); 8638 8639 // Opening links in a popup window 8640 shareCurrent.$content.find(".fancybox-share__button").on('click',function () { 8641 window.open(this.href, "Share", "width=550, height=450"); 8642 return false; 8643 }); 8644 }, 8645 mobile: { 8646 autoFocus: false 8647 } 8648 } 8649 }); 8650 }); 8651 })(document, jQuery); 8652 // ========================================================================== 8653 // 8654 // Hash 8655 // Enables linking to each modal 8656 // 8657 // ========================================================================== 8658 (function (window, document, $) { 8659 "use strict"; 8660 8661 // Simple $.escapeSelector polyfill (for jQuery prior v3) 8662 if (!$.escapeSelector) { 8663 $.escapeSelector = function (sel) { 8664 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 8665 var fcssescape = function (ch, asCodePoint) { 8666 if (asCodePoint) { 8667 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 8668 if (ch === "\0") { 8669 return "\uFFFD"; 8670 } 8671 8672 // Control characters and (dependent upon position) numbers get escaped as code points 8673 return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " "; 8674 } 8675 8676 // Other potentially-special ASCII characters get backslash-escaped 8677 return "\\" + ch; 8678 }; 8679 8680 return (sel + "").replace(rcssescape, fcssescape); 8681 }; 8682 } 8683 8684 // Get info about gallery name and current index from url 8685 function parseUrl() { 8686 var hash = window.location.hash.substr(1), 8687 rez = hash.split("-"), 8688 index = rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) ? parseInt(rez.pop(-1), 10) || 1 : 1, 8689 gallery = rez.join("-"); 8690 8691 return { 8692 hash: hash, 8693 /* Index is starting from 1 */ 8694 index: index < 1 ? 1 : index, 8695 gallery: gallery 8696 }; 8697 } 8698 8699 // Trigger click evnt on links to open new fancyBox instance 8700 function triggerFromUrl(url) { 8701 if (url.gallery !== "") { 8702 // If we can find element matching 'data-fancybox' atribute, 8703 // then triggering click event should start fancyBox 8704 $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']") 8705 .eq(url.index - 1) 8706 .focus() 8707 .trigger("click.fb-start"); 8708 } 8709 } 8710 8711 // Get gallery name from current instance 8712 function getGalleryID(instance) { 8713 var opts, ret; 8714 8715 if (!instance) { 8716 return false; 8717 } 8718 8719 opts = instance.current ? instance.current.opts : instance.opts; 8720 ret = opts.hash || (opts.$orig ? opts.$orig.data("fancybox") || opts.$orig.data("fancybox-trigger") : ""); 8721 8722 return ret === "" ? false : ret; 8723 } 8724 8725 // Start when DOM becomes ready 8726 $(function () { 8727 if ( $.fancybox.version != '3.5.7' ) { 8728 return; 8729 } 8730 8731 // Check if user has disabled this module 8732 if ($.fancybox.defaults.hash === false) { 8733 return; 8734 } 8735 8736 // Update hash when opening/closing fancyBox 8737 $(document).on({ 8738 "onInit.fb": function (e, instance) { 8739 var url, gallery; 8740 8741 if (instance.group[instance.currIndex].opts.hash === false) { 8742 return; 8743 } 8744 8745 url = parseUrl(); 8746 gallery = getGalleryID(instance); 8747 8748 // Make sure gallery start index matches index from hash 8749 if (gallery && url.gallery && gallery == url.gallery) { 8750 instance.currIndex = url.index - 1; 8751 } 8752 }, 8753 8754 "beforeShow.fb": function (e, instance, current, firstRun) { 8755 var gallery; 8756 8757 if (!current || current.opts.hash === false) { 8758 return; 8759 } 8760 8761 // Check if need to update window hash 8762 gallery = getGalleryID(instance); 8763 8764 if (!gallery) { 8765 return; 8766 } 8767 8768 // Variable containing last hash value set by fancyBox 8769 // It will be used to determine if fancyBox needs to close after hash change is detected 8770 instance.currentHash = gallery + (instance.group.length > 1 ? "-" + (current.index + 1) : ""); 8771 8772 // If current hash is the same (this instance most likely is opened by hashchange), then do nothing 8773 if (window.location.hash === "#" + instance.currentHash) { 8774 return; 8775 } 8776 8777 if (firstRun && !instance.origHash) { 8778 instance.origHash = window.location.hash; 8779 } 8780 8781 if (instance.hashTimer) { 8782 clearTimeout(instance.hashTimer); 8783 } 8784 8785 // Update hash 8786 instance.hashTimer = setTimeout(function () { 8787 if ("replaceState" in window.history) { 8788 window.history[firstRun ? "pushState" : "replaceState"]({}, 8789 document.title, 8790 window.location.pathname + window.location.search + "#" + instance.currentHash 8791 ); 8792 8793 if (firstRun) { 8794 instance.hasCreatedHistory = true; 8795 } 8796 } else { 8797 window.location.hash = instance.currentHash; 8798 } 8799 8800 instance.hashTimer = null; 8801 }, 300); 8802 }, 8803 8804 "beforeClose.fb": function (e, instance, current) { 8805 if (!current || current.opts.hash === false) { 8806 return; 8807 } 8808 8809 clearTimeout(instance.hashTimer); 8810 8811 // Goto previous history entry 8812 if (instance.currentHash && instance.hasCreatedHistory) { 8813 window.history.back(); 8814 } else if (instance.currentHash) { 8815 if ("replaceState" in window.history) { 8816 window.history.replaceState({}, document.title, window.location.pathname + window.location.search + (instance.origHash || "")); 8817 } else { 8818 window.location.hash = instance.origHash; 8819 } 8820 } 8821 8822 instance.currentHash = null; 8823 } 8824 }); 8825 8826 // Check if need to start/close after url has changed 8827 $(window).on("hashchange.fb", function () { 8828 var url = parseUrl(), 8829 fb = null; 8830 8831 // Find last fancyBox instance that has "hash" 8832 $.each( 8833 $(".fancybox-container") 8834 .get() 8835 .reverse(), 8836 function (index, value) { 8837 var tmp = $(value).data("FancyBox"); 8838 8839 if (tmp && tmp.currentHash) { 8840 fb = tmp; 8841 return false;
8842 } 8843 } 8844 ); 8845 8846 if (fb) { 8847 // Now, compare hash values 8848 if (fb.currentHash !== url.gallery + "-" + url.index && !(url.index === 1 && fb.currentHash == url.gallery)) { 8849 fb.currentHash = null; 8850 8851 fb.close(); 8852 } 8853 } else if (url.gallery !== "") { 8854 triggerFromUrl(url); 8855 } 8856 }); 8857 8858 // Check current hash and trigger click event on matching element to start fancyBox, if needed 8859 setTimeout(function () { 8860 if ( $.fancybox.version != '3.5.7' ) { 8861 return; 8862 } 8863 8864 if (!$.fancybox.getInstance()) { 8865 triggerFromUrl(parseUrl()); 8866 } 8867 }, 50); 8868 }); 8869 })(window, document, jQuery); 8870 // ========================================================================== 8871 // 8872 // Wheel 8873 // Basic mouse weheel support for gallery navigation 8874 // 8875 // ========================================================================== 8876 (function (document, $) { 8877 "use strict"; 8878 8879 var prevTime = new Date().getTime(); 8880 8881 $(document).on({ 8882 "onInit.fb": function (e, instance, current) { 8883 instance.$refs.stage.on("mousewheel DOMMouseScroll wheel MozMousePixelScroll", function (e) { 8884 var current = instance.current, 8885 currTime = new Date().getTime(); 8886 8887 if (instance.group.length < 2 || current.opts.wheel === false || (current.opts.wheel === "auto" && current.type !== "image")) { 8888 return; 8889 } 8890 8891 e.preventDefault(); 8892 e.stopPropagation(); 8893 8894 if (current.$slide.hasClass("fancybox-animated")) { 8895 return; 8896 } 8897 8898 e = e.originalEvent || e; 8899 8900 if (currTime - prevTime < 250) { 8901 return; 8902 } 8903 8904 prevTime = currTime; 8905 8906 instance[(-e.deltaY || -e.deltaX || e.wheelDelta || -e.detail) < 0 ? "next" : "previous"](); 8907 }); 8908 } 8909 }); 8910 })(document, jQuery); 8911 8912; 8913(function ($, window, document, undefined) { 8914 8915 var pluginName = "MegaMenu", 8916 defaults = { 8917 propertyName: "value" 8918 }; 8919 8920 // Set delay time for mouseout 8921 var delayOut = 400; // Default delay time is 200 8922 8923 // the list of menus 8924 var menus = []; 8925 8926 function CustomMenu(element, options) { 8927 this.element = element; 8928 8929 this.options = $.extend({}, defaults, options); 8930 8931 this._defaults = defaults; 8932 this._name = pluginName; 8933 8934 this.init(); 8935 } 8936 8937 CustomMenu.prototype = { 8938 isOpen: false, 8939 timeout: null, 8940 init: function () { 8941 8942 var that = this; 8943 8944 $(this).each(function(index, menu) { 8945 that.node = menu.element; 8946 that.addListeners(menu.element); 8947 8948 var $menu = $(menu.element); 8949 $menu.addClass("dropdownJavascript"); 8950 menus.push(menu.element); 8951 8952 $menu.find('ul > li').each(function(index, submenu) { 8953 if ($(submenu).find('ul').length > 0 ) { 8954 $(submenu).addClass('with-menu'); 8955 } 8956 }); 8957 }); 8958 }, 8959 addListeners: function(menu) { 8960 var that = this; 8961 $(menu).on('mouseover',function(e) { 8962 that.handleMouseOver.call(that, e); 8963 }).on('mouseout',function(e) { 8964 that.handleMouseOut.call(that, e); 8965 }); 8966 }, 8967 handleMouseOver: function (e) { 8968 var that = this; 8969 // clear the timeout 8970 this.clearTimeout(); 8971 8972 // find the parent list item 8973 //var item = ('target' in e ? e.target : e.srcElement); 8974 var item = e.target || e.srcElement; 8975 while (item.nodeName != 'LI' && item != this.node) { 8976 item = item.parentNode; 8977 } 8978 8979 // if the target is within a list item, set the timeout 8980 if (item.nodeName == 'LI') { 8981 this.toOpen = item; 8982 this.timeout = setTimeout(function() { 8983 that.open.call(that); 8984 }, this.options.delay); 8985 } 8986 8987 }, 8988 handleMouseOut: function () { 8989 var that = this; 8990 // clear the timeout 8991 this.clearTimeout(); 8992 8993 // Check mouseout delay overriding 8994 var _delayOut = this.options.delay; 8995 if ( delayOut ) { 8996 _delayOut = delayOut; 8997 } 8998 8999 this.timeout = setTimeout(function() { 9000 that.close.call(that); 9001 }, _delayOut ); 9002 9003 }, 9004 clearTimeout: function () { 9005 9006 // clear the timeout 9007 if (this.timeout) { 9008 clearTimeout(this.timeout); 9009 this.timeout = null; 9010 } 9011 9012 }, 9013 open: function () { 9014 9015 var that = this; 9016 // store that the menu is open 9017 this.isOpen = true; 9018 9019 // loop over the list items with the same parent 9020 var items = $(this.toOpen).parent().children('li'); 9021 $(items).each(function(index, item) { 9022 $(item).find("ul").each(function(index, submenu) { 9023 if (item != that.toOpen) { 9024 // close the submenu 9025 $(item).removeClass("dropdownOpen"); 9026 that.close(item); 9027 9028 } else if (!$(item).hasClass('dropdownOpen')) { 9029 9030 // open the submenu 9031 //if ( !$(item).parents('li').hasClass('has-mega-menu') ) { 9032 $(item).addClass("dropdownOpen"); 9033 //} 9034 9035 9036 // determine the location of the edges of the submenu 9037 var left = 0; 9038 var node = submenu; 9039 while (node) { 9040 //abs is because when you make menus right to left 9041 //the offsetLeft would be negative 9042 left += Math.abs(node.offsetLeft); 9043 node = node.offsetParent; 9044 } 9045 var right = left + submenu.offsetWidth; 9046 9047 9048 //We should refactor this code to execute only when menu is vertical 9049 var menuHeight = $(submenu).outerHeight(); 9050 var parentTop = $(submenu).offset().top - $(window).scrollTop(); 9051 var totalHeight = menuHeight + parentTop; 9052 var windowHeight = window.innerHeight; 9053 9054 /* if (totalHeight > windowHeight) { 9055 var bestTop = (windowHeight - totalHeight) - 20; 9056 $(submenu).css('margin-top', bestTop + "px"); 9057 }*/ 9058 9059 //remove any previous classes 9060 $(item).removeClass('dropdownRightToLeft'); 9061 9062 // move the submenu to the right of the item if appropriate 9063 if (left < 0) $(item).addClass('dropdownLeftToRight'); 9064 9065 // move the submenu to the left of the item if appropriate 9066 if (right > document.body.clientWidth) { 9067 $(item).addClass('dropdownRightToLeft'); 9068 } 9069 9070 } 9071 }); 9072 }); 9073 9074 }, 9075 9076 9077 close: function (node) { 9078 9079 // if no node was specified, close all menus 9080 if (!node) { 9081 this.isOpen = false;
9082 node = this.node; 9083 } 9084 9085 // loop over the items, closing their submenus 9086 $(node).find('li').each(function(index, item) { 9087 $(item).removeClass('dropdownOpen'); 9088 }); 9089 9090 } 9091 }; 9092 9093 $.fn[pluginName] = function (options) { 9094 return this.each(function () { 9095 if (!$.data(this, "plugin_" + pluginName)) { 9096 $.data(this, "plugin_" + pluginName, 9097 new CustomMenu(this, options)); 9098 } 9099 }); 9100 }; 9101 9102})(jQuery, window, document); 9103 9104/*! 9105 * modernizr v3.3.1 9106 * Build https://modernizr.com/download?-cssanimations-csstransitions-touchevents-domprefixes-prefixed-prefixes-setclasses-shiv-testallprops-testprop-teststyles-dontmin 9107 * 9108 * Copyright (c) 9109 * Faruk Ates 9110 * Paul Irish 9111 * Alex Sexton 9112 * Ryan Seddon 9113 * Patrick Kettner 9114 * Stu Cox 9115 * Richard Herrera 9116 9117 * MIT License 9118 */ 9119 9120/* 9121 * Modernizr tests which native CSS3 and HTML5 features are available in the 9122 * current UA and makes the results available to you in two ways: as properties on 9123 * a global `Modernizr` object, and as classes on the `<html>` element. This 9124 * information allows you to progressively enhance your pages with a granular level 9125 * of control over the experience. 9126*/ 9127 9128;(function(window, document, undefined){ 9129 var classes = []; 9130 9131 9132 var tests = []; 9133 9134 9135 /** 9136 * 9137 * ModernizrProto is the constructor for Modernizr 9138 * 9139 * @class 9140 * @access public 9141 */ 9142 9143 var ModernizrProto = { 9144 // The current version, dummy 9145 _version: '3.3.1', 9146 9147 // Any settings that don't work as separate modules 9148 // can go in here as configuration. 9149 _config: { 9150 'classPrefix': '', 9151 'enableClasses': true, 9152 'enableJSClass': true, 9153 'usePrefixes': true 9154 }, 9155 9156 // Queue of tests 9157 _q: [], 9158 9159 // Stub these for people who are listening 9160 on: function(test, cb) { 9161 // I don't really think people should do this, but we can 9162 // safe guard it a bit. 9163 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 9164 // This is in case people listen to synchronous tests. I would leave it out, 9165 // but the code to *disallow* sync tests in the real version of this 9166 // function is actually larger than this. 9167 var self = this; 9168 setTimeout(function() { 9169 cb(self[test]); 9170 }, 0); 9171 }, 9172 9173 addTest: function(name, fn, options) { 9174 tests.push({name: name, fn: fn, options: options}); 9175 }, 9176 9177 addAsyncTest: function(fn) { 9178 tests.push({name: null, fn: fn}); 9179 } 9180 }; 9181 9182 9183 9184 // Fake some of Object.create so we can force non test results to be non "own" properties. 9185 var Modernizr = function() {}; 9186 Modernizr.prototype = ModernizrProto; 9187 9188 // Leak modernizr globally when you `require` it rather than force it here. 9189 // Overwrite name so constructor name is nicer :D 9190 Modernizr = new Modernizr(); 9191 9192 9193 9194 /** 9195 * List of property values to set for css tests. See ticket #21 9196 * http://git.io/vUGl4 9197 * 9198 * @memberof Modernizr 9199 * @name Modernizr._prefixes 9200 * @optionName Modernizr._prefixes 9201 * @optionProp prefixes 9202 * @access public 9203 * @example 9204 * 9205 * Modernizr._prefixes is the internal list of prefixes that we test against 9206 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 9207 * an array of kebab-case vendor prefixes you can use within your code. 9208 * 9209 * Some common use cases include 9210 * 9211 * Generating all possible prefixed version of a CSS property 9212 * ```js 9213 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 9214 * 9215 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 9216 * ``` 9217 * 9218 * Generating all possible prefixed version of a CSS value 9219 * ```js 9220 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 9221 * 9222 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 9223 * ``` 9224 */ 9225 9226 // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test 9227 // values in feature detects to continue to work 9228 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); 9229 9230 // expose these for the plugin API. Look in the source for how to join() them against your input 9231 ModernizrProto._prefixes = prefixes; 9232 9233 9234 9235 /** 9236 * is returns a boolean if the typeof an obj is exactly type. 9237 * 9238 * @access private 9239 * @function is 9240 * @param {*} obj - A thing we want to check the type of 9241 * @param {string} type - A string to compare the typeof against 9242 * @returns {boolean} 9243 */ 9244 9245 function is(obj, type) { 9246 return typeof obj === type; 9247 } 9248 ; 9249 9250 /** 9251 * Run through all tests and detect their support in the current UA. 9252 * 9253 * @access private 9254 */ 9255 9256 function testRunner() { 9257 var featureNames; 9258 var feature; 9259 var aliasIdx; 9260 var result; 9261 var nameIdx; 9262 var featureName; 9263 var featureNameSplit; 9264 9265 for (var featureIdx in tests) { 9266 if (tests.hasOwnProperty(featureIdx)) { 9267 featureNames = []; 9268 feature = tests[featureIdx]; 9269 // run the test, throw the return value into the Modernizr, 9270 // then based on that boolean, define an appropriate className 9271 // and push it into an array of classes we'll join later. 9272 // 9273 // If there is no name, it's an 'async' test that is run, 9274 // but not directly added to the object. That should 9275 // be done with a post-run addTest call. 9276 if (feature.name) { 9277 featureNames.push(feature.name.toLowerCase()); 9278 9279 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 9280 // Add all the aliases into the names list 9281 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 9282 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 9283 } 9284 } 9285 } 9286 9287 // Run the test, or use the raw value if it's not a function 9288 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 9289 9290 9291 // Set each of the names on the Modernizr object 9292 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 9293 featureName = featureNames[nameIdx];
9294 // Support dot properties as sub tests. We don't do checking to make sure 9295 // that the implied parent tests have been added. You must call them in 9296 // order (either in the test, or make the parent test a dependency). 9297 // 9298 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 9299 // hashtag famous last words 9300 featureNameSplit = featureName.split('.'); 9301 9302 if (featureNameSplit.length === 1) { 9303 Modernizr[featureNameSplit[0]] = result; 9304 } else { 9305 // cast to a Boolean, if not one already 9306 /* jshint -W053 */ 9307 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 9308 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 9309 } 9310 9311 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 9312 } 9313 9314 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 9315 } 9316 } 9317 } 9318 } 9319 ; 9320 9321 /** 9322 * docElement is a convenience wrapper to grab the root element of the document 9323 * 9324 * @access private 9325 * @returns {HTMLElement|SVGElement} The root element of the document 9326 */ 9327 9328 var docElement = document.documentElement; 9329 9330 9331 /** 9332 * A convenience helper to check if the document we are running in is an SVG document 9333 * 9334 * @access private 9335 * @returns {boolean} 9336 */ 9337 9338 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 9339 9340 9341 /** 9342 * setClasses takes an array of class names and adds them to the root element 9343 * 9344 * @access private 9345 * @function setClasses 9346 * @param {string[]} classes - Array of class names 9347 */ 9348 9349 // Pass in an and array of class names, e.g.:
9350 // ['no-webp', 'borderradius', ...] 9351 function setClasses(classes) { 9352 var className = docElement.className; 9353 var classPrefix = Modernizr._config.classPrefix || ''; 9354 9355 if (isSVG) { 9356 className = className.baseVal; 9357 } 9358 9359 // Change `no-js` to `js` (independently of the `enableClasses` option) 9360 // Handle classPrefix on this too 9361 if (Modernizr._config.enableJSClass) { 9362 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 9363 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 9364 } 9365 9366 if (Modernizr._config.enableClasses) { 9367 // Add the new classes 9368 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 9369 isSVG ? docElement.className.baseVal = className : docElement.className = className; 9370 } 9371 9372 } 9373 9374 ; 9375 9376/** 9377 * @optionName html5shiv 9378 * @optionProp html5shiv 9379 */ 9380 9381 // Take the html5 variable out of the html5shiv scope so we can return it. 9382 var html5; 9383 if (!isSVG) { 9384 /** 9385 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 9386 */ 9387 ;(function(window, document) { 9388 /*jshint evil:true */ 9389 /** version */ 9390 var version = '3.7.3'; 9391 9392 /** Preset options */ 9393 var options = window.html5 || {}; 9394 9395 /** Used to skip problem elements */ 9396 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 9397 9398 /** Not all elements can be cloned in IE **/ 9399 var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; 9400 9401 /** Detect whether the browser supports default html5 styles */ 9402 var supportsHtml5Styles; 9403 9404 /** Name of the expando, to work with multiple documents or to re-shiv one document */ 9405 var expando = '_html5shiv'; 9406 9407 /** The id for the the documents expando */ 9408 var expanID = 0; 9409 9410 /** Cached data for each document */ 9411 var expandoData = {}; 9412 9413 /** Detect whether the browser supports unknown elements */ 9414 var supportsUnknownElements; 9415 9416 (function() { 9417 try { 9418 var a = document.createElement('a'); 9419 a.innerHTML = '<xyz></xyz>'; 9420 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 9421 supportsHtml5Styles = ('hidden' in a); 9422 9423 supportsUnknownElements = a.childNodes.length == 1 || (function() { 9424 // assign a false positive if unable to shiv 9425 (document.createElement)('a'); 9426 var frag = document.createDocumentFragment(); 9427 return ( 9428 typeof frag.cloneNode == 'undefined' || 9429 typeof frag.createDocumentFragment == 'undefined' || 9430 typeof frag.createElement == 'undefined' 9431 ); 9432 }()); 9433 } catch(e) { 9434 // assign a false positive if detection fails => unable to shiv 9435 supportsHtml5Styles = true; 9436 supportsUnknownElements = true; 9437 } 9438 9439 }()); 9440 9441 /*--------------------------------------------------------------------------*/ 9442 9443 /** 9444 * Creates a style sheet with the given CSS text and adds it to the document. 9445 * @private 9446 * @param {Document} ownerDocument The document. 9447 * @param {String} cssText The CSS text. 9448 * @returns {StyleSheet} The style element. 9449 */ 9450 function addStyleSheet(ownerDocument, cssText) { 9451 var p = ownerDocument.createElement('p'), 9452 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 9453 9454 p.innerHTML = 'x<style>' + cssText + '</style>'; 9455 return parent.insertBefore(p.lastChild, parent.firstChild); 9456 } 9457 9458 /** 9459 * Returns the value of `html5.elements` as an array. 9460 * @private 9461 * @returns {Array} An array of shived element node names. 9462 */ 9463 function getElements() { 9464 var elements = html5.elements; 9465 return typeof elements == 'string' ? elements.split(' ') : elements; 9466 } 9467 9468 /** 9469 * Extends the built-in list of html5 elements 9470 * @memberOf html5 9471 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 9472 * @param {Document} ownerDocument The context document. 9473 */ 9474 function addElements(newElements, ownerDocument) { 9475 var elements = html5.elements; 9476 if(typeof elements != 'string'){ 9477 elements = elements.join(' '); 9478 } 9479 if(typeof newElements != 'string'){ 9480 newElements = newElements.join(' '); 9481 } 9482 html5.elements = elements +' '+ newElements; 9483 shivDocument(ownerDocument); 9484 } 9485 9486 /** 9487 * Returns the data associated to the given document 9488 * @private 9489 * @param {Document} ownerDocument The document. 9490 * @returns {Object} An object of data. 9491 */ 9492 function getExpandoData(ownerDocument) { 9493 var data = expandoData[ownerDocument[expando]]; 9494 if (!data) { 9495 data = {}; 9496 expanID++; 9497 ownerDocument[expando] = expanID; 9498 expandoData[expanID] = data; 9499 } 9500 return data; 9501 } 9502 9503 /** 9504 * returns a shived element for the given nodeName and document 9505 * @memberOf html5 9506 * @param {String}
vendor: 4,418 bytes, lines 9506-9617
9506 nodeName name of the element 9507 * @param {Document|DocumentFragment} ownerDocument The context document. 9508 * @returns {Object} The shived element. 9509 */ 9510 function createElement(nodeName, ownerDocument, data){ 9511 if (!ownerDocument) { 9512 ownerDocument = document; 9513 } 9514 if(supportsUnknownElements){ 9515 return ownerDocument.createElement(nodeName); 9516 } 9517 if (!data) { 9518 data = getExpandoData(ownerDocument); 9519 } 9520 var node; 9521 9522 if (data.cache[nodeName]) { 9523 node = data.cache[nodeName].cloneNode(); 9524 } else if (saveClones.test(nodeName)) { 9525 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 9526 } else { 9527 node = data.createElem(nodeName); 9528 } 9529 9530 // Avoid adding some elements to fragments in IE < 9 because 9531 // * Attributes like `name` or `type` cannot be set/changed once an element 9532 // is inserted into a document/fragment 9533 // * Link elements with `src` attributes that are inaccessible, as with 9534 // a 403 response, will cause the tab/window to crash 9535 // * Script elements appended to fragments will execute when their `src` 9536 // or `text` property is set 9537 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 9538 } 9539 9540 /** 9541 * returns a shived DocumentFragment for the given document 9542 * @memberOf html5 9543 * @param {Document} ownerDocument The context document. 9544 * @returns {Object} The shived DocumentFragment. 9545 */ 9546 function createDocumentFragment(ownerDocument, data){ 9547 if (!ownerDocument) { 9548 ownerDocument = document; 9549 } 9550 if(supportsUnknownElements){ 9551 return ownerDocument.createDocumentFragment(); 9552 } 9553 data = data || getExpandoData(ownerDocument); 9554 var clone = data.frag.cloneNode(), 9555 i = 0, 9556 elems = getElements(), 9557 l = elems.length; 9558 for(;i<l;i++){ 9559 clone.createElement(elems[i]); 9560 } 9561 return clone; 9562 } 9563 9564 /** 9565 * Shivs the `createElement` and `createDocumentFragment` methods of the document. 9566 * @private 9567 * @param {Document|DocumentFragment} ownerDocument The document. 9568 * @param {Object} data of the document. 9569 */ 9570 function shivMethods(ownerDocument, data) { 9571 if (!data.cache) { 9572 data.cache = {}; 9573 data.createElem = ownerDocument.createElement; 9574 data.createFrag = ownerDocument.createDocumentFragment; 9575 data.frag = data.createFrag(); 9576 } 9577 9578 9579 ownerDocument.createElement = function(nodeName) { 9580 //abort shiv 9581 if (!html5.shivMethods) { 9582 return data.createElem(nodeName); 9583 } 9584 return createElement(nodeName, ownerDocument, data); 9585 }; 9586 9587 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' + 9588 'var n=f.cloneNode(),c=n.createElement;' + 9589 'h.shivMethods&&(' + 9590 // unroll the `createElement` calls 9591 getElements().join().replace(/[\w\-:]+/g, function(nodeName) { 9592 data.createElem(nodeName); 9593 data.frag.createElement(nodeName); 9594 return 'c("' + nodeName + '")'; 9595 }) + 9596 ');return n}' 9597 )(html5, data.frag); 9598 } 9599 9600 /*--------------------------------------------------------------------------*/ 9601 9602 /** 9603 * Shivs the given document. 9604 * @memberOf html5 9605 * @param {Document} ownerDocument The document to shiv. 9606 * @returns {Document} The shived document. 9607 */ 9608 function shivDocument(ownerDocument) { 9609 if (!ownerDocument) { 9610 ownerDocument = document; 9611 } 9612 var data = getExpandoData(ownerDocument); 9613 9614 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) { 9615 data.hasCSS = !!addStyleSheet(ownerDocument, 9616 // corrects block display not defined in IE6/7/8/9 9617 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{dis
9617play:block}' + 9618 // adds styling not present in IE6/7/8/9 9619 'mark{background:#FF0;color:#000}' + 9620 // hides non-rendered elements 9621 'template{display:none}' 9622 ); 9623 } 9624 if (!supportsUnknownElements) { 9625 shivMethods(ownerDocument, data); 9626 } 9627 return ownerDocument; 9628 } 9629 9630 /*--------------------------------------------------------------------------*/ 9631 9632 /** 9633 * The `html5` object is exposed so that more elements can be shived and 9634 * existing shiving can be detected on iframes. 9635 * @type Object 9636 * @example 9637 * 9638 * // options can be changed before the script is included 9639 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false }; 9640 */ 9641 var html5 = { 9642 9643 /** 9644 * An array or space separated string of node names of the elements to shiv. 9645 * @memberOf html5 9646 * @type Array|String 9647 */ 9648 'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video', 9649 9650 /** 9651 * current version of html5shiv 9652 */ 9653 'version': version, 9654 9655 /** 9656 * A flag to indicate that the HTML5 style sheet should be inserted. 9657 * @memberOf html5 9658 * @type Boolean 9659 */ 9660 'shivCSS': (options.shivCSS !== false), 9661 9662 /** 9663 * Is equal to true if a browser supports creating unknown/HTML5 elements 9664 * @memberOf html5 9665 * @type boolean 9666 */ 9667 'supportsUnknownElements': supportsUnknownElements, 9668 9669 /** 9670 * A flag to indicate that the document's `createElement` and `createDocumentFragment` 9671 * methods should be overwritten. 9672 * @memberOf html5 9673 * @type Boolean 9674 */ 9675 'shivMethods': (options.shivMethods !== false), 9676 9677 /** 9678 * A string to describe the type of `html5` object ("default" or "default print"). 9679 * @memberOf html5
9680 * @type String 9681 */ 9682 'type': 'default', 9683 9684 // shivs the document according to the specified `html5` object options 9685 'shivDocument': shivDocument, 9686 9687 //creates a shived element 9688 createElement: createElement, 9689 9690 //creates a shived documentFragment 9691 createDocumentFragment: createDocumentFragment, 9692 9693 //extends list of elements 9694 addElements: addElements 9695 }; 9696 9697 /*--------------------------------------------------------------------------*/ 9698 9699 // expose html5 9700 window.html5 = html5; 9701 9702 // shiv the document 9703 shivDocument(document); 9704 9705 if(typeof module == 'object' && module.exports){ 9706 module.exports = html5; 9707 } 9708 9709 }(typeof window !== "undefined" ? window : this, document)); 9710 } 9711; 9712 9713 /** 9714 * If the browsers follow the spec, then they would expose vendor-specific style as: 9715 * elem.style.WebkitBorderRadius 9716 * instead of something like the following, which would be technically incorrect: 9717 * elem.style.webkitBorderRadius 9718 9719 * Webkit ghosts their properties in lowercase but Opera & Moz do not. 9720 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 9721 * erik.eae.net/archives/2008/03/10/21.48.10/ 9722 9723 * More here: github.com/Modernizr/Modernizr/issues/issue/21 9724 * 9725 * @access private 9726 * @returns {string} The string representing the vendor-specific style properties 9727 */ 9728 9729 var omPrefixes = 'Moz O ms Webkit'; 9730 9731 9732 /** 9733 * List of JavaScript DOM values used for tests 9734 * 9735 * @memberof Modernizr 9736 * @name Modernizr._domPrefixes 9737 * @optionName Modernizr._domPrefixes 9738 * @optionProp domPrefixes 9739 * @access public 9740 * @example 9741 * 9742 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 9743 * than kebab-case properties, all properties are their Capitalized variant 9744 * 9745 * ```js 9746 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 9747 * ``` 9748 */ 9749 9750 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 9751 ModernizrProto._domPrefixes = domPrefixes; 9752 9753 9754 /** 9755 * cssToDOM takes a kebab-case string and converts it to camelCase 9756 * e.g. box-sizing -> boxSizing 9757 * 9758 * @access private 9759 * @function cssToDOM 9760 * @param {string} name - String name of kebab-case prop we want to convert 9761 * @returns {string} The camelCase version of the supplied name 9762 */ 9763 9764 function cssToDOM(name) { 9765 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 9766 return m1 + m2.toUpperCase(); 9767 }).replace(/^-/, ''); 9768 } 9769 ; 9770 9771 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 9772 ModernizrProto._cssomPrefixes = cssomPrefixes; 9773 9774 9775 /** 9776 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in 9777 * some prefixed form, or false, in the case of an unsupported rule 9778 * 9779 * @memberof Modernizr 9780 * @name Modernizr.atRule 9781 * @optionName Modernizr.atRule() 9782 * @optionProp atRule 9783 * @access public 9784 * @function atRule 9785 * @param {string} prop - String name of the @-rule to test for 9786 * @returns {string|boolean} The string representing the (possibly prefixed) 9787 * valid version of the @-rule, or `false` when it is unsupported. 9788 * @example 9789 * ```js 9790 * var keyframes = Modernizr.atRule('@keyframes'); 9791 * 9792 * if (keyframes) { 9793 * // keyframes are supported 9794 * // could be `@-webkit-keyframes` or `@keyframes` 9795 * } else { 9796 * // keyframes === `false` 9797 * } 9798 * ``` 9799 * 9800 */ 9801 9802 var atRule = function(prop) { 9803 var length = prefixes.length; 9804 var cssrule = window.CSSRule; 9805 var rule; 9806 9807 if (typeof cssrule === 'undefined') { 9808 return undefined; 9809 } 9810 9811 if (!prop) { 9812 return false; 9813 } 9814 9815 // remove literal @ from beginning of provided property 9816 prop = prop.replace(/^@/, ''); 9817 9818 // CSSRules use underscores instead of dashes 9819 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; 9820 9821 if (rule in cssrule) { 9822 return '@' + prop; 9823 } 9824 9825 for (var i = 0; i < length; i++) { 9826 // prefixes gives us something like -o-, and we want O_ 9827 var prefix = prefixes[i]; 9828 var thisRule = prefix.toUpperCase() + '_' + rule; 9829 9830 if (thisRule in cssrule) { 9831 return '@-' + prefix.toLowerCase() + '-' + prop; 9832 } 9833 } 9834 9835 return false;
9836 }; 9837 9838 ModernizrProto.atRule = atRule; 9839 9840 9841 9842 9843 /** 9844 * contains checks to see if a string contains another string 9845 * 9846 * @access private 9847 * @function contains 9848 * @param {string} str - The string we want to check for substrings 9849 * @param {string} substr - The substring we want to search the first string for 9850 * @returns {boolean} 9851 */ 9852 9853 function contains(str, substr) { 9854 return !!~('' + str).indexOf(substr); 9855 } 9856 9857 ; 9858 9859 /** 9860 * createElement is a convenience wrapper around document.createElement. Since we 9861 * use createElement all over the place, this allows for (slightly) smaller code 9862 * as well as abstracting away issues with creating elements in contexts other than 9863 * HTML documents (e.g. SVG documents). 9864 * 9865 * @access private 9866 * @function createElement 9867 * @returns {HTMLElement|SVGElement} An HTML or SVG element 9868 */ 9869 9870 function createElement() { 9871 if (typeof document.createElement !== 'function') { 9872 // This is the case in IE7, where the type of createElement is "object". 9873 // For this reason, we cannot call apply() as Object is not a Function. 9874 return document.createElement(arguments[0]); 9875 } else if (isSVG) { 9876 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 9877 } else { 9878 return document.createElement.apply(document, arguments); 9879 } 9880 } 9881 9882 ; 9883 9884 /** 9885 * getBody returns the body of a document, or an element that can stand in for 9886 * the body if a real body does not exist 9887 * 9888 * @access private 9889 * @function getBody 9890 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 9891 * artificially created element that stands in for the body 9892 */ 9893 9894 function getBody() { 9895 // After page load injecting a fake body doesn't work so check if body exists 9896 var body = document.body; 9897 9898 if (!body) { 9899 // Can't use the real body create a fake one. 9900 body = createElement(isSVG ? 'svg' : 'body'); 9901 body.fake = true; 9902 } 9903 9904 return body; 9905 } 9906 9907 ; 9908 9909 /** 9910 * injectElementWithStyles injects an element with style element and some CSS rules 9911 * 9912 * @access private 9913 * @function injectElementWithStyles 9914 * @param {string} rule - String representing a css rule 9915 * @param {function} callback - A function that is used to test the injected element 9916 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 9917 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 9918 * @returns {boolean} 9919 */ 9920 9921 function injectElementWithStyles(rule, callback, nodes, testnames) { 9922 var mod = 'modernizr'; 9923 var style; 9924 var ret; 9925 var node; 9926 var docOverflow; 9927 var div = createElement('div'); 9928 var body = getBody(); 9929 9930 if (parseInt(nodes, 10)) { 9931 // In order not to give false positives we create a node for each test 9932 // This also allows the method to scale for unspecified uses 9933 while (nodes--) { 9934 node = createElement('div'); 9935 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 9936 div.appendChild(node); 9937 } 9938 } 9939 9940 style = createElement('style'); 9941 style.type = 'text/css'; 9942 style.id = 's' + mod; 9943 9944 // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. 9945 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 9946 (!body.fake ? div : body).appendChild(style); 9947 body.appendChild(div); 9948 9949 if (style.styleSheet) { 9950 style.styleSheet.cssText = rule; 9951 } else { 9952 style.appendChild(document.createTextNode(rule)); 9953 } 9954 div.id = mod; 9955 9956 if (body.fake) { 9957 //avoid crashing IE8, if background image is used 9958 body.style.background = ''; 9959 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 9960 body.style.overflow = 'hidden'; 9961 docOverflow = docElement.style.overflow; 9962 docElement.style.overflow = 'hidden'; 9963 docElement.appendChild(body); 9964 } 9965 9966 ret = callback(div, rule); 9967 // If this is done after page load we don't want to remove the body so check if body exists 9968 if (body.fake) { 9969 body.parentNode.removeChild(body); 9970 docElement.style.overflow = docOverflow; 9971 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 9972 docElement.offsetHeight; 9973 } else { 9974 div.parentNode.removeChild(div); 9975 } 9976 9977 return !!ret; 9978 9979 } 9980 9981 ; 9982 9983 /** 9984 * testStyles injects an element with style element and some CSS rules 9985 * 9986 * @memberof Modernizr 9987 * @name Modernizr.testStyles 9988 * @optionName Modernizr.testStyles() 9989 * @optionProp testStyles 9990 * @access public 9991 * @function testStyles 9992 * @param {string} rule - String representing a css rule 9993 * @param {function} callback - A function that is used to test the injected element 9994 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 9995 * @param {string[]}
9995 [testnames] - An array of strings that are used as ids for the additional nodes 9996 * @returns {boolean} 9997 * @example 9998 * 9999 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 10000 * along with (possibly multiple) DOM elements. This lets you check for features 10001 * that can not be detected by simply checking the [IDL](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Interface_development_guide/IDL_interface_rules). 10002 * 10003 * ```js 10004 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 10005 * // elem is the first DOM node in the page (by default #modernizr) 10006 * // rule is the first argument you supplied - the CSS rule in string form 10007 * 10008 * addTest('widthworks', elem.style.width === '9px') 10009 * }); 10010 * ``` 10011 * 10012 * If your test requires multiple nodes, you can include a third argument 10013 * indicating how many additional div elements to include on the page. The 10014 * additional nodes are injected as children of the `elem` that is returned as 10015 * the first argument to the callback. 10016 * 10017 * ```js 10018 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 10019 * document.getElementById('modernizr').style.width === '1px'; // true 10020 * document.getElementById('modernizr2').style.width === '2px'; // true 10021 * elem.firstChild === document.getElementById('modernizr2'); // true 10022 * }, 1); 10023 * ``` 10024 * 10025 * By default, all of the additional elements have an ID of `modernizr[n]`, where 10026 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 10027 * the second additional is `#modernizr3`, etc.). 10028 * If you want to have more meaningful IDs for your function, you can provide 10029 * them as the fourth argument, as an array of strings 10030 * 10031 * ```js 10032 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 10033 * elem.firstChild === document.getElementById('foo'); // true 10034 * elem.lastChild === document.getElementById('bar'); // true 10035 * }, 2, ['foo', 'bar']); 10036 * ``` 10037 * 10038 */ 10039 10040 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 10041 10042 10043 /** 10044 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 10045 * 10046 * @access private 10047 * @function fnBind 10048 * @param {function} fn - a function you want to change `this` reference to 10049 * @param {object} that - the `this` you want to call the function with 10050 * @returns {function} The wrapped version of the supplied function 10051 */ 10052 10053 function fnBind(fn, that) { 10054 return function() { 10055 return fn.apply(that, arguments); 10056 }; 10057 } 10058 10059 ; 10060 10061 /** 10062 * testDOMProps is a generic DOM property test; if a browser supports 10063 * a certain property, it won't return undefined for it. 10064 * 10065 * @access private 10066 * @function testDOMProps 10067 * @param {array.<string>} props - An array of properties to test for 10068 * @param {object} obj - An object or Element you want to use to test the parameters again 10069 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check 10070 */ 10071 function testDOMProps(props, obj, elem) { 10072 var item; 10073 10074 for (var i in props) { 10075 if (props[i] in obj) { 10076 10077 // return the property name as a string 10078 if (elem === false) { 10079 return props[i]; 10080 } 10081 10082 item = obj[props[i]]; 10083 10084 // let's bind a function 10085 if (is(item, 'function')) { 10086 // bind to obj unless overriden 10087 return fnBind(item, elem || obj); 10088 } 10089 10090 // return the unbound function or obj or value 10091 return item; 10092 } 10093 } 10094 return false; 10095 } 10096 10097 ; 10098 10099 /** 10100 * Create our "modernizr" element that we do most feature tests on. 10101 * 10102 * @access private 10103 */ 10104 10105 var modElem = { 10106 elem: createElement('modernizr') 10107 }; 10108 10109 // Clean up this element 10110 Modernizr._q.push(function() { 10111 delete modElem.elem; 10112 }); 10113 10114 10115 10116 var mStyle = { 10117 style: modElem.elem.style 10118 }; 10119 10120 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 10121 // the front of the queue. 10122 Modernizr._q.unshift(function() { 10123 delete mStyle.style; 10124 }); 10125 10126 10127 10128 /** 10129 * domToCSS takes a camelCase string and converts it to kebab-case 10130 * e.g. boxSizing -> box-sizing 10131 * 10132 * @access private 10133 * @function domToCSS 10134 * @param {string} name - String name of camelCase prop we want to convert 10135 * @returns {string} The kebab-case version of the supplied name 10136 */ 10137 10138 function domToCSS(name) { 10139 return name.replace(/([A-Z])/g, function(str, m1) { 10140 return '-' + m1.toLowerCase(); 10141 }).replace(/^ms-/, '-ms-'); 10142 } 10143 ; 10144 10145 /** 10146 * nativeTestProps allows for us to use native feature detection functionality if available. 10147 * some prefixed form, or false, in the case of an unsupported rule 10148 * 10149 * @access private 10150 * @function nativeTestProps 10151 * @param {array} props - An array of property names 10152 * @param {string} value - A string representing the value we want to check via @supports 10153 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 10154 */ 10155 10156 // Accepts a list of property names and a single value 10157 // Returns `undefined` if native detection not available 10158 function nativeTestProps(props, value) { 10159 var i = props.length; 10160 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 10161 if ('CSS' in window && 'supports' in window.CSS) { 10162 // Try every prefixed variant of the property 10163 while (i--) { 10164 if (window.CSS.supports(domToCSS(props[i]), value)) { 10165 return true; 10166 } 10167 } 10168 return false;
10169 } 10170 // Otherwise fall back to at-rule (for Opera 12.x) 10171 else if ('CSSSupportsRule' in window) { 10172 // Build a condition string for every prefixed variant 10173 var conditionText = []; 10174 while (i--) { 10175 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 10176 } 10177 conditionText = conditionText.join(' or '); 10178 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 10179 return getComputedStyle(node, null).position == 'absolute'; 10180 }); 10181 } 10182 return undefined; 10183 } 10184 ; 10185 10186 // testProps is a generic CSS / DOM property test. 10187 10188 // In testing support for a given CSS property, it's legit to test: 10189 // `elem.style[styleName] !== undefined` 10190 // If the property is supported it will return an empty string, 10191 // if unsupported it will return undefined. 10192 10193 // We'll take advantage of this quick test and skip setting a style 10194 // on our modernizr element, but instead just testing undefined vs 10195 // empty string. 10196 10197 // Property names can be provided in either camelCase or kebab-case. 10198 10199 function testProps(props, prefixed, value, skipValueTest) { 10200 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 10201 10202 // Try native detect first 10203 if (!is(value, 'undefined')) { 10204 var result = nativeTestProps(props, value); 10205 if (!is(result, 'undefined')) { 10206 return result; 10207 } 10208 } 10209 10210 // Otherwise do it properly 10211 var afterInit, i, propsLength, prop, before; 10212 10213 // If we don't have a style element, that means we're running async or after 10214 // the core tests, so we'll need to create our own elements to use 10215 10216 // inside of an SVG element, in certain browsers, the `style` element is only 10217 // defined for valid tags. Therefore, if `modernizr` does not have one, we 10218 // fall back to a less used element and hope for the best. 10219 // for strict XHTML browsers the hardly used samp element is used 10220 var elems = ['modernizr', 'tspan', 'samp']; 10221 while (!mStyle.style && elems.length) { 10222 afterInit = true; 10223 mStyle.modElem = createElement(elems.shift()); 10224 mStyle.style = mStyle.modElem.style; 10225 } 10226 10227 // Delete the objects if we created them. 10228 function cleanElems() { 10229 if (afterInit) { 10230 delete mStyle.style; 10231 delete mStyle.modElem; 10232 } 10233 } 10234 10235 propsLength = props.length; 10236 for (i = 0; i < propsLength; i++) { 10237 prop = props[i]; 10238 before = mStyle.style[prop]; 10239 10240 if (contains(prop, '-')) { 10241 prop = cssToDOM(prop); 10242 } 10243 10244 if (mStyle.style[prop] !== undefined) { 10245 10246 // If value to test has been passed in, do a set-and-check test. 10247 // 0 (integer) is a valid property value, so check that `value` isn't 10248 // undefined, rather than just checking it's truthy. 10249 if (!skipValueTest && !is(value, 'undefined')) { 10250 10251 // Needs a try catch block because of old IE. This is slow, but will 10252 // be avoided in most cases because `skipValueTest` will be used. 10253 try { 10254 mStyle.style[prop] = value; 10255 } catch (e) {} 10256 10257 // If the property value has changed, we assume the value used is 10258 // supported. If `value` is empty string, it'll fail here (because 10259 // it hasn't changed), which matches how browsers have implemented 10260 // CSS.supports() 10261 if (mStyle.style[prop] != before) { 10262 cleanElems(); 10263 return prefixed == 'pfx' ? prop : true; 10264 } 10265 } 10266 // Otherwise just return true, or the property name if this is a 10267 // `prefixed()` call 10268 else { 10269 cleanElems(); 10270 return prefixed == 'pfx' ? prop : true; 10271 } 10272 } 10273 } 10274 cleanElems(); 10275 return false; 10276 } 10277 10278 ; 10279 10280 /** 10281 * testProp() investigates whether a given style property is recognized 10282 * Property names can be provided in either camelCase or kebab-case. 10283 * 10284 * @memberof Modernizr 10285 * @name Modernizr.testProp 10286 * @access public 10287 * @optionName Modernizr.testProp() 10288 * @optionProp testProp 10289 * @function testProp 10290 * @param {string} prop - Name of the CSS property to check 10291 * @param {string} [value] - Name of the CSS value to check 10292 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 10293 * @returns {boolean} 10294 * @example 10295 * 10296 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 10297 * version of the string. 10298 * 10299 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 10300 * 10301 * ```js 10302 * Modernizr.testProp('pointerEvents') // true 10303 * ``` 10304 * 10305 * You can also provide a value as an optional second argument to check if a 10306 * specific value is supported 10307 * 10308 * ```js
10309 * Modernizr.testProp('pointerEvents', 'none') // true 10310 * Modernizr.testProp('pointerEvents', 'penguin') // false 10311 * ``` 10312 */ 10313 10314 var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 10315 return testProps([prop], undefined, value, useValue); 10316 }; 10317 10318 10319 /** 10320 * testPropsAll tests a list of DOM properties we want to check against. 10321 * We specify literally ALL possible (known and/or likely) properties on 10322 * the element including the non-vendor prefixed one, for forward- 10323 * compatibility. 10324 * 10325 * @access private 10326 * @function testPropsAll 10327 * @param {string} prop - A string of the property to test for 10328 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip 10329 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against 10330 * @param {string} [value] - A string of a css value 10331 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set 10332 */ 10333 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 10334 10335 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 10336 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 10337 10338 // did they call .prefixed('boxSizing') or are we just testing a prop? 10339 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 10340 return testProps(props, prefixed, value, skipValueTest); 10341 10342 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 10343 } else { 10344 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 10345 return testDOMProps(props, prefixed, elem); 10346 } 10347 } 10348 10349 // Modernizr.testAllProps() investigates whether a given style property, 10350 // or any of its vendor-prefixed variants, is recognized 10351 // 10352 // Note that the property names must be provided in the camelCase variant. 10353 // Modernizr.testAllProps('boxSizing') 10354 ModernizrProto.testAllProps = testPropsAll; 10355 10356 10357 10358 /** 10359 * prefixed returns the prefixed or nonprefixed property name variant of your input 10360 * 10361 * @memberof Modernizr 10362 * @name Modernizr.prefixed 10363 * @optionName Modernizr.prefixed() 10364 * @optionProp prefixed 10365 * @access public 10366 * @function prefixed 10367 * @param {string} prop - String name of the property to test for 10368 * @param {object} [obj] - An object to test for the prefixed properties on 10369 * @param {HTMLElement} [elem] - An element used to test specific properties against 10370 * @returns {string|false} The string representing the (possibly prefixed) valid 10371 * version of the property, or `false` when it is unsupported. 10372 * @example 10373 * 10374 * Modernizr.prefixed takes a string css value in the DOM style camelCase (as 10375 * opposed to the css style kebab-case) form and returns the (possibly prefixed) 10376 * version of that property that the browser actually supports. 10377 * 10378 * For example, in older Firefox... 10379 * ```js 10380 * prefixed('boxSizing') 10381 * ``` 10382 * returns 'MozBoxSizing' 10383 * 10384 * In newer Firefox, as well as any other browser that support the unprefixed 10385 * version would simply return `boxSizing`. Any browser that does not support 10386 * the property at all, it will return `false`. 10387 * 10388 * By default, prefixed is checked against a DOM element. If you want to check 10389 * for a property on another object, just pass it as a second argument 10390 * 10391 * ```js 10392 * var rAF = prefixed('requestAnimationFrame', window); 10393 * 10394 * raf(function() { 10395 * renderFunction(); 10396 * }) 10397 * ``` 10398 * 10399 * Note that this will return _the actual function_ - not the name of the function. 10400 * If you need the actual name of the property, pass in `false` as a third argument 10401 * 10402 * ```js 10403 * var rAFProp = prefixed('requestAnimationFrame', window, false); 10404 * 10405 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit 10406 * ``` 10407 * 10408 * One common use case for prefixed is if you're trying to determine which transition 10409 * end event to bind to, you might do something like... 10410 * ```js 10411 * var transEndEventNames = { 10412 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser 10413 * 'MozTransition' : 'transitionend', * only for FF < 15 10414 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ 10415 * }; 10416 * 10417 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 10418 * ``` 10419 * 10420 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). 10421 */ 10422 10423 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { 10424 if (prop.indexOf('@') === 0) { 10425 return atRule(prop); 10426 } 10427 10428 if (prop.indexOf('-') != -1) { 10429 // Convert kebab-case to camelCase 10430 prop = cssToDOM(prop); 10431 } 10432 if (!obj) { 10433 return testPropsAll(prop, 'pfx'); 10434 } else { 10435 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' 10436 return testPropsAll(prop, obj, elem); 10437 } 10438 }; 10439 10440 10441 10442 /** 10443 * testAllProps determines whether a given CSS property is supported in the browser 10444 * 10445 * @memberof Modernizr 10446 * @name Modernizr.testAllProps 10447 * @optionName Modernizr.testAllProps() 10448 * @optionProp testAllProps 10449 * @access public 10450 * @function testAllProps 10451 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 10452 * @param {string} [value] - String of the value to test 10453 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 10454 * @example 10455 * 10456 * testAllProps determines whether a given CSS property, in some prefixed form, 10457 * is supported by the browser. 10458 * 10459 * ```js 10460 * testAllProps('boxSizing') // true 10461 * ``` 10462 * 10463 * It can optionally be given a CSS value in string form to test if a property 10464 * value is valid 10465 * 10466 * ```js 10467 * testAllProps('display', 'block') // true 10468 * testAllProps('display', 'penguin') // false 10469 * ``` 10470 * 10471 * A boolean can be passed as a third parameter to skip the value check when 10472 * native detection (@supports) isn't available. 10473 * 10474 * ```js 10475 * testAllProps('shapeOutside', 'content-box', true); 10476 * ``` 10477 */ 10478 10479 function testAllProps(prop, value, skipValueTest) { 10480 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 10481 } 10482 ModernizrProto.testAllProps = testAllProps; 10483 10484/*! 10485{ 10486 "name": "CSS Animations", 10487 "property": "cssanimations", 10488 "caniuse": "css-animation", 10489 "polyfills": ["transformie", "csssandpaper"], 10490 "tags": ["css"], 10491 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], 10492 "notes": [{ 10493 "name" : "Article: 'Dispelling the Android CSS animation myths'", 10494 "href": "https://goo.gl/OGw5Gm" 10495 }] 10496} 10497!*/ 10498/* DOC 10499Detects whether or not elements can be animated using CSS 10500*/ 10501 10502 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); 10503 10504/*! 10505{ 10506 "name": "CSS Transitions", 10507 "property": "csstransitions", 10508 "caniuse": "css-transitions", 10509 "tags": ["css"] 10510} 10511!*/ 10512 10513 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); 10514 10515/*! 10516{ 10517 "name": "Touch Events", 10518 "property": "touchevents", 10519 "caniuse" : "touch", 10520 "tags": ["media", "attribute"], 10521 "notes": [{ 10522 "name": "Touch Events spec", 10523 "href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/" 10524 }], 10525 "warnings": [ 10526 "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device" 10527 ], 10528 "knownBugs": [ 10529 "False-positive on some configurations of Nokia N900", 10530 "False-positive on some BlackBerry 6.0 builds – https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695" 10531 ] 10532} 10533!*/ 10534/* DOC 10535Indicates if the browser supports the W3C Touch Events API. 10536 10537This *does not* necessarily reflect a touchscreen device: 10538 10539* Older touchscreen devices only emulate mouse events 10540* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that 10541* Some browsers & OS setups may enable touch APIs when no touchscreen is connected 10542* Future browsers may implement other event models for touch interactions 10543 10544See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). 10545 10546It's recommended to bind both mouse and touch/pointer events simultaneously – see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/). 10547 10548This test will also return `true` for Firefox 4 Multitouch support. 10549*/ 10550 10551 // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415 10552 Modernizr.addTest('touchevents', function() { 10553 var bool; 10554 if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 10555 bool = true; 10556 } else { 10557 // include the 'heartz' as a way to have a non matching MQ to help terminate the join 10558 // https://git.io/vznFH 10559 var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join(''); 10560 testStyles(query, function(node) { 10561 bool = node.offsetTop === 9; 10562 }); 10563 } 10564 return bool; 10565 }); 10566 10567 10568 // Run each test 10569 testRunner(); 10570 10571 // Remove the "no-js" class if it exists 10572 setClasses(classes); 10573 10574 delete ModernizrProto.addTest; 10575 delete ModernizrProto.addAsyncTest; 10576 10577 // Run the things that are supposed to run after the tests 10578 for (var i = 0; i < Modernizr._q.length; i++) { 10579 Modernizr._q[i](); 10580 } 10581 10582 // Leak Modernizr namespace 10583 window.Modernizr = Modernizr; 10584 10585 10586; 10587 10588})(window, document); 10589$(function() { 10590 ParallaxScroll.init(); 10591}); 10592 10593// Credit: https://github.com/alumbo/jquery.parallax-scroll 10594var ParallaxScroll = { 10595 /* PUBLIC VARIABLES */ 10596 showLogs: false, 10597 round: 1000, 10598 10599 /* PUBLIC FUNCTIONS */ 10600 init: function() { 10601 this._log("init"); 10602 if (this._inited) { 10603 this._log("Already Inited"); 10604 this._inited = true; 10605 return; 10606 } 10607 this._requestAnimationFrame = (function(){ 10608 return window.requestAnimationFrame || 10609 window.webkitRequestAnimationFrame || 10610 window.mozRequestAnimationFrame || 10611 window.oRequestAnimationFrame || 10612 window.msRequestAnimationFrame || 10613 function(/* function */ callback, /* DOMElement */ element){ 10614 window.setTimeout(callback, 1000 / 60); 10615 }; 10616 })(); 10617 this._onScroll(true); 10618 }, 10619 10620 /* PRIVATE VARIABLES */ 10621 _inited: false, 10622 _properties: ['x', 'y', 'z', 'rotateX', 'rotateY', 'rotateZ', 'scaleX', 'scaleY', 'scaleZ', 'scale'], 10623 _requestAnimationFrame:null, 10624 10625 /* PRIVATE FUNCTIONS */ 10626 _log: function(message) { 10627 if (this.showLogs) console.log("Parallax Scroll / " + message); 10628 }, 10629 _onScroll: function(noSmooth) { 10630 var scroll = $(document).scrollTop(); 10631 var windowHeight = $(window).height(); 10632 this._log("onScroll " + scroll); 10633 $("[data-parallax]").each($.proxy(function(index, el) { 10634 var $el = $(el); 10635 var properties = []; 10636 var applyProperties = false;
10637 var style = $el.data("style"); 10638 if (style == undefined) { 10639 style = $el.attr("style") || ""; 10640 $el.data("style", style); 10641 } 10642 var datas = [$el.data("parallax")]; 10643 var iData; 10644 for(iData = 2; ; iData++) { 10645 if($el.data("parallax"+iData)) { 10646 datas.push($el.data("parallax-"+iData)); 10647 } 10648 else { 10649 break; 10650 } 10651 } 10652 var datasLength = datas.length; 10653 for(iData = 0; iData < datasLength; iData ++) { 10654 var data = datas[iData]; 10655 var scrollFrom = data["from-scroll"]; 10656 if (scrollFrom == undefined) scrollFrom = Math.max(0, $(el).offset().top - windowHeight); 10657 scrollFrom = scrollFrom | 0; 10658 var scrollDistance = data["distance"]; 10659 var scrollTo = data["to-scroll"]; 10660 if (scrollDistance == undefined && scrollTo == undefined) scrollDistance = windowHeight; 10661 scrollDistance = Math.max(scrollDistance | 0, 1); 10662 var easing = data["easing"]; 10663 var easingReturn = data["easing-return"]; 10664 if (easing == undefined || !$.easing|| !$.easing[easing]) easing = null; 10665 if (easingReturn == undefined || !$.easing|| !$.easing[easingReturn]) easingReturn = easing; 10666 if (easing) { 10667 var totalTime = data["duration"]; 10668 if (totalTime == undefined) totalTime = scrollDistance; 10669 totalTime = Math.max(totalTime | 0, 1); 10670 var totalTimeReturn = data["duration-return"]; 10671 if (totalTimeReturn == undefined) totalTimeReturn = totalTime; 10672 scrollDistance = 1; 10673 var currentTime = $el.data("current-time"); 10674 if(currentTime == undefined) currentTime = 0; 10675 } 10676 if (scrollTo == undefined) scrollTo = scrollFrom + scrollDistance; 10677 scrollTo = scrollTo | 0; 10678 var smoothness = data["smoothness"]; 10679 if (smoothness == undefined) smoothness = 30; 10680 smoothness = smoothness | 0; 10681 if (noSmooth || smoothness == 0) smoothness = 1; 10682 smoothness = smoothness | 0; 10683 var scrollCurrent = scroll; 10684 scrollCurrent = Math.max(scrollCurrent, scrollFrom); 10685 scrollCurrent = Math.min(scrollCurrent, scrollTo); 10686 if(easing) { 10687 if($el.data("sens") == undefined) $el.data("sens", "back"); 10688 if(scrollCurrent>scrollFrom) { 10689 if($el.data("sens") == "back") { 10690 currentTime = 1; 10691 $el.data("sens", "go"); 10692 } 10693 else { 10694 currentTime++; 10695 } 10696 } 10697 if(scrollCurrent<scrollTo) { 10698 if($el.data("sens") == "go") { 10699 currentTime = 1; 10700 $el.data("sens", "back"); 10701 } 10702 else { 10703 currentTime++; 10704 } 10705 } 10706 if(noSmooth) currentTime = totalTime; 10707 $el.data("current-time", currentTime); 10708 } 10709 this._properties.map($.proxy(function(prop) { 10710 var defaultProp = 0; 10711 var to = data[prop]; 10712 if (to == undefined) return; 10713 if(prop=="scale" || prop=="scaleX" || prop=="scaleY" || prop=="scaleZ" ) { 10714 defaultProp = 1; 10715 } 10716 else { 10717 to = to | 0; 10718 } 10719 var prev = $el.data("_" + prop); 10720 if (prev == undefined) prev = defaultProp; 10721 var next = ((to-defaultProp) * ((scrollCurrent - scrollFrom) / (scrollTo - scrollFrom))) + defaultProp; 10722 var val = prev + (next - prev) / smoothness; 10723 if(easing && currentTime>0 && currentTime<=totalTime) { 10724 var from = defaultProp; 10725 if($el.data("sens") == "back") { 10726 from = to; 10727 to = -to; 10728 easing = easingReturn; 10729 totalTime = totalTimeReturn; 10730 } 10731 val = $.easing[easing](null, currentTime, from, to, totalTime); 10732 }
10733 val = Math.ceil(val * this.round) / this.round; 10734 if(val==prev&&next==to) val = to; 10735 if(!properties[prop]) properties[prop] = 0; 10736 properties[prop] += val; 10737 if (prev != properties[prop]) { 10738 $el.data("_" + prop, properties[prop]); 10739 applyProperties = true; 10740 } 10741 }, this)); 10742 } 10743 if (applyProperties) { 10744 if (properties["z"] != undefined) { 10745 var perspective = data["perspective"]; 10746 if (perspective == undefined) perspective = 800; 10747 var $parent = $el.parent(); 10748 if(!$parent.data("style")) $parent.data("style", $parent.attr("style") || ""); 10749 $parent.attr("style", "perspective:" + perspective + "px; -webkit-perspective:" + perspective + "px; "+ $parent.data("style")); 10750 } 10751 if(properties["scaleX"] == undefined) properties["scaleX"] = 1; 10752 if(properties["scaleY"] == undefined) properties["scaleY"] = 1; 10753 if(properties["scaleZ"] == undefined) properties["scaleZ"] = 1; 10754 if (properties["scale"] != undefined) { 10755 properties["scaleX"] *= properties["scale"]; 10756 properties["scaleY"] *= properties["scale"]; 10757 properties["scaleZ"] *= properties["scale"]; 10758 } 10759 var translate3d = "translate3d(" + (properties["x"] ? properties["x"] : 0) + "px, " + (properties["y"] ? properties["y"] : 0) + "px, " + (properties["z"] ? properties["z"] : 0) + "px)"; 10760 var rotate3d = "rotateX(" + (properties["rotateX"] ? properties["rotateX"] : 0) + "deg) rotateY(" + (properties["rotateY"] ? properties["rotateY"] : 0) + "deg) rotateZ(" + (properties["rotateZ"] ? properties["rotateZ"] : 0) + "deg)"; 10761 var scale3d = "scaleX(" + properties["scaleX"] + ") scaleY(" + properties["scaleY"] + ") scaleZ(" + properties["scaleZ"] + ")"; 10762 var cssTransform = translate3d + " " + rotate3d + " " + scale3d + ";"; 10763 this._log(cssTransform); 10764 $el.attr("style", "transform:" + cssTransform + " -webkit-transform:" + cssTransform + " " + style); 10765 } 10766 }, this)); 10767 if(window.requestAnimationFrame) { 10768 window.requestAnimationFrame($.proxy(this._onScroll, this, false)); 10769 } 10770 else { 10771 this._requestAnimationFrame($.proxy(this._onScroll, this, false)); 10772 } 10773 } 10774}; 10775; 10776(function($, window, undefined) { 10777 10778 'use strict'; 10779 10780 // global 10781 var Modernizr = window.Modernizr, 10782 $body = $('body'); 10783 10784 $.DLMenu = function(options, element) { 10785 this.$el = $(element); 10786 this._init(options); 10787 }; 10788 10789 $.DLMenu.defaults = { 10790 animationClasses: { 10791 classin: 'mk-vm-animate-in-' + mk_vertical_header_anim, 10792 classout: 'mk-vm-animate-out-' + mk_vertical_header_anim 10793 }, 10794 onLevelClick: function(el, name) { 10795 return false; 10796 }, 10797 onLinkClick: function(el, ev) { 10798 return false; 10799 } 10800 }; 10801 10802 $.DLMenu.prototype = { 10803 _init: function(options) { 10804 10805 this.options = $.extend(true, {}, $.DLMenu.defaults, options); 10806 this._config(); 10807 10808 var animEndEventNames = { 10809 'WebkitAnimation': 'webkitAnimationEnd', 10810 'OAnimation': 'oAnimationEnd', 10811 'msAnimation': 'MSAnimationEnd', 10812 'animation': 'animationend' 10813 }, 10814 transEndEventNames = { 10815 'WebkitTransition': 'webkitTransitionEnd', 10816 'MozTransition': 'transitionend', 10817 'OTransition': 'oTransitionEnd', 10818 'msTransition': 'MSTransitionEnd', 10819 'transition': 'transitionend' 10820 }; 10821 10822 this.animEndEventName = animEndEventNames[Modernizr.prefixed('animation')] + '.dlmenu'; 10823 this.transEndEventName = transEndEventNames[Modernizr.prefixed('transition')] + '.dlmenu'; 10824 10825 this.animEndEventNameUnsufixed = animEndEventNames[Modernizr.prefixed('animation')]; 10826 this.transEndEventNameUnsufixed = transEndEventNames[Modernizr.prefixed('transition')]; 10827 10828 this.supportAnimations = Modernizr.cssanimations; 10829 this.supportTransitions = Modernizr.csstransitions; 10830 10831 this._initEvents(); 10832 10833 }, 10834 _config: function() { 10835 this.open = false;
10836 this.$trigger = this.$el.children('.mk-vm-trigger'); 10837 this.$menu = this.$el.children('ul.mk-vm-menu'); 10838 this.$menuitems = this.$menu.find('li:not(.mk-vm-back)'); 10839 this.$back = this.$menu.find('li.mk-vm-back'); 10840 }, 10841 _initEvents: function() { 10842 10843 var self = this; 10844 10845 $('.mk-vm-menuwrapper a').on('transitionend', function(event) { 10846 event.stopPropagation(); 10847 }); 10848 10849 this.$menuitems.on('click.dlmenu', 'a', function(event) { 10850 10851 // Breaks smooth scroll in vertical menu 10852 // event.stopPropagation(); 10853 10854 var $item = $(event.delegateTarget), 10855 $submenu = $(event.currentTarget).siblings('ul.sub-menu'); 10856 10857 if ($submenu.length > 0) { 10858 var $flyin = $submenu.clone().css('opacity', 0).insertAfter(self.$menu); 10859 var onAnimationEndFn = function() { 10860 var $parent = $item.parents('.mk-vm-subviewopen:first'); 10861 self.$menu.off(self.animEndEventName).removeClass(self.options.animationClasses.classout).addClass('mk-vm-subview'); 10862 $item.addClass('mk-vm-subviewopen') 10863 $parent.removeClass('mk-vm-subviewopen').addClass('mk-vm-subview'); 10864 $flyin.remove(); 10865 10866 // Redraw for firefox issues 10867 var $txt = $item.find('.meni-item-text'); 10868 $txt.css('opacity', 0.99); 10869 setTimeout(function() { $txt.css('opacity', 1) }, 0); 10870 }; 10871 10872 setTimeout(function() { 10873 $flyin.addClass(self.options.animationClasses.classin); 10874 self.$menu.addClass(self.options.animationClasses.classout); 10875 if (self.supportAnimations) { 10876 self.$menu.on(self.animEndEventName, onAnimationEndFn); 10877 } else { 10878 onAnimationEndFn.call(); 10879 } 10880 10881 self.options.onLevelClick($item, $item.children('a:first').text()); 10882 }); 10883 10884 10885 // This caused firefox issue. Test properly if need to bring it back 10886 // if (self.open) { 10887 // self._closeMenu(); 10888 // } else { 10889 // self._openMenu(); 10890 // } 10891 10892 return false; 10893 10894 } else { 10895 self.options.onLinkClick($item, event); 10896 } 10897 10898 }); 10899 10900 10901 // this.$trigger.on('click.dlmenu', function() { 10902 10903 // if (self.open) { 10904 // self._closeMenu(); 10905 // } else { 10906 // // if( ! $(this).hasClass('menu-item-has-children') ) return false; 10907 // self._openMenu(); 10908 // } 10909 // return false; 10910 10911 // }); 10912 10913 this.$back.on('click.dlmenu', function(event) { 10914 10915 var $this = $(this), 10916 $submenu = $this.parents('ul.sub-menu:first'), 10917 $item = $submenu.parent(), 10918 10919 $flyin = $submenu.clone().insertAfter(self.$menu); 10920 10921 var onAnimationEndFn = function() { 10922 self.$menu.off(self.animEndEventName).removeClass(self.options.animationClasses.classin); 10923 $flyin.remove(); 10924 }; 10925 10926 setTimeout(function() { 10927 $flyin.addClass(self.options.animationClasses.classout); 10928 self.$menu.addClass(self.options.animationClasses.classin); 10929 if (self.supportAnimations) { 10930 self.$menu.on(self.animEndEventName, onAnimationEndFn); 10931 } else { 10932 onAnimationEndFn.call(); 10933 } 10934 10935 $item.removeClass('mk-vm-subviewopen'); 10936 10937 var $subview = $this.parents('.mk-vm-subview:first'); 10938 if ($subview.is('li')) { 10939 $subview.addClass('mk-vm-subviewopen'); 10940 } 10941 $subview.removeClass('mk-vm-subview'); 10942 }); 10943 10944 return false; 10945 10946 }); 10947 10948 }, 10949 closeMenu: function() { 10950 if (this.open) { 10951 this._closeMenu(); 10952 } 10953 }, 10954 _closeMenu: function() { 10955 var self = this, 10956 onTransitionEndFn = function() { 10957 self.$menu.off(self.transEndEventName); 10958 self._resetMenu(); 10959 }; 10960 10961 this.$menu.removeClass('mk-vm-menuopen'); 10962 this.$menu.addClass('mk-vm-menu-toggle'); 10963 this.$trigger.removeClass('mk-vm-active'); 10964 10965 if (this.supportTransitions) { 10966 this.$menu.on(this.transEndEventName, onTransitionEndFn); 10967 } else { 10968 onTransitionEndFn.call(); 10969 } 10970 10971 this.open = false; 10972 }, 10973 openMenu: function() { 10974 if (!this.open) { 10975 this._openMenu(); 10976 } 10977 }, 10978 _openMenu: function() { 10979 var self = this; 10980 $body.off('click').on('click.dlmenu', function() { 10981 self._closeMenu(); 10982 }); 10983 this.$menu.addClass('mk-vm-menuopen mk-vm-menu-toggle').on(this.transEndEventName, function() { 10984 $(this).removeClass('mk-vm-menu-toggle'); 10985 }); 10986 this.$trigger.addClass('mk-vm-active'); 10987 this.open = true; 10988 }, 10989 _resetMenu: function() { 10990 this.$menu.removeClass('mk-vm-subview'); 10991 this.$menuitems.removeClass('mk-vm-subview mk-vm-subviewopen'); 10992 } 10993 }; 10994 10995 var logError = function(message) { 10996 if (window.console) { 10997 window.console.error(message); 10998 } 10999 }; 11000 11001 $.fn.dlmenu = function(options) { 11002 if (typeof options === 'string') { 11003 var args = Array.prototype.slice.call(arguments, 1); 11004 this.each(function() { 11005 var instance = $.data(this, 'dlmenu'); 11006 if (!instance) { 11007 logError("cannot call methods on dlmenu prior to initialization; " + 11008 "attempted to call method '" + options + "'"); 11009 return; 11010 } 11011 if (!$.isFunction(instance[options]) || options.charAt(0) === "_") { 11012 logError("no such method '" + options + "' for dlmenu instance"); 11013 return; 11014 } 11015 instance[options].apply(instance, args); 11016 }); 11017 } else { 11018 this.each(function() { 11019 var instance = $.data(this, 'dlmenu'); 11020 if (instance) { 11021 instance._init(); 11022 } else { 11023 instance = $.data(this, 'dlmenu', new $.DLMenu(options, this)); 11024 } 11025 }); 11026 } 11027 return this; 11028 }; 11029 11030})(jQuery, window); 11031( function($) { 11032 'use strict'; 11033 11034 /* 11035 * Define popup / hover states manually to prevent click for IE on touchdevices 11036 */ 11037 11038 $('.mk-main-navigation .menu-item-has-children').children('a').attr('aria-haspopup', 'true'); 11039 $('.animated-column-item').attr('aria-haspopup', 'true'); 11040 11041})( jQuery ); 11042(function($) { 11043 'use strict'; 11044 11045 var Accordion = function(el) { 11046 // Private 11047 var that = this, 11048 $el = $(el), 11049 initial = $el.data('initialindex'), 11050 timeout; 11051 11052 // Public 11053 this.$el = $el; 11054 this.$single = $('.' + this.dom.single, $el); 11055 this.isExpendable = ($el.data('style') === 'toggle-action'); 11056 11057 // Init 11058 this.bindClicks(); 11059 // Reveal initial tab on load event (wait for possible images inside) 11060 $(window).on('load', function() {
11061 if( initial !== -1 ) that.show(that.$single.eq(initial), true) 11062 }); 11063 $(window).on('resize', function() { 11064 clearTimeout(timeout); 11065 timeout = setTimeout(that.bindClicks.bind(that), 500); 11066 }); 11067 } 11068 11069 Accordion.prototype.dom = { 11070 // only class names please! 11071 single : 'mk-accordion-single', 11072 tab : 'mk-accordion-tab', 11073 pane : 'mk-accordion-pane', 11074 current : 'current', 11075 mobileToggle : 'mobile-false', 11076 mobileBreakPoint : 767 11077 } 11078 11079 Accordion.prototype.bindClicks = function() { 11080 // Prevent multiple events binding 11081 this.$single.off('click', '.' + this.dom.tab); 11082 11083 if( !(window.matchMedia('(max-width: ' + this.dom.mobileBreakPoint +'px)').matches 11084 && this.$el.hasClass(this.dom.mobileToggle)) ) { 11085 11086 this.$single.on('click', '.' + this.dom.tab, this.handleEvent.bind(this)); 11087 // When website is loaded in mobile view and resized to desktop 'current' will 11088 // inherit display: none from css. Repair it by calling show() on this element 11089 var $current = $('.' + this.dom.current, this.$el); 11090 if($('.' + this.dom.pane, $current).css('display') === 'none') this.show($current); 11091 } 11092 } 11093 11094 Accordion.prototype.handleEvent = function(e) { 11095 e.preventDefault(); 11096 e.stopPropagation(); 11097 11098 var $single = $(e.delegateTarget); 11099 11100 if(!$single.hasClass(this.dom.current)) { 11101 this.show($single); 11102 } 11103 else { 11104 if(this.isExpendable) this.hide($single); 11105 } 11106 } 11107 11108 Accordion.prototype.hide = function($single) { 11109 $single.removeClass(this.dom.current);
11110 $('.' + this.dom.pane, $single).slideUp(); 11111 } 11112 11113 Accordion.prototype.show = function($single, initial) { 11114 // hide currently opened tab 11115 if(!this.isExpendable) { 11116 var that = this; 11117 this.hide($('.' + this.dom.current, that.$el)); 11118 } 11119 11120 $single.addClass(this.dom.current); 11121 $('.' + this.dom.pane, $single).slideDown('', function() { 11122 if ( initial || ! $(this).parents('.mk-accordion').hasClass('scroll-click') ) { 11123 return; 11124 } 11125 11126 if (typeof $single.prev() === 'undefined') { 11127 $single = $single.prev() 11128 } 11129 11130 window.scrollTo({ top: $single.offset().top - 100, left: 0, behavior: 'smooth' }); 11131 }); 11132 } 11133 11134 11135 11136 // /////////////////////////////////////// 11137 // 11138 // Apply to: 11139 // 11140 // /////////////////////////////////////// 11141 11142 function init() { 11143 $('.mk-accordion').each(function() { 11144 new Accordion(this); 11145 }); 11146 } 11147 11148 init(); 11149 $(window).on('vc_reload', init); 11150 11151})(jQuery); 11152 11153(function($) { 11154 11155 'use strict'; 11156 11157 var SkillDiagram = function( el ) { 11158 this.el = el; 11159 } 11160 11161 SkillDiagram.prototype = { 11162 init : function() { 11163 this.cacheElements(); 11164 this.createDiagram(); 11165 this.$skills.each( this.createSkill.bind( this ) ); 11166 }, 11167 11168 cacheElements : function() { 11169 this.$el = $( this.el ); 11170 this.$skills = this.$el.find( '.mk-meter-arch'); 11171 this.config = this.$el.data(); 11172 this.config.radius = this.config.dimension / 2; 11173 }, 11174 11175 random : function( l, u ) { 11176 return Math.floor( ( Math.random() * ( u - l + 1 ) ) + l ); 11177 }, 11178 11179 createDiagram : function() { 11180 var self = this; 11181 $(this.el).find('svg').remove(); 11182 11183 this.diagram = Raphael( this.el, this.config.dimension, this.config.dimension ); 11184 11185 // Make svg scalable in different screen sizes 11186 this.diagram.setViewBox(0,0,this.config.dimension,this.config.dimension,true); 11187 this.diagram.setSize('90%', '90%'); 11188 11189 this.diagram.circle( this.config.radius, this.config.radius, 80 ).attr({ 11190 stroke: 'none', 11191 fill: this.config.circleColor 11192 }); 11193 11194 // Export title 11195 this.title = this.diagram.text( this.config.radius, this.config.radius, this.config.defaultText ).attr({ 11196 font: "22px helvetica", 11197 fill: this.config.defaultTextColor 11198 }).toFront(); 11199 11200 this.diagram.customAttributes.arc = function(value, color, rad){ 11201 var v = 3.6 * value, 11202 alpha = v == 360 ? 359.99 : v, 11203 r = self.random( 91, 240 ), 11204 a = (r - alpha) * Math.PI/180, 11205 b = r * Math.PI/180, 11206 sx = self.config.radius + rad * Math.cos(b), 11207 sy = self.config.radius - rad * Math.sin(b), 11208 x = self.config.radius + rad * Math.cos(a), 11209 y = self.config.radius - rad * Math.sin(a), 11210 path = [['M', sx, sy], ['A', rad, rad, 0, +(alpha > 180), 1, x, y]]; 11211 11212 return { 11213 path: path, 11214 stroke: color 11215 } 11216 } 11217 }, 11218 11219 createSkill : function( id, el ) { 11220 var self = this, 11221 $this = $( el ), 11222 config = $this.data(), 11223 radMin = 72, 11224 radVal = 27, 11225 newRad = radMin + ( radVal * (id + 1) ); 11226 11227 var $path = this.diagram.path().attr({ 11228 'stroke-width': 28, 11229 arc: [config.percent, config.color, newRad] 11230 }); 11231 11232 $path.on('mouseover', function() { 11233 self.showSkill( this, config.name, config.percent ); 11234 }).on('mouseout', function() { 11235 self.hideSkill( this ) 11236 }); 11237 }, 11238 11239 showSkill : function( self, name, percent ) { 11240 var $this = self, 11241 time = 250; 11242 11243 //solves IE problem 11244 if(Raphael.type != 'VML') $this.toFront(); 11245 11246 $this.animate({ 11247 'stroke-width': 50, 11248 'opacity': 0.9, 11249 }, 800, 'elastic' ); 11250 11251 this.title.stop() 11252 .animate({ opacity: 0 }, time, '>', function(){ 11253 this.attr({ text: name + '\n' + percent + '%' }).animate({ opacity: 1 }, time, '<'); 11254 }).toFront(); 11255 }, 11256 11257 hideSkill : function( self ) { 11258 var $this = self, 11259 self = this, 11260 time = 250; 11261 11262 $this.stop().animate({ 11263 'stroke-width': 28, 11264 opacity: 1 11265 }, time * 4, 'elastic' ); 11266 11267 self.title.stop() 11268 .animate({ opacity: 0 }, time, '>', function(){ 11269 self.title.attr({ text: self.config.defaultText }) 11270 .animate({ opacity: 1 }, time, '<'); 11271 }); 11272 } 11273 } 11274
11275 var init = function init() { 11276 if( typeof Raphael === 'undefined' ) return; 11277 $( '.mk-skill-diagram' ).each( function() { 11278 var diagram = new SkillDiagram( this ); 11279 diagram.init(); 11280 }); 11281 } 11282 11283 init(); 11284 $(window).on('vc_reload', init); 11285 11286})(jQuery); 11287 11288/* 11289 * Tab delegation 11290 * Action for modules when we don't have access to chidren DOM on processing templates 11291 * yet we want ass option of opening link in new tab. 11292 * Helpful for use with external widgets like flickr 11293 */ 11294 11295(function($) { 11296 11297 'use strict'; 11298 11299 $( '[data-js="tab-delegation"]' ).each( tabDelegation ); 11300 11301 function tabDelegation() { 11302 var $this = $( this ), 11303 data = $this.data(); 11304 11305 // Create delegation on parent element to affect async loaded children 11306 if( data.tab ) $this.on( 'click', 'a', openInTab ); 11307 } 11308 11309 function openInTab( e ) { 11310 e.preventDefault(); 11311 11312 var $this = $( this ), 11313 url = $this.attr( 'href' ); 11314 11315 window.open( url, '_blank' ); 11316 } 11317 11318})(jQuery); 11319(function($) { 11320 'use strict'; 11321 11322 var init = function init() { 11323 var Toggle = function(el) { 11324 var that = this, 11325 $el = $(el); 11326 11327 this.$el = $el; 11328 $el.on('click', function() { 11329 $el.hasClass('active-toggle') ? that.close() : that.open() 11330 }); 11331 }; 11332 11333 Toggle.prototype.dom = { 11334 pane : 'mk-toggle-pane', 11335 active : 'active-toggle' 11336 }; 11337 11338 Toggle.prototype.open = function() { 11339 var $this = this.$el; 11340 $this.addClass(this.dom.active); 11341 $this.siblings('.' + this.dom.pane).slideDown(200); 11342 }; 11343 11344 Toggle.prototype.close = function() { 11345 var $this = this.$el; 11346 $this.removeClass(this.dom.active); 11347 $this.siblings('.' + this.dom.pane).slideUp(200); 11348 }; 11349 11350 // Apply to. 11351 var $toggle = $('.mk-toggle-title'); 11352 11353 if(!$toggle.length) return; 11354 11355 $toggle.each(function() { 11356 new Toggle(this); 11357 }); 11358 } 11359 $(window).on('load vc_reload', init); 11360 11361})(jQuery); 11362 11363////////////////////////////////////////////////////////////////////////// 11364// 11365// Init all scripts 11366// 11367////////////////////////////////////////////////////////////////////////// 11368 11369// This is bad but we don't have other access to this scope. 11370// Ajax Portfolio is defined as plugin and on success needs these to be reinited 11371// We'll refactor all of this. 11372window.ajaxInit = function() { 11373 mk_lightbox_init(); 11374 mk_click_events(); 11375 mk_social_share_global(); 11376 // mk_social_share(); 11377 mk_gallery(); 11378 loop_audio_init(); 11379}; 11380 11381window.ajaxDelayedInit = function() { 11382 mk_flexslider_init(); 11383 // mk_portfolio_ajax(); 11384}; 11385 11386$(document).ready(function() { 11387 mk_lightbox_init(); 11388 mk_login_form(); 11389 mk_backgrounds_parallax(); 11390 mk_flexslider_init(); 11391 mk_event_countdown(); 11392 mk_skill_meter(); 11393 mk_milestone(); 11394 mk_ajax_search(); 11395 mk_hover_events(); 11396 mk_portfolio_ajax(); 11397 product_loop_add_cart(); 11398 // mk_social_share(); 11399 mk_portfolio_widget(); 11400 mk_contact_form(); 11401 mk_blog_carousel(); 11402 mk_header_searchform(); 11403 mk_click_events(); 11404 mk_text_typer(); 11405 mk_tab_slider_func(); 11406 mkPositionSidebar(); 11407 11408 $(window).on('load', function() { 11409 mk_unfold_footer(); 11410 mk_tabs(); 11411 mk_accordion_toggles_tooltip(); 11412 mk_gallery(); 11413 mk_theatre_responsive_calculator(); 11414 mk_tabs_responsive(); 11415 mk_start_tour_resize(); 11416 mk_header_social_resize(); 11417 mk_page_section_social_video_bg(); 11418 loop_audio_init(); 11419 mk_one_page_scroller(); 11420 11421 setTimeout(function() { 11422 /* 11423 Somehow the values are not correctly updated for the screens 11424 and we need to put setTimeout to fix the issue 11425 */ 11426 mk_mobile_tablet_responsive_calculator(); 11427 }, 300); 11428 11429 console.log("ready for rock"); 11430 }); 11431 11432 11433 var onDebouncedResize = function() { 11434 mk_theatre_responsive_calculator(); 11435 mk_mobile_tablet_responsive_calculator(); 11436 mk_tabs_responsive(); 11437 mk_accordion_toggles_tooltip(); 11438 mk_start_tour_resize(); 11439 mk_header_social_resize(); 11440 11441 setTimeout(function() { 11442 mk_unfold_footer(); 11443 }, 300); 11444 }; 11445 11446 var debounceResize = null; 11447 $(window).on("resize", function() { 11448 if( debounceResize !== null ) { clearTimeout( debounceResize ); } 11449 debounceResize = setTimeout( onDebouncedResize, 300 ); 11450 }); 11451 11452 var onDebouncedScroll = function() { 11453 mk_skill_meter(); 11454 //TODO: Ask to Bart how we can call javascript component 11455 //mk_charts(); 11456 mk_milestone(); 11457 }; 11458 11459 var debounceScroll = null; 11460 $(window).on("scroll", function() { 11461 if( debounceScroll !== null ) { clearTimeout( debounceScroll ); } 11462 debounceScroll = setTimeout( onDebouncedScroll, 100 ); 11463 }); 11464 11465 if (MK.utils.isMobile()) { 11466 $('body').addClass('no-transform'); 11467 } 11468 11469}); 11470 11471/* VC frontend editor */ 11472/* -------------------------------------------------------------------- */ 11473$(window).on("vc_reload",function () { 11474 mk_flexslider_init(); 11475 loop_audio_init(); 11476 mk_tab_slider_func();
11477 mk_event_countdown(); 11478 videoLoadState(); 11479 mk_page_section_social_video_bg(); 11480 mk_hover_events(); 11481 11482 setTimeout(function() { 11483 mkPositionSidebar(); 11484 }, 200); 11485}); 11486 11487// Replace this if new event for remove found. 11488$( document ).on( 'click', '.vc_control-btn-delete', function() { 11489 $( window ).trigger( 'vc_reload' ); 11490} ); 11491 11492$( document ).on( 'sortupdate', '.ui-sortable', function() { 11493 $( window ).trigger( 'vc_reload' ); 11494} ); 11495 11496/* Typer */ 11497/* -------------------------------------------------------------------- */ 11498function mk_text_typer() { 11499 11500 "use strict"; 11501 11502 $('[data-typer-targets]').each(function() { 11503 var that = this; 11504 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.typed.js' ], function() { 11505 var $this = $(that), 11506 $first_string = [$this.text()], 11507 $rest_strings = $this.attr('data-typer-targets').split(','), 11508 $strings = $first_string.concat($rest_strings); 11509 11510 $this.text(''); 11511 11512 $this.typed({ 11513 strings: $strings, 11514 typeSpeed: 30, // typing speed 11515 backDelay: 1200, // pause before backspacing 11516 loop: true, // loop on or off (true or false) 11517 loopCount: false, // number of loops, false = infinite 11518 }); 11519 }); 11520 }); 11521} 11522 11523 11524 11525/* Tab Slider */ 11526/* -------------------------------------------------------------------- */ 11527 11528function mk_tab_slider_func() { 11529 11530 "use strict"; 11531 11532 $('.mk-tab-slider').each(function() { 11533 var that = this; 11534 11535 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.swiper.js' ], function() { 11536 var $this = $(that), 11537 id = $this.data('id'), 11538 $autoplayTime = $this.data('autoplay'), 11539 $content = $('.mk-slider-content'); 11540 11541 var mk_tab_slider = $this.swiper({ 11542 wrapperClass: 'mk-tab-slider-wrapper', 11543 slideClass: 'mk-tab-slider-item', 11544 calculateHeight: true, 11545 speed: 500, 11546 autoplay: isTest ? false : $autoplayTime, 11547 onSlideChangeStart: function() { 11548 $('.mk-tab-slider-nav[data-id="' + id + '"]').find(".active").removeClass('active') 11549 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").eq(mk_tab_slider.activeIndex).addClass('active') 11550 } 11551 }); 11552 11553 // Simple repaint for firefox issue (can't handle 100% height after plugin init) 11554 function repaintFirefox() { 11555 $content.css('display','block'); 11556 setTimeout(function() { 11557 mk_tab_slider.reInit(); 11558 $content.css('display','table'); 11559 },100); 11560 } 11561 11562 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").first().addClass('active'); 11563 11564 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").on('touchstart mousedown', function(e) { 11565 e.preventDefault() 11566 $('.mk-tab-slider-nav[data-id="' + id + '"]').find(".active").removeClass('active') 11567 $(this).addClass('active') 11568 mk_tab_slider.swipeTo($(this).index()) 11569 }); 11570 11571 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").on('click',function(e) { 11572 e.preventDefault(); 11573 }); 11574 11575 repaintFirefox(); 11576 $(window).on('resize', repaintFirefox); 11577 }); 11578 11579 }); 11580 11581} 11582 11583 11584 11585/* Edge One Pager */ 11586/* -------------------------------------------------------------------- */ 11587function mk_one_page_scroller() { 11588 11589 "use strict"; 11590 11591 $('.mk-edge-one-pager').each(function() { 11592 var self = this; 11593 11594 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.fullpage.js' ], function() { 11595 11596 var $this = $(self), 11597 anchorArr = []; 11598 11599 $this.find('.section').each(function() { 11600 anchorArr.push($(this).attr('data-title')); 11601 }); 11602 11603 var scrollable = true; 11604 $this.find('.section').each(function() { 11605 var $section = $(this), 11606 $content = $section.find('.edge-slide-content'), 11607 sectionHeight = $section.height(),
11608 contentHeight = $content.innerHeight(); 11609 11610 if((contentHeight + 30) > $(window).height()) { 11611 scrollable = false; 11612 } 11613 }); 11614 11615 if(!scrollable){ 11616 $this.find('.section').each(function() { 11617 var $section = $(this); 11618 $section.addClass('active').css({ 11619 'padding-bottom': '50px' 11620 }); 11621 }); 11622 } 11623 11624 if(scrollable) { 11625 $this.fullpage({ 11626 verticalCentered: false, 11627 resize: true, 11628 slidesColor: ['#ccc', '#fff'], 11629 anchors: anchorArr, 11630 scrollingSpeed: 600, 11631 easing: 'easeInQuart', 11632 menu: false, 11633 navigation: true, 11634 navigationPosition: 'right', 11635 navigationTooltips: false, 11636 slidesNavigation: true, 11637 slidesNavPosition: 'bottom', 11638 loopBottom: false, 11639 loopTop: false, 11640 loopHorizontal: true, 11641 autoScrolling: true, 11642 scrollOverflow: false, 11643 css3: true, 11644 paddingTop: 0, 11645 paddingBottom: 0, 11646 normalScrollElements: '.mk-header, .mk-responsive-wrap', 11647 normalScrollElementTouchThreshold: 5, 11648 keyboardScrolling: true, 11649 touchSensitivity: 15, 11650 continuousVertical: false, 11651 animateAnchor: true, 11652 11653 onLeave: function(index, nextIndex, direction) { 11654 var currentSkin = $this.find('.one-pager-slide').eq(nextIndex - 1).attr('data-header-skin'); 11655 MK.utils.eventManager.publish( 'firstElSkinChange', currentSkin ); 11656 $('#fullPage-nav').removeClass('light-skin dark-skin').addClass(currentSkin + '-skin'); 11657 11658 }, 11659 afterRender: function() { 11660 11661 var $nav = $('#fullPage-nav'); 11662 11663 setTimeout(function() { 11664 var currentSkin = $this.find('.one-pager-slide').eq(0).attr('data-header-skin'); 11665 MK.utils.eventManager.publish( 'firstElSkinChange', currentSkin ); 11666 if($nav.length) $nav.removeClass('light-skin dark-skin').addClass(currentSkin + '-skin'); 11667 }, 300); 11668 11669 var $slide = $this.find('.section'), 11670 headerHeight = MK.val.offsetHeaderHeight(0), 11671 windowHeight = $(window).height(); 11672 11673 $slide.height(windowHeight - headerHeight); 11674 11675 if($nav.length) { 11676 $nav.css({ 11677 'top': 'calc(50% + ' + (headerHeight/2) + 'px)', 11678 'marginTop': 0 11679 }); 11680 11681 var style = $this.attr('data-pagination'); 11682 $nav.addClass('pagination-' + style); 11683 } 11684 11685 setTimeout(mk_one_pager_resposnive, 1000); 11686 }, 11687 afterResize: function() { 11688 var $slide = $this.find('.section'), 11689 headerHeight = MK.val.offsetHeaderHeight(0), 11690 windowHeight = $(window).height(); 11691 11692 $slide.height(windowHeight - headerHeight); 11693 11694 $('#fullPage-nav').css({ 11695 'top': 'calc(50% + ' + (headerHeight/2) + 'px)', 11696 'marginTop': 0 11697 }); 11698 11699 setTimeout(mk_one_pager_resposnive, 1000); 11700 console.log('Reposition pager content.'); 11701 }, 11702 }); 11703 } 11704 11705 // Linking to slides available for desktop and mobile scenarios 11706 function swipeTo(href, e) { 11707 href = '_' + href; // ensure a char before # 11708 if (!~href.indexOf('#')) return; 11709 var section = href.split('#')[1]; 11710 if (~anchorArr.indexOf(section)) { 11711 if (typeof e !== 'undefined') e.preventDefault(); 11712 if (scrollable) $.fn.fullpage.moveTo(section); 11713 else MK.utils.scrollToAnchor('[data-title="'+section+'"]'); 11714 } 11715 } 11716 11717 // onload 11718 var loc = window.location; 11719 if(loc.hash) swipeTo(loc.hash); 11720 11721 $(document).on('click', 'a', function(e) { 11722 var $link = $(e.currentTarget); 11723 swipeTo($link.attr('href'), e); 11724 }); 11725 }); 11726 }); 11727 11728 11729 11730} 11731 11732 11733function mk_one_pager_resposnive() { 11734 "use strict"; 11735 11736 $('.mk-edge-one-pager').each(function() { 11737 var $pager = $(this), 11738 headerHeight = MK.val.offsetHeaderHeight(0), 11739 windowHeight = $(window).height() - headerHeight; 11740 11741 $pager.find('.one-pager-slide').each(function() { 11742 var $slide = $(this), 11743 $content = $slide.find('.edge-slide-content'); 11744 11745 if ($slide.hasClass('left_center') || $slide.hasClass('center_center') || $slide.hasClass('right_center')) { 11746 var contentHeight = $content.height(), 11747 distanceFromTop = (windowHeight - contentHeight) / 2; 11748 11749 distanceFromTop = (distanceFromTop < 50) ? 50 + headerHeight : distanceFromTop; 11750 11751 $content.css('marginTop', distanceFromTop); 11752 } 11753 11754 if ($slide.hasClass('left_bottom') || $slide.hasClass('center_bottom') || $slide.hasClass('right_bottom')) { 11755 var distanceFromTop = windowHeight - $content.height() - 90; 11756 $content.css('marginTop', (distanceFromTop)); 11757 } 11758 }); 11759 11760 /** 11761 * Fix AM-2853 11762 * 11763 * @since 6.0.3 11764 * 11765 * At the init of Edge One Pager (EOP), EOP will render all image 11766 * background of each sections from top to bottom. In this case, 11767 * the page height will be more than screen height and the scroll 11768 * bar will appear. At the same time, the full width row will set 11769 * the container width into 100%. But, after all Slides are set up, 11770 * EOP height will be 100% of the screen height, so the scroll bar 11771 * will be disappeared. It's caused spacing issues on the left and 11772 * right side of the EOP container. To fix this, the row width 11773 * should be resized and row position should be readjusted. 11774 */ 11775 var $row = $pager.parents( '.vc_row.vc_row-fluid.mk-fullwidth-true' ); 11776 11777 // Run only if the Edge One Pager is wrapped inside full width row. 11778 if ( $row.length > 0 ) { 11779 // Set the wrapper and row width. 11780 var $wrapper = $( '.mk-main-wrapper-holder' ); 11781 var $grid = $row.children( '.mk-grid' ); 11782 var rowWidth = $row.width(); // Original width.
11783 var wrapperWidth = $wrapper.width(); // The new width. 11784 11785 // Run only if original width is smaller than the new width. 11786 if ( rowWidth >= wrapperWidth || $grid.length > 0 ) { 11787 return; 11788 } 11789 11790 // Get the content left offset. 11791 var $content = $wrapper.find( '.theme-content' ); 11792 var oriPos = $content.position(); 11793 var oriPadLeft = $content.css( 'padding-left' ); 11794 var oriLeft = parseInt( oriPos.left ) + parseInt( oriPadLeft ); 11795 11796 // Ensure the new width and left position is more than 0. 11797 if ( wrapperWidth <= 0 || oriLeft <= 0 ) { 11798 return; 11799 } 11800 11801 // Resize the width and left position of row full width. 11802 $row.css({ 11803 'width': wrapperWidth, 11804 'left': oriLeft * -1, 11805 }); 11806 } 11807 }); 11808} 11809 11810/* Image Gallery */ 11811/* -------------------------------------------------------------------- */ 11812 11813function mk_gallery() { 11814 11815 "use strict"; 11816 11817 $('.mk-gallery .mk-gallery-item.hover-overlay_layer .item-holder').each(function() { 11818 var itemHolder = $(this), 11819 galleryDesc = itemHolder.find('.gallery-desc'); 11820 11821 function updatePosition() { 11822 var parentHeight = itemHolder.outerHeight(), 11823 contentHeight = galleryDesc.innerHeight(); 11824 11825 var paddingVal = (parentHeight - contentHeight) / 2; 11826 galleryDesc.css({ 11827 'top': paddingVal, 11828 // 'padding-bottom': paddingVal 11829 }); 11830 11831 // console.log(parentHeight); 11832 // console.log(contentHeight); 11833 11834 11835 } 11836 updatePosition(); 11837 11838 $(window).on('resize', function() { 11839 setTimeout(function() { 11840 updatePosition(); 11841 }, 1000); 11842 }); 11843 }); 11844 // Execute hover state for mk gallery item 11845 if ($(window).width() <= 1024) { 11846 $('.mk-gallery .mk-gallery-item').on('click', function (e) { 11847 var clicks = $(this).data('clicks'); 11848 if (clicks) { 11849 // First click 11850 $(this).toggleClass('hover-state'); 11851 } else { 11852 // Second click 11853 $(this).toggleClass('hover-state'); 11854 } 11855 $(this).data("clicks", !clicks); 11856 }); 11857 } 11858} 11859 11860/* Theatre Slider Responsive Calculator */ 11861/* -------------------------------------------------------------------- */ 11862 11863function mk_theatre_responsive_calculator() { 11864 var $laptopContainer = $(".laptop-theatre-slider"); 11865 var $computerContainer = $(".desktop-theatre-slider"); 11866 $laptopContainer.each(function() { 11867 var $this = $(this), 11868 $window = $(window), 11869 $windowWidth = $window.outerWidth(), 11870 $windowHeight = $window.outerHeight(), 11871 $width = $this.outerWidth(), 11872 $height = $this.outerHeight(), 11873 $paddingTop = 38, 11874 $paddingRight = 143, 11875 $paddingBottom = 78, 11876 $paddingLeft = 143; 11877 11878 var $player = $this.find('.player-container'); 11879 11880 if ($windowWidth > $width) { 11881 $player.css({ 11882 'padding-left': parseInt(($width * $paddingLeft) / 1200), 11883 'padding-right': parseInt(($width * $paddingRight) / 1200), 11884 'padding-top': parseInt(($height * $paddingTop) / 690), 11885 'padding-bottom': parseInt(($height * $paddingBottom) / 690), 11886 }); 11887 } 11888 11889 }); 11890 11891 $computerContainer.each(function() { 11892 var $this = $(this), 11893 $window = $(window), 11894 $windowWidth = $window.outerWidth(), 11895 $windowHeight = $window.outerHeight(), 11896 $width = $this.outerWidth(), 11897 $height = $this.outerHeight(), 11898 $paddingTop = 60, 11899 $paddingRight = 52, 11900 $paddingBottom = 290, 11901 $paddingLeft = 49; 11902 11903 var $player = $this.find('.player-container'); 11904 11905 if ($windowWidth > $width) { 11906 $player.css({ 11907 'padding-left': parseInt(($width * $paddingLeft) / 1200), 11908 'padding-right': parseInt(($width * $paddingRight) / 1200), 11909 'padding-top': parseInt(($height * $paddingTop) / 969), 11910 'padding-bottom': parseInt(($height * $paddingBottom) / 969), 11911 }); 11912 } 11913 11914 }); 11915 11916} 11917 11918/* Mobile and Tablet Slideshow Responsive Calculator */ 11919/* -------------------------------------------------------------------- */ 11920function mk_mobile_tablet_responsive_calculator() { 11921 var $laptopSlideshow = $(".mk-laptop-slideshow-shortcode"); 11922 var $lcdSlideshow = $(".mk-lcd-slideshow"); 11923 11924 if ($.exists(".mk-laptop-slideshow-shortcode")) { 11925 $laptopSlideshow.each(function() { 11926 var $this = $(this), 11927 $window = $(window), 11928 $windowWidth = $window.outerWidth(), 11929 $windowHeight = $window.outerHeight(), 11930 $width = $this.outerWidth(), 11931 $height = $this.outerHeight(), 11932 $paddingTop = 28, 11933 $paddingRight = 102, 11934 $paddingBottom = 52, 11935 $paddingLeft = 102; 11936 11937 var $player = $this.find(".slideshow-container"); 11938 11939 $player.css({ 11940 "padding-left": parseInt(($width * $paddingLeft) / 836), 11941 "padding-right": parseInt(($width * $paddingRight) / 836), 11942 "padding-top": parseInt(($height * $paddingTop) / 481), 11943 "padding-bottom": parseInt(($height * $paddingBottom) / 481), 11944 }); 11945 11946 }); 11947 } 11948 11949 if ($.exists(".mk-lcd-slideshow")) {
11950 $lcdSlideshow.each(function() { 11951 var $this = $(this), 11952 $window = $(window), 11953 $windowWidth = $window.outerWidth(), 11954 $windowHeight = $window.outerHeight(), 11955 $width = $this.outerWidth(), 11956 $height = $this.outerHeight(), 11957 $paddingTop = 35, 11958 $paddingRight = 39, 11959 $paddingBottom = 213, 11960 $paddingLeft = 36; 11961 11962 var $player = $this.find(".slideshow-container"); 11963 $player.css({ 11964 "padding-left": parseInt(($width * $paddingLeft) / 886), 11965 "padding-right": parseInt(($width * $paddingRight) / 886), 11966 "padding-top": parseInt(($height * $paddingTop) / 713), 11967 "padding-bottom": parseInt(($height * $paddingBottom) / 713), 11968 }); 11969 }); 11970 } 11971} 11972 11973 11974/* Start a tour resize function */ 11975/* -------------------------------------------------------------------- */ 11976function mk_start_tour_resize() { 11977 11978 $('.mk-header-start-tour').each(function() { 11979 11980 var $windowWidth = $(document).width(), 11981 $this = $(this), 11982 $linkWidth = $this.width() + 15, 11983 $padding = ($windowWidth - mk_responsive_nav_width) / 2; 11984 11985 11986 11987 function updateStartTour(){ 11988 if($windowWidth < mk_responsive_nav_width){ 11989 $this.removeClass('hidden'); 11990 $this.addClass('show'); 11991 }else{ 11992 if($padding < $linkWidth){ 11993 $this.removeClass('show'); 11994 $this.addClass('hidden'); 11995 }else{ 11996 $this.removeClass('hidden'); 11997 $this.addClass('show'); 11998 } 11999 } 12000 } 12001 12002 setTimeout(function() { 12003 updateStartTour(); 12004 }, 300); 12005 }); 12006} 12007 12008/* Header social resize function */ 12009/* -------------------------------------------------------------------- */ 12010function mk_header_social_resize() { 12011 12012 $('.mk-header-social.header-section').each(function() { 12013 12014 var $windowWidth = $(document).width(), 12015 $this = $(this), 12016 $linkWidth = $this.width() + 15, 12017 $padding = ($windowWidth - mk_responsive_nav_width) / 2; 12018 12019 12020 12021 function updateStartTour(){ 12022 if($windowWidth < mk_responsive_nav_width){ 12023 $this.removeClass('hidden'); 12024 $this.addClass('show'); 12025 }else{ 12026 if($padding < $linkWidth){ 12027 $this.removeClass('show'); 12028 $this.addClass('hidden'); 12029 }else{ 12030 $this.removeClass('hidden'); 12031 $this.addClass('show'); 12032 } 12033 } 12034 } 12035 12036 setTimeout(function() { 12037 updateStartTour(); 12038 }, 300); 12039 }); 12040} 12041 12042/* Page Section Socail Video Player Controls */ 12043/* -------------------------------------------------------------------- */ 12044 12045function mk_page_section_social_video_bg() { 12046 $(".mk-page-section.social-hosted").each(function() { 12047 var $container = $(this), 12048 $sound = $container.data('sound'), 12049 $source = $container.data('source'), 12050 player, 12051 timer = 1000; 12052 12053 if ( $( 'body' ).hasClass( '.compose-mode' ) ) { 12054 timer = 2000; 12055 } 12056 12057 if ($source == 'youtube') { 12058 var youtube = $container.find('iframe')[0]; 12059 try { 12060 player = new YT.Player(youtube, { 12061 events: { 12062 'onReady': function () { 12063 player.playVideo(); 12064 if($sound == false) { 12065 player.mute(); 12066 } 12067 } 12068 } 12069 }); 12070 12071 12072 } catch (e) { 12073 console.log( e ); 12074 } 12075 } 12076 if ($source == 'vimeo') { 12077 var vimeo = $container.find('iframe')[0]; 12078 player = $f(vimeo); 12079 setTimeout(function() { 12080 player.api('play'); 12081 if($sound === false) {
12082 player.api('setVolume', 0); 12083 } 12084 }, timer); 12085 } 12086 12087 }); 12088} 12089 12090// Pre RequireJS hot bug fixing 12091 12092function videoLoadState() { 12093 $('.mk-section-video video').each(function() { 12094 var mkVideo = this; 12095 12096 mkVideo.play(); 12097 this.onload = fire(); 12098 12099 function fire() { 12100 setTimeout(function() { 12101 $(mkVideo).animate({ 12102 'opacity': 1 12103 }, 300); 12104 }, 1000); 12105 } 12106 }); 12107} 12108videoLoadState(); 12109 12110 12111// Gmap Widget 12112(function($) { 12113 12114 $(window).on('load vc_reload', initialize); 12115 12116 function initialize() { 12117 var $gmap = $('.gmap_widget'); 12118 if($gmap.length && typeof google !== 'undefined') $gmap.each(run); 12119 } 12120 12121 function run() { 12122 var $mapHolder = $(this); 12123 var myLatlng = new google.maps.LatLng($mapHolder.data('latitude'), $mapHolder.data('longitude')); 12124 var mapOptions = $mapHolder.data('options'); 12125 mapOptions.mapTypeId = google.maps.MapTypeId.ROADMAP; 12126 mapOptions.center = myLatlng; 12127 var map = new google.maps.Map(this, mapOptions); 12128 12129 new google.maps.Marker({ 12130 position: myLatlng, 12131 map: map 12132 }); 12133 } 12134 12135}(jQuery)); 12136 12137// Instagram Widget 12138(function($) { 12139 12140 $(window).on('load', function() { 12141 var $feeds = $('.mk-instagram-feeds'); 12142 if($feeds.length) $feeds.each(run); 12143 }); 12144 12145 function run() { 12146 var options = $(this).data('options'); 12147 options.template = '<a class="featured-image '+options.tmp_col+'-columns" href="{{link}}" target="_'+options.tmp_target+'"><div class="item-holder"><img src="{{image}}" /><div class="image-hover-overlay"></div></div></a>'; 12148 var feed = new Instafeed(options); 12149 feed.run(); 12150 } 12151}(jQuery)); 12152 12153 12154// Flipbox backface visibility fix for chrome 12155(function($) { 12156 $(window).on('load', function() { 12157 setTimeout( function() { 12158 $('.chrome-flipbox-backface-fix').removeClass('chrome-flipbox-backface-fix'); 12159 }, 300); 12160 }); 12161}(jQuery)); 12162 12163 12164/* Product in VC Tab Bug Fix 12165/* -------------------------------------------------------------------- */ 12166(function($) { 12167 $(window).on('load', function() { 12168 $('.vc_tta-tab a').on('click', function() { 12169 setTimeout( function() { 12170 $(window).trigger('resize'); 12171 }, 100); 12172 }); 12173 }); 12174}(jQuery)); 12175 12176 12177/* Vertical menu fix when childrens exceed screen height 12178/* -------------------------------------------------------------------- */ 12179(function($) { 12180 $(window).on('load', function() { 12181 $('#mk-vm-menu .menu-item-has-children, #mk-vm-menu .mk-vm-back').on('mouseenter', function() { 12182 var $header_inner = $(this).closest('.mk-header-inner'), 12183 $header_inner_height = $header_inner.outerHeight(), 12184 $header_bg = $header_inner.find('.mk-header-bg'), 12185 total_height = 0; 12186 $header_bg.css('height', '100%'); 12187 setTimeout( function() { 12188 $header_inner.children(':visible').each(function() { 12189 total_height += $(this).outerHeight(true); 12190 }); 12191 total_height -= $header_bg.height(); 12192 if ( total_height < $header_inner_height ) { 12193 $header_bg.css('height', '100%'); 12194 } else { 12195 $header_bg.css('height', total_height + 'px'); 12196 } 12197 }, 600); 12198 }); 12199 }); 12200}(jQuery)); 12201 12202 12203/* Woocommerce varitions lightbox fix 12204/* -------------------------------------------------------------------- */ 12205(function($) { 12206 $(window).on('load', function() { 12207 12208 var $variations_form = $('.variations_form'); 12209 12210 if ( $variations_form.length ) { 12211 12212 var $varitions_selects = $variations_form.find('.variations').find('.value').find('select'); 12213 $varitions_selects.on('change', function() { 12214 12215 // Woocommerce variations lightbox with galleries 12216 var $all_img_container = $('.mk-product-image .mk-woocommerce-main-image'); 12217 if ( $all_img_container.length ) { 12218 $( $all_img_container ).each( set_lightbox_href ); 12219 } 12220 12221 }); 12222 $varitions_selects.trigger('change'); 12223 12224 } 12225 12226 }); 12227 12228 function set_lightbox_href() { 12229 12230 var $product_img = $( this ).find( 'img' ), 12231 $lightbox = $( this ).find( '.mk-lightbox' ); 12232 12233 setTimeout( function() { 12234 var image_url = $product_img.attr( 'src' ), 12235 image_suffix = image_url.substr( image_url.lastIndexOf('.') - image_url.length ), // Get image suffix 12236 image_url = image_url.slice( 0 , image_url.lastIndexOf('-') ); // Remove image size 12237 $lightbox.attr('href', image_url + image_suffix ); 12238 }, 300); 12239 } 12240 12241}(jQuery)); 12242 12243 12244/* Remove video section when on mobile */ 12245/* -------------------------------------------------------------------- */ 12246(function($) { 12247 if ( MK.utils.isMobile() ) {
12248 $('.mk-section-video video').remove(); 12249 } 12250}(jQuery)); 12251 12252 12253/* Yith AJAX Product Filter & Yith Infinite Scrolling Plugin Fix 12254/* -------------------------------------------------------------------- */ 12255(function($) { 12256 $(window).on('load', function() { 12257 12258 $(document).on( 'yith-wcan-ajax-filtered yith_infs_added_elem yith-wcan-ajax-reset-filtered', function(){ 12259 setTimeout( function() { 12260 MK.utils.eventManager.publish('ajaxLoaded'); 12261 MK.core.initAll( document ); 12262 }, 1000 ); 12263 }); 12264 12265 // Fixed YITH Filter plugin causes issue for dropdown sort 12266 $(document).on( 'yith-wcan-ajax-filtered yith-wcan-ajax-reset-filtered', function(){ 12267 setTimeout( function() { 12268 $( '.woocommerce-ordering' ).on( 'change', 'select.orderby', function() { 12269 $( this ).closest( 'form' ).submit(); 12270 }); 12271 }, 1000 ); 12272 }); 12273 12274 }); 12275}(jQuery)); 12276 12277 12278/* Toggle loading state in URL for anchor links. 12279 * - Add a filter to escape meta-chars from hash string. 12280/* -------------------------------------------------------------------- */ 12281!function(e){var a=window.location,n=a.hash;if(n.length&&n.substring(1).length){var hSuf = n.substring(1).replace(/[!"#$%&'()*+,./:;<=>?@[\]^`{|}~]/g, "\\$&");var r=e(".vc_row, .mk-main-wrapper-holder, .mk-page-section, #comments"),t=r.filter("#"+hSuf);if(!t.length)return;n=n.replace("!loading","");var i=n+"!loading";a.hash=i}}(jQuery); 12282 12283 12284/* Determine the top spacing of sidebar for full-width page section & row. 12285/* -------------------------------------------------------------------- */ 12286function mkPositionSidebar() { 12287 12288 var themeContent = $( '.theme-content' ), 12289 lastFullWidthChild = themeContent.find( '.vc_row-full-width' ).last(), 12290 top, 12291 sidebar = $( '#theme-page > .mk-main-wrapper-holder > .theme-page-wrapper > #mk-sidebar' ); 12292 12293 if ( ! lastFullWidthChild.length ) { 12294 sidebar.removeAttr( 'style' ); 12295 return; 12296 } 12297 12298 top = lastFullWidthChild.offset().top - themeContent.offset().top; 12299 sidebar.css( 'padding-top', top ); 12300} 12301 12302function mk_accordion_toggles_tooltip() { 12303 12304 "use strict"; 12305 12306 12307 /* Message Boxes */ 12308 /* -------------------------------------------------------------------- */ 12309 12310 $('.box-close-btn').on('click', function() { 12311 $(this).parent().fadeOut(300); 12312 return false; 12313 12314 }); 12315 12316} 12317 12318function mk_portfolio_ajax() { 12319 "use strict"; 12320 12321 var headerHeight = 0; 12322 if ($.exists("#wpadminbar")) { 12323 headerHeight += $("#wpadminbar").height(); 12324 } 12325 if (!$.exists('.mk-vm-menuwrapper')) { 12326 headerHeight += parseInt($('.mk-header').attr('data-sticky-height')); 12327 } 12328 12329 function init() { 12330 var $portfolio = $('.portfolio-grid.portfolio-ajax-enabled'); 12331 if (!$portfolio.length) return; // quit if no portfolio wrappers 12332 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.ajax.portfolio.js' ], function() { 12333 // wait for ajax response propagation and insertion 12334 setTimeout(function() { 12335 $portfolio.each( function() { 12336 $(this).ajaxPortfolio({ extraOffset: headerHeight }); 12337 }); 12338 }, 100); 12339 }); 12340 } 12341 12342 init(); 12343 12344 // Reinit when ajax loaded stuff 12345 // We have global reinit on Components. so move the ajaxPortfolio to component namespace as a standalone module to get it right. 12346 MK.utils.eventManager.subscribe('ajaxLoaded', init); 12347} 12348 12349/** 12350 * Ajax Search on header nav. 12351 */ 12352function mk_ajax_search() { 12353 "use strict"; 12354 12355 // Check if ajax search box enabled. 12356 if (mk_ajax_search_option !== "beside_nav") { 12357 return; 12358 } 12359 12360 // Set minimum length of text to get ajax result. 12361 var minimumLengthToSearch = 3; 12362 var $mkAjaxSearchInput = $('#mk-ajax-search-input'); 12363 var security = $mkAjaxSearchInput.siblings('input[name="security"]').val(); 12364 var wpHttpReferer = $mkAjaxSearchInput.siblings('input[name="_wp_http_referer"]').val(); 12365 var querySpliter = (ajaxurl.indexOf('?') > -1) ? '&' : '?'; 12366 var ul = document.getElementById('mk-nav-search-result'); 12367 var searchTerm; // Search term 12368 var requestCounter = 0; 12369 var responseCounter = 0; 12370 12371 // Add listener for getting result quickly while user entering text, past text and etc. 12372 $mkAjaxSearchInput.on('paste input propertychange', onSearchBoxInput); 12373 12374 /** 12375 * Callback function for entering search term listener (e.g. keyup). 12376 * It will generate AJAX request to get data. 12377 * 12378 * @param e 12379 */ 12380 function onSearchBoxInput(e) { 12381 var target = e.target || e.srcElement; 12382 var newValue = target.value; 12383 12384 // Check if user change the value of text box 12385 if (searchTerm !== newValue) { 12386 searchTerm = newValue; 12387 ul.innerHTML = ''; 12388 if (searchTerm.length >= minimumLengthToSearch) { 12389 // Show loading icon 12390 $mkAjaxSearchInput.addClass('ajax-searching'); 12391 requestCounter++; 12392 $.getJSON(ajaxurl + querySpliter + 'callback=?&action=mk_ajax_search&security=' + security + '&_wp_http_referer=' + wpHttpReferer + '&term=' + searchTerm) 12393 .done(showSearchResult) 12394 .fail(showErrorMessage); 12395 } 12396 } 12397 } 12398 12399 /** 12400 * Callback function for the response of the search request. 12401 * 12402 * @param data 12403 */ 12404 function showSearchResult(data) { 12405 responseCounter++; 12406 12407 // Only show response of latest request. 12408 if (isCorrectResponse()) { 12409 if (data.length > 0) { 12410 // Append result to result container 12411 for (var i = 0; i < data.length; i++) { 12412 var item = data[i]; 12413 $('<li>').append('<a href="' + item.link + '">' + item.image + '<span class="search-title">' + item.label + '</span><span class="search-date">' + item.date + '</span></a>').appendTo(ul); 12414 } 12415 } else { 12416 // Show no result message 12417 ul.innerHTML = '<li class="mk-nav-search-result-zero">No Result.</li>'; 12418 } 12419 12420 // Hide loading icon 12421 $mkAjaxSearchInput.parent('form').removeClass('ajax-searching').addClass('ajax-search-complete'); 12422 } 12423 } 12424 12425 /** 12426 * If any errors, we need a handler to show error message. 12427 */ 12428 function showErrorMessage() { 12429 responseCounter++; 12430 12431 if (isCorrectResponse()) { 12432 ul.innerHTML = '<li class="mk-nav-search-error-message">Can not search! Please try again.</li>'; 12433 } 12434 } 12435 12436 /** 12437 * Check if the response is right for the request. we show latest response only. 12438 * 12439 * @returns {boolean} 12440 */ 12441 function isCorrectResponse() { 12442 return requestCounter === responseCounter; 12443 } 12444}
12445/* Background Parallax Effects */ 12446/* -------------------------------------------------------------------- */ 12447 12448function mk_backgrounds_parallax() { 12449 12450 "use strict"; 12451 12452 if (mk_header_parallax == true) { 12453 $('.mk-header-bg').addClass('mk-parallax-enabled'); 12454 } 12455 if (mk_body_parallax == true) { 12456 $('body').addClass('mk-parallax-enabled'); 12457 } 12458 if (mk_banner_parallax == true) { 12459 $('.mk-header').addClass('mk-parallax-enabled'); 12460 } 12461 if (mk_footer_parallax == true) { 12462 $('#mk-footer').addClass('mk-parallax-enabled'); 12463 } 12464 12465 $('.mk-parallax-enabled').each(function () { 12466 var $this = $( this ); 12467 if (!MK.utils.isMobile()) { 12468 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.parallax.js' ], function() { 12469 $this.parallax("49%", 0.3); 12470 }); 12471 } 12472 }); 12473 12474 $('.mk-fullwidth-slideshow.parallax-slideshow').each(function () { 12475 var $this = $( this ); 12476 if (!MK.utils.isMobile()) { 12477 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.parallax.js' ], function() { 12478 var speed_factor = $this.attr('data-speedFactor'); 12479 $this.parallax("49%", speed_factor); 12480 }); 12481 } 12482 }); 12483 12484} 12485/* Blog, Portfolio Audio */ 12486/* -------------------------------------------------------------------- */ 12487 12488function loop_audio_init() { 12489 if ($.exists('.jp-jplayer')) { 12490 $('.jp-jplayer.mk-blog-audio').each(function () { 12491 var $this = $( this ); 12492 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.jplayer.js' ], function() { 12493 var css_selector_ancestor = "#" + $this.siblings('.jp-audio').attr('id'); 12494 var ogg_file, mp3_file, mk_theme_js_path; 12495 ogg_file = $this.attr('data-ogg'); 12496 mp3_file = $this.attr('data-mp3'); 12497 $this.jPlayer({ 12498 ready: function () { 12499 $this.jPlayer("setMedia", { 12500 mp3: mp3_file, 12501 ogg: ogg_file 12502 }); 12503 }, 12504 play: function () { // To avoid both jPlayers playing together. 12505 $this.jPlayer("pauseOthers"); 12506 }, 12507 swfPath: mk_theme_js_path, 12508 supplied: "mp3, ogg", 12509 cssSelectorAncestor: css_selector_ancestor, 12510 wmode: "window" 12511 }); 12512 }); 12513 }); 12514 } 12515} 12516 12517 12518/* Blog Loop Carousel Shortcode */ 12519/* -------------------------------------------------------------------- */ 12520 12521 12522function mk_blog_carousel() { 12523 12524 "use strict"; 12525 12526 if (!$.exists('.mk-blog-showcase')) { 12527 return; 12528 } 12529 $('.mk-blog-showcase ul li').each(function () { 12530 12531 $(this).mouseenter( function () { 12532 12533 $(this).siblings('li').removeClass('mk-blog-first-el').end().addClass('mk-blog-first-el'); 12534 12535 }); 12536 12537 }); 12538 12539 12540} 12541 12542 12543 12544 12545/** 12546 * Contact Form 12547 * 12548 * Mostly implemented in Vanilla JS instead of jQuery. 12549 */ 12550function mk_contact_form() { 12551 "use strict"; 12552 var mkContactForms = document.getElementsByClassName('mk-contact-form'); 12553 if (mkContactForms.length === 0) { 12554 return; 12555 } 12556 var captchaImageHolder = $('.captcha-image-holder'); 12557 var activeClassName = 'is-active'; 12558 var invalidClassName = 'mk-invalid'; 12559 for (var i = 0; i < mkContactForms.length; i++) { 12560 initializeForm(mkContactForms[i], activeClassName, invalidClassName); 12561 } 12562 if (captchaImageHolder.length > 0) { 12563 $(window).on('load', initializeCaptchas); 12564 } 12565 /** 12566 * Initialize mk forms. e.g add activeClassName for inputs. 12567 * 12568 * @param form 12569 * @param activeClassName 12570 * @param invalidClassName 12571 */ 12572 function initializeForm(form, activeClassName, invalidClassName) { 12573 var inputs = getFormInputs(form); 12574 for (var i = 0; i < inputs.length; i++) { 12575 markActiveClass(inputs[i]); 12576 } 12577 form.addEventListener('submit', function(e) { 12578 validateForm(e, invalidClassName); 12579 }); 12580 /** 12581 * Set activeClassName for the parent node of the inout 12582 */ 12583 function setActiveClass() { 12584 addClass(this.parentNode, activeClassName); 12585 } 12586 /** 12587 * Unset activeClassName from the parent node of the input. 12588 * We need to unset activeClassName only if the data was empty. 12589 * e.g. in the line style of the mk-contact-form, we set labels position based on activeClassName. 12590 */ 12591 function unsetActiveClass() { 12592 if (this.value === '') { 12593 removeClass(this.parentNode, activeClassName); 12594 } 12595 } 12596 /** 12597 * Add event listeners (focus,blur) for input to set and unset activeClassName. 12598 * 12599 * @param input 12600 */ 12601 function markActiveClass(input) { 12602 input.addEventListener('focus', setActiveClass); 12603 input.addEventListener('blur', unsetActiveClass); 12604 } 12605 } 12606 /** 12607 * Validate form when it's submitted. If everything was valid, we with post form in ajax request. 12608 * 12609 * @param e 12610 * @param invalidClassName 12611 */ 12612 function validateForm(e, invalidClassName) { 12613 e.preventDefault(); 12614 var form = e.target || e.srcElement; 12615 var inputs = getFormInputs(form); 12616 var isValidForm = true; 12617 var hasCaptchaField = false;
12618 for (var i = 0; i < inputs.length; i++) { 12619 var input = inputs[i]; 12620 input.value = String(input.value).trim(); 12621 switch (input.type) { 12622 case 'hidden': 12623 break; 12624 case 'checkbox': 12625 isValidForm = validateCheckBox(input, invalidClassName) && isValidForm; 12626 break; 12627 case 'email': 12628 isValidForm = validateEmail(input, invalidClassName) && isValidForm; 12629 break; 12630 case 'textarea': 12631 isValidForm = validateText(input, invalidClassName) && isValidForm; 12632 break; 12633 case 'text': 12634 /** 12635 * Some old browsers such as IE 8 and 9 detect email type as text. 12636 * So, we need to extra check for data-type attribute 12637 */ 12638 if (input.dataset.type === 'captcha') { 12639 isValidForm = validateText(input, invalidClassName) && isValidForm; 12640 hasCaptchaField = true; 12641 } else if (input.dataset.type === 'email') { 12642 isValidForm = validateEmail(input, invalidClassName) && isValidForm; 12643 } else { 12644 isValidForm = validateText(input, invalidClassName) && isValidForm; 12645 } 12646 break; 12647 default: 12648 /** 12649 * e.g. validating for radiobox, selectbox and etc. 12650 */ 12651 console.warn('Implement validation for ' + input.name + ':' + input.type); 12652 break; 12653 } 12654 } 12655 if (isValidForm) { 12656 if (hasCaptchaField) { 12657 validateCaptcha(form, invalidClassName, sendForm); 12658 } else { 12659 sendForm(form); 12660 } 12661 } 12662 } 12663 /** 12664 * Validate captcha of form. If everything was, we will execute captchaIsValidCallback which as sendForm(). 12665 * 12666 * @param form 12667 * @param invalidClassName 12668 * @param captchaIsValidCallback 12669 * @returns boolean 12670 */ 12671 function validateCaptcha(form, invalidClassName, captchaIsValidCallback) { 12672 var input = form.querySelectorAll('[data-type="captcha"]')[0]; 12673 if (input.value.length === 0) { 12674 addClass(input, invalidClassName); 12675 return false; 12676 } else { 12677 window.get.captcha(input.value).done(function(data) { 12678 loadCaptcha(); 12679 input.value = ''; 12680 if (data !== 'ok') { 12681 addClass(input, invalidClassName); 12682 addClass(input, 'contact-captcha-invalid'); 12683 removeClass(input, 'contact-captcha-valid'); 12684 input.placeholder = mk_captcha_invalid_txt; 12685 } else { 12686 removeClass(input, invalidClassName); 12687 removeClass(input, 'contact-captcha-invalid'); 12688 addClass(input, 'contact-captcha-valid'); 12689 input.placeholder = mk_captcha_correct_txt; 12690 captchaIsValidCallback(form); 12691 } 12692 }); 12693 } 12694 } 12695 /** 12696 * Send submitted form. 12697 * 12698 * @param form 12699 */ 12700 function sendForm(form) { 12701 var $form = $(form); 12702 var data = getFormData(form); 12703 progressButton.loader($form); 12704 $.post(ajaxurl, data, function(response) { 12705 var res = JSON.parse(response); 12706 if (res.action_Status) { 12707 progressButton.success($form); 12708 $form.find('.text-input').val(''); 12709 $form.find('textarea').val(''); 12710 $form.find('input[type=checkbox]').attr("checked", false); 12711 $form.find('.contact-form-message').slideDown().addClass('state-success').html(res.message); 12712 setTimeout(function() { 12713 $form.find('.contact-form-message').slideUp(); 12714 }, 5000); 12715 } else { 12716 progressButton.error($form); 12717 $form.find('.contact-form-message').removeClass('state-success').html(res.message); 12718 } 12719 }); 12720 } 12721 /** 12722 * Initialize all captcha images for first time. All captcha images is always same. e.g. if we have multiple form, 12723 * all of them will have the same image. 12724 * It will also add event listener for '.captcha-change-image' objects to reload the captcha. 12725 */ 12726 function initializeCaptchas() { 12727 var captchaChangeImageButtons = document.getElementsByClassName('captcha-change-image'); 12728 for (var i = 0; i < captchaChangeImageButtons.length; i++) { 12729 captchaChangeImageButtons[i].addEventListener('click', loadCaptcha); 12730 } 12731 } 12732 /** 12733 * Load captcha text and append the image to captcha container. 12734 * If it used as a callback, it will prevent default behave of the event. 12735 * e.g. loading new captcha by click on <a> without changing url. 12736 */ 12737 function loadCaptcha(e) { 12738 if (e) { 12739 e.preventDefault(); 12740 } 12741 $.post(ajaxurl, { 12742 action: 'mk_create_captcha_image' 12743 }, appendImage); 12744 /** 12745 * The callback function for append or change old image src based on response. T 12746 * The captchaImageURL is the url of the captcha which is provided in ajax response of mk_create_captcha_image. 12747 * @param captchaImageURL 12748 */ 12749 function appendImage(captchaImageURL) { 12750 if (captchaImageHolder.find('.captcha-image').length === 0) { 12751 captchaImageHolder.html('<img src="' + captchaImageURL + '" class="captcha-image" alt="captcha txt">'); 12752 } else { 12753 captchaImageHolder.find('.captcha-image').attr("src", captchaImageURL + '?' + new Date().getTime()); 12754 } 12755 } 12756 } 12757 /** 12758 * Get form inputs using querySelectorAll(). 12759 * It returns <input> and <textarea> tags. If you need any other tags such as <select>, please update this function. 12760 * 12761 * @param form 12762 * @returns {NodeList} 12763 */ 12764 function getFormInputs(form) { 12765 return form.querySelectorAll('input,textarea'); 12766 } 12767 /** 12768 * Get data of the form inputs and textareas as a object. 12769 * 12770 * @param form 12771 * @returns {{action: string}} 12772 */ 12773 function getFormData(form) { 12774 var data = { 12775 action: 'mk_contact_form' 12776 }; 12777 var inputs = getFormInputs(form); 12778 for (var i = 0; i < inputs.length; i++) {
12779 data[inputs[i].name] = inputs[i].value; 12780 } 12781 return data; 12782 } 12783} 12784/* Ajax Login Form */ 12785/* -------------------------------------------------------------------- */ 12786function mk_login_form() { 12787 $('form.mk-login-form').each(function() { 12788 var $this = $(this); 12789 $this.on('submit', function(e) { 12790 $('p.mk-login-status', $this).show().text(ajax_login_object.loadingmessage); 12791 $.ajax({ 12792 type: 'POST', 12793 dataType: 'json', 12794 url: ajax_login_object.ajaxurl, 12795 data: { 12796 'action': 'ajaxlogin', 12797 'username': $('#username', $this).val(), 12798 'password': $('#password', $this).val(), 12799 'security': $('#security', $this).val() 12800 }, 12801 success: function(data) { 12802 $('p.mk-login-status', $this).text(data.message); 12803 if (data.loggedin === true) { 12804 document.location.href = ajax_login_object.redirecturl; 12805 } 12806 } 12807 }); 12808 e.preventDefault(); 12809 }); 12810 }); 12811} 12812/* Progress Button */ 12813/* -------------------------------------------------------------------- */ 12814var progressButton = { 12815 loader: function(form) { 12816 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12817 var $form = form, 12818 progressBar = $form.find(".mk-progress-button .mk-progress-inner"), 12819 buttonText = $form.find(".mk-progress-button .mk-progress-button-content"), 12820 progressButton = new TimelineLite(); 12821 progressButton.to(progressBar, 0, { 12822 width: "100%", 12823 scaleX: 0, 12824 scaleY: 1 12825 }).to(buttonText, .3, { 12826 y: -5 12827 }).to(progressBar, 1.5, { 12828 scaleX: 1, 12829 ease: Power2.easeInOut 12830 }, "-=.1").to(buttonText, .3, { 12831 y: 0 12832 }).to(progressBar, .3, { 12833 scaleY: 0 12834 }); 12835 }); 12836 }, 12837 success: function(form) { 12838 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12839 var $form = form, 12840 buttonText = $form.find(".mk-button .mk-progress-button-content, .mk-contact-button .mk-progress-button-content"), 12841 successIcon = $form.find(".mk-progress-button .state-success"), 12842 progressButtonSuccess = new TimelineLite({ 12843 onComplete: hideSuccessMessage 12844 }); 12845 progressButtonSuccess.to(buttonText, .3, { 12846 paddingRight: 20, 12847 ease: Power2.easeInOut 12848 }, "+=1").to(successIcon, .3, { 12849 opacity: 1 12850 }).to(successIcon, 2, { 12851 opacity: 1 12852 }); 12853 12854 function hideSuccessMessage() { 12855 progressButtonSuccess.reverse() 12856 } 12857 }); 12858 }, 12859 error: function(form) { 12860 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12861 var $form = form, 12862 buttonText = $form.find(".mk-button .mk-progress-button-content, .mk-contact-button .mk-progress-button-content"), 12863 errorIcon = $form.find(".mk-progress-button .state-error"), 12864 progressButtonError = new TimelineLite({ 12865 onComplete: hideErrorMessage 12866 }); 12867 progressButtonError.to(buttonText, .3, { 12868 paddingRight: 20 12869 }, "+=1").to(errorIcon, .3, { 12870 opacity: 1 12871 }).to(errorIcon, 2, { 12872 opacity: 1 12873 }); 12874 12875 function hideErrorMessage() { 12876 progressButtonError.reverse() 12877 } 12878 }); 12879 } 12880}; 12881function mk_click_events() { 12882 "use strict"; 12883 12884 var eventtype = 'click'; 12885 12886 $(".mk-header-login, .mk-header-signup, .mk-side-dashboard, .mk-quick-contact-wrapper, .mk-dashboard-trigger, .blog-share-container, .news-share-buttons, .main-nav-side-search, #mk-fullscreen-search-wrapper, #fullscreen-navigation").on(eventtype, function(event) { 12887 if (event.stopPropagation) { 12888 event.stopPropagation(); 12889 } else if (window.event) { 12890 window.event.cancelBubble = true; 12891 } 12892 }); 12893 $("html").on(eventtype, function() {
12894 $(".mk-login-register, .mk-header-subscribe, #mk-quick-contact, .single-share-buttons, .single-share-box, .blog-social-share, .news-share-buttons, #mk-nav-search-wrapper").fadeOut(300); 12895 $('.mk-quick-contact-link').removeClass('quick-contact-active'); 12896 // Removed By Maki for repairing fullnav scroll issue. Hope it odesnt break anything 12897 // $('body').css('overflow', 'visible'); 12898 }); 12899 12900 $('.mk-fullscreen-search-overlay').on(eventtype,function(){ 12901 $(this).removeClass('mk-fullscreen-search-overlay-show'); 12902 }); 12903 12904 $('.mk-forget-password').on(eventtype, function() { 12905 $('.mk-forget-panel').siblings().hide().end().show(); 12906 }); 12907 12908 $('.mk-create-account').on(eventtype, function() { 12909 $('#mk-register-panel').siblings().hide().end().show(); 12910 }); 12911 12912 $('.mk-return-login').on(eventtype, function() { 12913 $('#mk-login-panel').siblings().hide().end().show(); 12914 }); 12915 12916 12917 $('.mk-quick-contact-link').on(eventtype, function() { 12918 var $this = $(this), 12919 $quickContact = $('#mk-quick-contact'); 12920 if (!$this.hasClass('quick-contact-active')) { 12921 $quickContact.addClass('quick-contact-anim').fadeIn(250); 12922 $this.addClass('quick-contact-active'); 12923 } else { 12924 $quickContact.removeClass('quick-contact-anim').fadeOut(100); 12925 $this.removeClass('quick-contact-active'); 12926 } 12927 return false; 12928 }); 12929 12930} 12931 12932function mk_social_share_global() { 12933 12934 "use strict"; 12935 12936 var eventtype = 'click'; 12937 12938 $('.twitter-share').on(eventtype, function() { 12939 var $this = $(this), 12940 $url = $this.attr('data-url'), 12941 $title = $this.attr('data-title'); 12942 12943 window.open('http://twitter.com/intent/tweet?text=' + $title + ' ' + $url, "twitterWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 12944 return false; 12945 }); 12946 12947 $('.pinterest-share').on(eventtype, function() { 12948 var $this = $(this), 12949 $url = $this.attr('data-url'), 12950 $title = $this.attr('data-title'), 12951 $image = $this.attr('data-image'); 12952 window.open('http://pinterest.com/pin/create/button/?url=' + $url + '&media=' + $image + '&description=' + $title, "twitterWindow", "height=320,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 12953 return false; 12954 }); 12955 12956 $('.facebook-share').on(eventtype, function() { 12957 var $url = $(this).attr('data-url'); 12958 window.open('https://www.facebook.com/sharer/sharer.php?u=' + $url, "facebookWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 12959 return false; 12960 }); 12961 12962 $('.googleplus-share').on(eventtype, function() { 12963 var $url = $(this).attr('data-url'); 12964 window.open('https://plus.google.com/share?url=' + $url, "googlePlusWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 12965 return false; 12966 }); 12967 12968 $('.linkedin-share').on(eventtype, function() { 12969 var $this = $(this), 12970 $url = $this.attr('data-url'), 12971 $title = $this.attr('data-title'), 12972 $desc = $this.attr('data-desc'); 12973 window.open('http://www.linkedin.com/shareArticle?mini=true&url=' + $url + '&title=' + $title + '&summary=' + $desc, "linkedInWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 12974 return false; 12975 }); 12976} 12977 12978 12979/* Event Count Down */ 12980/* -------------------------------------------------------------------- */ 12981 12982function mk_event_countdown() { 12983 if ($.exists('.mk-event-countdown')) { 12984 12985 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.countdown.js' ], function() { 12986 12987 $('.mk-event-countdown').each(function () { 12988 if (isTest) return; 12989 var $this = $(this), 12990 $date = $this.attr('data-date'), 12991 $offset = $this.attr('data-offset'); 12992 12993 $this.downCount({ 12994 date: $date, 12995 offset: $offset 12996 }); 12997 }); 12998 12999 }); 13000 } 13001} 13002/* Flexslider init */ 13003/* -------------------------------------------------------------------- */ 13004 13005function mk_flexslider_init() { 13006 13007 var $lcd = $('.mk-lcd-slideshow'), 13008 $laptop = $('.mk-laptop-slideshow-shortcode'); 13009 13010 if($lcd.length) $lcd.find('.mk-lcd-image').fadeIn(); 13011 if($laptop.length) $laptop.find(".mk-laptop-image").fadeIn(); 13012 13013 $('.js-flexslider').each(function () { 13014 13015 if ($(this).parents('.mk-tabs').length || $(this).parents('.mk-accordion').length) { 13016 $(this).removeData("flexslider"); 13017 } 13018 13019 var $this = $(this), 13020 $selector = $this.attr('data-selector'), 13021 $animation = $this.attr('data-animation'), 13022 $easing = $this.attr('data-easing'), 13023 $direction = $this.attr('data-direction'), 13024 $smoothHeight = $this.attr('data-smoothHeight') == "true" ? true : false,
13025 $slideshowSpeed = $this.attr('data-slideshowSpeed'), 13026 $animationSpeed = $this.attr('data-animationSpeed'), 13027 $controlNav = $this.attr('data-controlNav') == "true" ? true : false, 13028 $directionNav = $this.attr('data-directionNav') == "true" ? true : false, 13029 $pauseOnHover = $this.attr('data-pauseOnHover') == "true" ? true : false, 13030 $isCarousel = $this.attr('data-isCarousel') == "true" ? true : false; 13031 13032 13033 if ($selector !== undefined) { 13034 var $selector_class = $selector; 13035 } else { 13036 var $selector_class = ".mk-flex-slides > li"; 13037 } 13038 13039 if ($isCarousel === true) { 13040 var $itemWidth = parseInt($this.attr('data-itemWidth')), 13041 $itemMargin = parseInt($this.attr('data-itemMargin')), 13042 $minItems = parseInt($this.attr('data-minItems')), 13043 $maxItems = parseInt($this.attr('data-maxItems')), 13044 $move = parseInt($this.attr('data-move')); 13045 } else { 13046 var $itemWidth = $itemMargin = $minItems = $maxItems = $move = 0; 13047 } 13048 13049 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.flexslider.js' ], function() { 13050 $this.flexslider({ 13051 selector: $selector_class, 13052 animation: $animation, 13053 easing: $easing, 13054 direction: $direction, 13055 smoothHeight: $smoothHeight, 13056 slideshow: !isTest, // autoplay 13057 slideshowSpeed: $slideshowSpeed, 13058 animationSpeed: $animationSpeed, 13059 controlNav: $controlNav, 13060 directionNav: $directionNav, 13061 pauseOnHover: $pauseOnHover, 13062 prevText: "", 13063 nextText: "", 13064 itemWidth: $itemWidth, 13065 itemMargin: $itemMargin, 13066 minItems: $minItems, 13067 maxItems: $maxItems, 13068 move: $move 13069 }); 13070 }); 13071 13072 }); 13073 13074} 13075 13076/* Header Search Form */ 13077/* -------------------------------------------------------------------- */ 13078 13079function mk_header_searchform() { 13080 13081 $('.mk-search-trigger').on('click',function(){ 13082 setTimeout(function() { 13083 $('#mk-ajax-search-input').focus(); 13084 },500); 13085 }); 13086 13087 "use strict"; 13088 13089 $('.mk-header-toolbar .mk-header-searchform .text-input').on('focus', function () { 13090 13091 if ($('.mk-header-toolbar .mk-header-searchform .text-input').hasClass('on-close-state')) { 13092 $('.mk-header-toolbar .mk-header-searchform .text-input').removeClass('on-close-state').animate({ 13093 'width': '200px' 13094 }, 200); 13095 return false; 13096 } 13097 }); 13098 13099 $(".mk-header-toolbar .mk-header-searchform").on('click',function (event) { 13100 if (event.stopPropagation) { 13101 event.stopPropagation(); 13102 } else if (window.event) { 13103 window.event.cancelBubble = true; 13104 } 13105 }); 13106 13107 13108 $("html").on('click',function () { 13109 $(this).find(".mk-header-toolbar .mk-header-searchform .text-input").addClass('on-close-state').animate({ 13110 'width': 90 13111 }, 300); 13112 }); 13113 13114 // Fix search for edge browser 13115 if(MK.utils.browser.name === 'Edge') { 13116 $('#mk-fullscreen-search-input').on('keydown', function(e) { 13117 if (e.which == 13) { 13118 e.preventDefault(); 13119 $('#mk-fullscreen-searchform').submit(); 13120 } 13121 }); 13122 } 13123 13124} 13125 13126 13127 13128 13129/* Hover Events */ 13130/* -------------------------------------------------------------------- */ 13131 13132function mk_hover_events() { 13133 13134 "use strict"; 13135 13136 $('.shopping-cart-header').hover( 13137 function() { 13138 $(this).find('.mk-shopping-cart-box').stop(true, true).fadeIn(250); 13139 }, 13140 function() { 13141 $(this).find('.mk-shopping-cart-box').stop(true, true).delay(500).fadeOut(250); 13142 } 13143 ); 13144 13145 13146 $('.widget-sub-navigation > ul > li, .widget_nav_menu ul.menu > li, .widget_product_categories ul > .cat-item').each(function() { 13147 13148 var $this = $(this), 13149 $subLevel = $this.find('ul').first(); 13150 13151 if ($this.hasClass('page_item_has_children') || $this.hasClass('menu-item-has-children') || $this.hasClass('cat-parent')) { 13152 $this.on('click', function() { 13153 if($this.hasClass('toggle-active')) { 13154 $subLevel.stop(true, true).slideUp(700); 13155 $this.removeClass('toggle-active'); 13156 } else { 13157 $subLevel.stop(true, true).slideDown(700); 13158 $this.addClass('toggle-active'); 13159 } 13160 }); 13161 13162 $subLevel.on('click', function(e){ 13163 e.stopPropagation(); 13164 }); 13165 } 13166 13167 }); 13168 13169 // var eventtype = mobilecheck() ? 'touchstart' : 'click'; 13170 var eventtype = 'click'; 13171 13172 $('.mk-fullscreen-trigger').on(eventtype, function(e) { 13173 $('.mk-fullscreen-search-overlay').addClass('mk-fullscreen-search-overlay-show'); 13174 setTimeout(function(){ 13175 $("#mk-fullscreen-search-input").focus(); 13176 }, 300); 13177 e.preventDefault(); 13178 }); 13179 13180 $('.mk-fullscreen-close').on(eventtype, function(e) { 13181 $('.mk-fullscreen-search-overlay').removeClass('mk-fullscreen-search-overlay-show'); 13182 e.preventDefault(); 13183 }); 13184 13185} 13186 13187function mk_unfold_footer() { 13188 var $this = $('#mk-footer'), 13189 $spacer = $('#mk-footer-unfold-spacer'), 13190 $footerHeight = $this.outerHeight(); 13191 13192 // Stick with CSS media query breakpoint to target exact screen width 13193 if( !window.matchMedia("(max-width: 767px)").matches ) { 13194 if ($this.hasClass('mk-footer-unfold')) { 13195 $spacer.css('height', $footerHeight); 13196 } 13197 } else { 13198 $spacer.css('height', 0); 13199 } 13200} 13201 13202/* jQuery fancybox lightbox */ 13203/* -------------------------------------------------------------------- */ 13204function mk_lightbox_init() { 13205 13206 $(".mk-lightbox").fancybox({ 13207 loop: true, 13208 }); 13209 13210 $.fancybox.defaults.hash = false;
13211 13212} 13213 13214 13215/* Milestone Number Shortcode */ 13216/* -------------------------------------------------------------------- */ 13217 13218function mk_milestone() { 13219 13220 "use strict"; 13221 13222 if( isTest || !$.exists('.mk-milestone') ) return; 13223 13224 $('.mk-milestone').each(function () { 13225 var $this = $(this), 13226 stop_number = $this.find('.milestone-number').attr('data-stop'), 13227 animation_speed = parseInt($this.find('.milestone-number').attr('data-speed')); 13228 13229 var build = function() { 13230 if (!$this.hasClass('scroll-animated')) { 13231 $this.addClass('scroll-animated'); 13232 13233 $({ 13234 countNum: $this.find('.milestone-number').text() 13235 }).animate({ 13236 countNum: stop_number 13237 }, { 13238 duration: animation_speed, 13239 easing: 'linear', 13240 step: function () { 13241 $this.find('.milestone-number').text(Math.floor(this.countNum)); 13242 }, 13243 complete: function () { 13244 $this.find('.milestone-number').text(this.countNum); 13245 } 13246 }); 13247 } 13248 }; 13249 13250 if ( !MK.utils.isMobile() ) { 13251 // refactored only :in-viewport logic. rest is to-do 13252 MK.utils.scrollSpy( this, { 13253 position: 'bottom', 13254 after: build 13255 }); 13256 } else { 13257 build(); 13258 } 13259 13260 }); 13261 13262} 13263 13264 13265 13266 13267/* Recent Works Widget */ 13268/* -------------------------------------------------------------------- */ 13269 13270function mk_portfolio_widget() { 13271 13272 "use strict"; 13273 13274 $('.widget_recent_portfolio li').each(function () { 13275 13276 $(this).find('.portfolio-widget-thumb').on('hover',function () { 13277 13278 $(this).siblings('.portfolio-widget-info').animate({ 13279 'opacity': 1 13280 }, 200); 13281 }, function () { 13282 13283 $(this).siblings('.portfolio-widget-info').animate({ 13284 'opacity': 0 13285 }, 200); 13286 }); 13287 13288 }); 13289} 13290 13291 13292 13293/* Skill Meter and Charts */ 13294/* -------------------------------------------------------------------- */ 13295function mk_skill_meter() { 13296 "use strict"; 13297 if ($.exists('.mk-skill-meter')) { 13298 if (!MK.utils.isMobile()) { 13299 $(".mk-skill-meter .progress-outer").each(function() { 13300 var $this = $(this); 13301 13302 var build = function() { 13303 if (!$this.hasClass('scroll-animated')) { 13304 $this.addClass('scroll-animated'); 13305 $this.animate({ 13306 width: $this.attr("data-width") + '%' 13307 }, 2000); 13308 } 13309 }; 13310 13311 MK.utils.scrollSpy( this, { 13312 position: 'bottom', 13313 after: build 13314 }); 13315 }); 13316 } else { 13317 $(".mk-skill-meter .progress-outer").each(function() { 13318 var $this = $(this); 13319 if (!$this.hasClass('scroll-animated')) { 13320 $this.addClass('scroll-animated'); 13321 $this.css({ 13322 width: $(this).attr("data-width") + '%' 13323 }); 13324 } 13325 }); 13326 } 13327 } 13328} 13329 13330// function mk_charts() { 13331// "use strict"; 13332 13333// if( !$.exists('.mk-chart') ) return; 13334 13335// MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.easyPieChart.js' ], function() { 13336 13337// $('.mk-chart').each(function() { 13338 13339// var $this = $(this), 13340// $parent_width = $(this).parent().width(), 13341// $chart_size = parseInt($this.attr('data-barSize')); 13342 13343// if ($parent_width < $chart_size) { 13344// $chart_size = $parent_width; 13345// $this.css('line-height', $chart_size); 13346// $this.find('i').css({ 13347// 'line-height': $chart_size + 'px' 13348// }); 13349// $this.css({ 13350// 'line-height': $chart_size + 'px' 13351// }); 13352// } 13353 13354// var build = function() { 13355// $this.easyPieChart({ 13356// animate: 1300, 13357// lineCap: 'butt', 13358// lineWidth: $this.attr('data-lineWidth'), 13359// size: $chart_size, 13360// barColor: $this.attr('data-barColor'), 13361// trackColor: $this.attr('data-trackColor'), 13362// scaleColor: 'transparent', 13363// onStep: function(value) {
13364// this.$el.find('.chart-percent span').text(Math.ceil(value)); 13365// } 13366// }); 13367// }; 13368 13369// // refactored only :in-viewport logic. rest is to-do 13370// MK.utils.scrollSpy( this, { 13371// position: 'bottom', 13372// after: build 13373// }); 13374 13375 13376// }); 13377// }); 13378// } 13379/* Toolbar subscribe form */ 13380/* -------------------------------------------------------------------- */ 13381 13382$( "#mc-embedded-subscribe-form" ).on('submit',function( e ){ 13383 var $this = $(this); 13384 13385 e.preventDefault(); 13386 $.ajax({ 13387 url: MK.core.path.ajaxUrl, 13388 type: "POST", 13389 data: { 13390 action: "mk_ajax_subscribe", 13391 email: $this.find( ".mk-subscribe--email" ).val(), 13392 list_id: $this.find( ".mk-subscribe--list-id" ).val(), 13393 optin: $this.find( ".mk-subscribe--optin" ).val() 13394 }, 13395 success: function ( res ) { 13396 $this.parent().find( ".mk-subscribe--message" ).html( $.parseJSON( res ).message ); 13397 } 13398 }); 13399}); 13400 13401/** 13402 * Add class to tag. It's vanilla js instead of jQuery. 13403 * @param tag 13404 * @param className 13405 */ 13406function addClass(tag, className) { 13407 tag.className += ' ' + className; 13408} 13409 13410/** 13411 * Remove class from tag. It's vanilla js instead of jQuery. 13412 * Replacing should be with g for replacing all occurrence. 13413 * 13414 * @param tag 13415 * @param className 13416 */ 13417function removeClass(tag, className) { 13418 tag.className = tag.className.replace(new RegExp(className, 'g'), ''); 13419} 13420 13421 13422/** 13423 * If we're running under Node for testing purpose. 13424 */ 13425if(typeof exports !== 'undefined') { 13426 exports.addClass = addClass; 13427 exports.removeClass = removeClass; 13428} 13429 13430/*! 13431* javascript-debounce 1.0.0 13432* 13433* A lightweight, dependency-free JavaScript module for debouncing functions based on David Walsh's debounce function. 13434* 13435* Source code available at: https://github.com/jgarber623/javascript-debounce 13436* 13437* (c) 2015-present Jason Garber (http://sixtwothree.org) 13438* 13439* javascript-debounce may be freely distributed under the MIT license. 13440*/ 13441 13442(function(root, factory) { 13443 if (typeof define === "function" && define.amd) { 13444 define([], factory); 13445 } else if (typeof exports === "object") { 13446 module.exports = factory(); 13447 } else { 13448 root.debounce = factory(); 13449 } 13450})(this, function() { 13451 "use strict"; 13452 return function(callback, delay) { 13453 var timeout; 13454 return function() { 13455 var context = this, args = arguments; 13456 clearTimeout(timeout); 13457 timeout = setTimeout(function() { 13458 callback.apply(context, args); 13459 }, delay); 13460 }; 13461 }; 13462}); 13463 13464/** 13465 * Validate email address. 13466 * 13467 * @param input 13468 * @param invalidClassName 13469 * @returns boolean 13470 */ 13471function validateEmail(input, invalidClassName) { 13472 var value = input.value.trim(); 13473 if ((input.required || value.length > 0) && !/^([a-z0-9_\.\-\+]+)@([\da-z\.\-]+)\.([a-z\.]{2,63})$/i.test(value)) { 13474 if (invalidClassName) { 13475 addClass(input, invalidClassName); 13476 } 13477 return false; 13478 } else { 13479 if (invalidClassName) { 13480 removeClass(input, invalidClassName); 13481 } 13482 return true; 13483 } 13484} 13485/** 13486 * Validate text entry. 13487 * 13488 * @param input 13489 * @param invalidClassName 13490 * @returns boolean 13491 */ 13492function validateText(input, invalidClassName) { 13493 var value = input.value.trim(); 13494 if (input.required && value.length === 0) { 13495 if (invalidClassName) { 13496 addClass(input, invalidClassName); 13497 } 13498 return false; 13499 } else { 13500 if (invalidClassName) { 13501 removeClass(input, invalidClassName); 13502 } 13503 return true; 13504 } 13505} 13506/** 13507 * Validate Checkbox. 13508 * 13509 * @param input 13510 * @param invalidClassName 13511 * @returns boolean 13512 */ 13513function validateCheckBox(input, invalidClassName) { 13514 if (input.required && input.checked == false) { 13515 if (invalidClassName) { 13516 addClass(input, invalidClassName); 13517 } 13518 return false; 13519 } else { 13520 if (invalidClassName) { 13521 removeClass(input, invalidClassName); 13522 } 13523 return true; 13524 } 13525} 13526/** 13527 * If we're running under Node for testing purpose. 13528 */ 13529if (typeof exports !== 'undefined') { 13530 exports.validateEmail = validateEmail; 13531 exports.validateText = validateText; 13532} 13533(function( $ ) { 13534 'use strict'; 13535 13536 var $wrapper = $('.js-bottom-corner-btns'); 13537 var $contactBtn = $wrapper.find('.js-bottom-corner-btn--contact'); 13538 var $backBtn = $wrapper.find('.js-bottom-corner-btn--back'); 13539 var hasContactBtn = $contactBtn.length; 13540 var hasBackBtn = $backBtn.length; 13541 13542 if(!hasBackBtn) return; 13543 13544 function deactivate() { 13545 $contactBtn.removeClass('is-active'); 13546 $backBtn.removeClass('is-active'); 13547 } 13548 13549 function activate() { 13550 $contactBtn.addClass('is-active'); 13551 $backBtn.addClass('is-active'); 13552 } 13553 13554 MK.utils.scrollSpy( 400, {
13555 before: deactivate, 13556 after: activate 13557 }); 13558 13559})( jQuery ); 13560(function( $ ) { 13561 'use strict'; 13562 13563 MK.ui.preloader = { 13564 dom : { 13565 overlay: '.mk-body-loader-overlay' 13566 }, 13567 13568 hide : function hide() { 13569 $( this.dom.overlay ).fadeOut(600, "easeInOutExpo", function() { 13570 $('body').removeClass('loading'); 13571 //$( this ).remove(); 13572 }); 13573 } 13574 }; 13575 13576})( jQuery ); 13577(function($) { 13578 'use strict'; 13579 13580 $('.mk-fullscreen-nav-close, .mk-fullscreen-nav-wrapper, #fullscreen-navigation a').on('click', function(e) { 13581 13582 // Close nav with removing classes 13583 $('.mk-fullscreen-nav').removeClass('opened'); 13584 $('.mk-dashboard-trigger').removeClass('fullscreen-active'); 13585 $('body').removeClass('fullscreen-nav-opened'); 13586 13587 var anchor = MK.utils.detectAnchor( this ), 13588 $this = $( this ); 13589 13590 // Scroll to anchor if exists 13591 if( anchor.length ) { 13592 // Moved 4 these lines after anchor check because console error. If anchor 13593 // return false, split part will throw an error because $this is not a link. 13594 // Fix AM-2027 13595 var href = $this.attr( 'href' ).split( '#' )[0]; 13596 var url = window.location.href; 13597 var isSamePage = url.indexOf( href ) !== -1; 13598 13599 if ( isSamePage ) { 13600 e.preventDefault(); 13601 } 13602 MK.utils.scrollToAnchor( anchor ); 13603 13604 // Or do nothing if pointless # as href 13605 // BAD PRACTICE: it is very popular to use "#" for click elements that we listen to with js. 13606 // GOOD PRACTICE: prefer "javascript:;" as easier to handle and more readable version 13607 } else if( $this.attr( 'href' ) === '#' ) { 13608 e.preventDefault(); 13609 } 13610 }); 13611 13612 $('.fullscreen-navigation-ul .menu-sub-level-arrow').on('click', function(){ 13613 $(this).siblings('.sub-menu').slideToggle(); 13614 }); 13615 13616}(jQuery)); 13617(function($) { 13618 'use strict'; 13619 13620 /** 13621 * Mega menu 13622 */ 13623 var $navList = $(".main-navigation-ul"); 13624 var megaMenu = function megaMenu() { 13625 13626 $navList.MegaMenu({ 13627 type: "vertical", 13628 delay: 200 13629 }); 13630 }; 13631 13632 $(window).on('load', megaMenu); 13633 13634}(jQuery)); 13635(function($) { 13636 'use strict'; 13637 13638 /** 13639 * One pager menu hash update. 13640 * Smooth scroll is appended globally whenever the click element has corresponding #el 13641 */ 13642 var onePageNavItem = function onePageNavItem() { 13643 var $this = $( this ), 13644 link = $this.find( 'a' ), 13645 anchor = MK.utils.detectAnchor( link ); // anchor on current page 13646 13647 if( !anchor.length ) return; 13648 13649 $this.removeClass( 'current-menu-item current-menu-ancestor current-menu-parent' ); 13650 13651 var activeNav = function( state ) { 13652 return function() { 13653 $this[ state ? 'addClass' : 'removeClass' ]( 'current-menu-item' ); 13654 window.history.replaceState( undefined, undefined, [ state ? anchor : ' ' ] ); 13655 }; 13656 }; 13657 13658 MK.utils.scrollSpy( $( anchor )[0], { 13659 before : activeNav( false ), 13660 active : activeNav( true ), 13661 after : activeNav( false ), 13662 }); 13663 }; 13664 13665 var $navItems = $('.js-main-nav').find( 'li' ); 13666 13667 $(window).on('load', function() { 13668 // Wait with spying anchors so we do not assign anchor that is browser scroll to after page load 13669 // Especially when there are anchors on top - refreshing page can cause grabbing one of them into url 13670 // and force unwanted scroll 13671 setTimeout(function() { 13672 $navItems.each( onePageNavItem ); 13673 }, 1000); 13674 }); 13675 13676}(jQuery)); 13677(function($) { 13678 'use strict'; 13679 13680 var $window = $(window); 13681 var $body = $('body'); 13682 var $resMenuWrap = $('.mk-responsive-wrap'); 13683 var $post_nav = $('.mk-post-nav'); 13684 var $toolbar = $('.mk-header-toolbar'); 13685 var $resMenuLink = $('.mk-nav-responsive-link'); 13686 13687 // Flags 13688 var hasResMenu = ($resMenuWrap.length > 0); 13689 13690 //initial window and screen height 13691 var windowHeight = $window.height(); 13692 var screenHeight = screen.height; 13693 13694 // We keep this handler above hasResMenu flag. 13695 // Even if our header doesn't contain droppable responsive menu (in favor of fullscreen or side menu) 13696 // we still transform tollbar into collapsible menu part in responsive state 13697 $('.mk-toolbar-resposnive-icon').on('click', function(e) { 13698 e.preventDefault(); 13699 console.log('clicked'); 13700 if ($body.hasClass('toolbar-opened')) { 13701 $body.removeClass('toolbar-opened').addClass('toolbar-closed'); 13702 $toolbar.hide(); 13703 } else { 13704 $body.removeClass('toolbar-closed').addClass('toolbar-opened'); 13705 $toolbar.show(); 13706 } 13707 }); 13708 13709 13710 if ( ! hasResMenu && ! $('.vc_mk_header') ) return; 13711 13712 function toggleResMenu(e) { 13713 e.preventDefault(); 13714 var $this = $(this); 13715 var $headerInner = $this.parents('header'); 13716 var $resMenu = $headerInner.find('.mk-responsive-wrap'); 13717 var searchBox = $('.responsive-searchform .text-input'); 13718 var adminBarHeight = $('#wpadminbar').height(); /* Fix AM-1918 */ 13719 13720 if ($body.hasClass('mk-opened-nav')) { 13721 $this.removeClass('is-active'); 13722 $body.removeClass('mk-opened-nav').addClass('mk-closed-nav').trigger('mk-closed-nav'); 13723 $resMenu.hide(); 13724 $post_nav.removeClass('post-nav-backward'); 13725 } else { 13726 $this.addClass('is-active'); 13727 $body.removeClass('mk-closed-nav').addClass('mk-opened-nav').trigger('mk-opened-nav'); 13728 $resMenu.show(); 13729 $post_nav.addClass('post-nav-backward'); 13730 13731 } 13732 13733 //for iPhone 5 focus bug , remove search box focused class 13734 if(searchBox.hasClass('input-focused')){ 13735 searchBox.removeClass('input-focused'); 13736 } 13737 13738 } 13739 13740 $resMenuLink.each(function() { 13741 $(this).on('click', toggleResMenu); 13742 }); 13743 13744 $( window ).on( 'vc_reload', function () { 13745 $('.vc_mk_header .mk-nav-responsive-link').each(function() { 13746 $(this).on('click', toggleResMenu); 13747 }); 13748 } ) 13749 13750 var setResMenuHeight = function() { 13751 var height = $window.height() - MK.val.offsetHeaderHeight(0); 13752 $resMenuWrap.css('max-height', height); 13753 }; 13754 13755 // check if device virtual keyboard is active 13756 var isVirtualKeyboard = function() { 13757 var currentWindowHeight = $window.height(); 13758 var currentScreenHeight = screen.height; 13759 var searchBox = $('.responsive-searchform .text-input'); 13760 var searchBoxIsFocused = false; 13761 13762 //for iPhone 5 focus bug , add class for detect focus state 13763 searchBox.on('touchstart touchend', function(e) { 13764 searchBox.addClass('input-focused'); 13765 }); 13766 13767 searchBoxIsFocused = (searchBox.is(':focus') || searchBox.hasClass("input-focused")); 13768 13769 if ($body.hasClass('mk-opened-nav') && searchBoxIsFocused && currentScreenHeight == screenHeight && currentWindowHeight != windowHeight) { 13770 return true; 13771 } else { 13772 return false;
13773 } 13774 }; 13775 13776 var hideResMenu = function hideResMenu() { 13777 if (MK.utils.isResponsiveMenuState()) { 13778 13779 //when search box in responsive menu is focused , window resize fired but at this time responsive menu should be open 13780 if (!isVirtualKeyboard()) { 13781 // hide toggled menu and its states 13782 if ($body.hasClass('mk-opened-nav')) { 13783 $resMenuLink.filter('.is-active').trigger('click'); 13784 } 13785 // hide menu wrapper 13786 $resMenuWrap.hide(); 13787 13788 } 13789 } 13790 }; 13791 13792 $resMenuWrap.on('click', 'a', hideResMenu); 13793 13794}(jQuery)); 13795 13796(function($) { 13797 'use strict'; 13798 13799 var $header = $('.mk-header'); 13800 var hasHeader = ($header.length > 0); 13801 13802 if(!hasHeader && ! $('.vc_mk_header') ) return; 13803 13804 var $sticky_style = $header.attr('data-header-style'); 13805 13806 //if ($sticky_style !== 3) return; 13807 13808 $('.sidedash-navigation-ul > li').each(function() { 13809 var $this = $(this); 13810 13811 $this.children('ul').siblings('a').after('<span class="mk-nav-arrow mk-nav-sub-closed"><svg class="mk-svg-icon" data-name="mk-moon-arrow-down" data-cacheid="2" style=" height:14px; width: 14px; " xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M512 192l-96-96-160 160-160-160-96 96 256 255.999z"></path></svg></span>'); 13812 }); 13813 13814 function toggleSubMenu(e) { 13815 e.preventDefault(); 13816 var $this = $(this); 13817 13818 if ($this.hasClass('mk-nav-sub-closed')) { 13819 $this.siblings('ul').slideDown(450).end().removeClass('mk-nav-sub-closed').addClass('mk-nav-sub-opened'); 13820 } else { 13821 $this.siblings('ul').slideUp(450).end().removeClass('mk-nav-sub-opened').addClass('mk-nav-sub-closed'); 13822 } 13823 }; 13824 13825 function toggleFullMenu(e) { 13826 e.preventDefault(); 13827 var $this = $(this), 13828 $body = $('body'), 13829 $fullscreen_box = $('.mk-fullscreen-nav'); 13830 13831 if ($this.hasClass('dashboard-style')){ 13832 if (!$this.hasClass('dashboard-active')) { 13833 $this.addClass('dashboard-active'); 13834 $body.addClass('dashboard-opened'); 13835 } else { 13836 $this.removeClass('dashboard-active'); 13837 $body.removeClass('dashboard-opened'); 13838 } 13839 }else if($this.hasClass('fullscreen-style')){ 13840 if (!$this.hasClass('fullscreen-active')) { 13841 $this.addClass('fullscreen-active'); 13842 $body.addClass('fullscreen-nav-opened'); 13843 $fullscreen_box.addClass('opened'); 13844 // MK.utils.scroll.disable(); 13845 } else { 13846 $this.removeClass('fullscreen-active'); 13847 $body.removeClass('fullscreen-nav-opened'); 13848 $fullscreen_box.removeClass('opened'); 13849 } 13850 } 13851 } 13852 13853 $('.mk-nav-arrow').each(function() { 13854 $(this).stop(true).on('click', toggleSubMenu); 13855 }); 13856 13857 $('.mk-dashboard-trigger').each(function() { 13858 $(this).on('click', toggleFullMenu); 13859 }); 13860 13861 $( window ).on( 'vc_reload', function () { 13862 $('.vc_mk_header .mk-dashboard-trigger').each(function() { 13863 $(this).on('click', toggleFullMenu); 13864 }); 13865 13866 $('.vc_mk_header .mk-nav-arrow').each(function() { 13867 $(this).stop(true).on('click', toggleSubMenu); 13868 }); 13869 } ); 13870 13871 $('html').on('click', function() { 13872 $('body').removeClass('dashboard-opened'); 13873 $('.mk-dashboard-trigger').removeClass('dashboard-active'); 13874 }); 13875 13876}(jQuery)); 13877 13878(function($) { 13879 'use strict'; 13880 13881 /** 13882 * Vertical menu 13883 */ 13884 var $verticalMenu = $('#mk-vm-menu'); 13885 var verticalMenu = function verticalMenu() { 13886 if(!$verticalMenu.data('vertical-menu') && !MK.utils.isResponsiveMenuState()) { 13887 $verticalMenu.dlmenu(); 13888 $verticalMenu.data('vertical-menu', true); 13889 } 13890 }; 13891 13892 verticalMenu(); 13893 $(window).on('resize', verticalMenu); 13894 13895}(jQuery)); 13896(function($) { 13897 'use strict'; 13898 13899 // WPML 13900 var $lang_item = $('.mk-main-navigation > .main-navigation-ul > .menu-item-language'); 13901 $lang_item.addClass('no-mega-menu').css('visibility', 'visible'); 13902 $('.mk-main-navigation .menu-item-language > a').addClass('menu-item-link'); 13903 13904})(jQuery); 13905(function($) { 13906 'use strict'; 13907 13908 var $header = $( '.mk-header' ).first(); // Theme header is always first, do not apply to shortcode headers. 13909 var hasHeader = ($header.length > 0); 13910 13911 if(!hasHeader) return; 13912 13913 var $window = $( window ); 13914 var $document = $( document ); 13915 var $headerHolder = $header.find( '.mk-header-holder' ); 13916 var $paddingWrapper = $header.find('.mk-header-padding-wrapper'); 13917 var config = $header.data(); 13918 13919 /** 13920 * Flags 13921 * @type {Boolean} 13922 */ 13923 var isStickyLazy = (config.stickyStyle === 'lazy'); 13924 var isStickyFixed = (config.stickyStyle === 'fixed'); 13925 var isStickySlide = (config.stickyStyle === 'slide'); 13926 13927 function isSticky() { // Check for sticky compatibility 13928 //return config.headerStyle !== 4; header style 4 needs sticky state for transparent header. 13929 return true; 13930 } 13931
13932 function isColorable() { // Check for coloring compatibility 13933 // TODO make it explicit in DOM that header is transparent as this is only scenario for colorable header 13934 // Monkey patch - exclude header styles that don't fit colorable 13935 return config.headerStyle !== 4; 13936 } 13937 13938 13939 /** 13940 * Change header skin 13941 */ 13942 function changeSkin( e, skin ) { 13943 $header.attr( 'data-transparent-skin', skin ); 13944 // Fix for class based skining 13945 var contrast = skin === 'light' ? 'dark' : 'light'; 13946 $header.addClass(skin + '-skin'); 13947 $header.removeClass(contrast + '-skin'); 13948 } 13949 13950 if(isColorable()) MK.utils.eventManager.subscribe( 'firstElSkinChange', changeSkin ); 13951 13952 13953 // Assign sticky scenarions 13954 if (isSticky() && isStickyLazy) { 13955 if(config.headerStyle !== 2) { 13956 lazySticky(); 13957 } 13958 } 13959 else if (isSticky() && isStickyFixed) fixedSticky(); 13960 else if (isSticky() && isStickySlide) slideSticky(); 13961 13962 /** 13963 * Sticky header behavior: Lazy 13964 */ 13965 function lazySticky() { 13966 var elClassHidden = 'header--hidden'; 13967 var elClassSticky = 'a-sticky'; 13968 var wScrollCurrent = 0; 13969 var wScrollBefore = 0; 13970 var wScrollDiff = 0; 13971 var wHeight = 0; 13972 var dHeight = 0; 13973 13974 var setSizes = function setSizes() { 13975 dHeight = $document.height(); 13976 wHeight = $window.height(); 13977 }; 13978 13979 var onScroll = function onScroll() { 13980 wScrollCurrent = MK.val.scroll(); 13981 wScrollDiff = wScrollBefore - wScrollCurrent; 13982 13983 if( wScrollCurrent <= 0 ) { // scrolled to the very top; element sticks to the top 13984 $headerHolder.removeClass( elClassHidden ); 13985 $header.removeClass( elClassSticky ); 13986 $('body').trigger('mk:header-unsticky'); 13987 13988 } else if( wScrollDiff > 0 && $headerHolder.hasClass( elClassHidden ) ) { // scrolled up; element slides in 13989 $headerHolder.removeClass( elClassHidden ); 13990 $header.addClass( elClassSticky ); 13991 $('body').trigger('mk:header-sticky'); 13992 13993 } else if( wScrollDiff < 0 ) { // scrolled down 13994 13995 if( wScrollCurrent + wHeight >= dHeight && $headerHolder.hasClass( elClassHidden ) ) { // scrolled to the very bottom; element slides in 13996 $headerHolder.removeClass( elClassHidden ); 13997 $header.addClass( elClassSticky ); 13998 $('body').trigger('mk:header-sticky'); 13999 } else { // scrolled down; element slides out 14000 $headerHolder.addClass( elClassHidden ); 14001 $header.removeClass( elClassSticky ); 14002 $('body').trigger('mk:header-unsticky'); 14003 } 14004 } 14005 14006 wScrollBefore = wScrollCurrent; 14007 }; 14008 14009 setSizes(); 14010 onScroll(); 14011 $window.on( 'resize', MK.utils.throttle( 100, setSizes ) ); 14012 $window.on( 'scroll', MK.utils.throttle( 500, onScroll ) ); 14013 } 14014 14015 14016 /** 14017 * Sticky header behavior: Fixed 14018 */ 14019 function fixedSticky() { 14020 var sticked = false; 14021 var scrollPos; 14022 14023 var toggleState = function toggleState() { 14024 scrollPos = MK.val.scroll() + MK.val.adminbarHeight(); 14025 14026 if( (scrollPos > MK.val.stickyOffset() ) && ! MK.utils.isResponsiveMenuState() ) { 14027 if(sticked) return; // stop if already sticked 14028 $header.addClass('a-sticky'); 14029 sticked = true; 14030 $('body').trigger('mk:header-sticky'); 14031 } else { 14032 if(!sticked) return; // stop if already unsticked 14033 $header.removeClass('a-sticky'); 14034 sticked = false; 14035 $('body').trigger('mk:header-unsticky'); 14036 } 14037 }; 14038 14039 toggleState(); 14040 $window.on( 'scroll', MK.utils.throttle( 100, toggleState) ); 14041 $window.on( 'resize', MK.utils.throttle( 100, toggleState) ); 14042 } 14043 14044 14045 /** 14046 * Sticky header behavior: Slide 14047 */ 14048 function slideSticky() { 14049 var sticked = false;
14050 var onScroll = function onScroll() { 14051 if (MK.val.scroll() > MK.val.stickyOffset()) { 14052 if(sticked) return; // stop if already sticked 14053 $header.addClass('pre-sticky'); 14054 $paddingWrapper.addClass('enable-padding'); 14055 setTimeout(function() { 14056 $header.addClass('a-sticky'); 14057 $('body').trigger('mk:header-sticky'); 14058 }, 1); 14059 sticked = true; 14060 } else { 14061 if(!sticked) return; // stop if already unsticked 14062 $header.removeClass('a-sticky'); 14063 $header.removeClass('pre-sticky'); 14064 $paddingWrapper.removeClass('enable-padding'); 14065 sticked = false; 14066 $('body').trigger('mk:header-unsticky'); 14067 } 14068 }; 14069 14070 onScroll(); 14071 $window.on( 'scroll', MK.utils.throttle( 100, onScroll) ); 14072 } 14073 14074 // TODO 14075 // var pageIntro = $('body').data('intro'); // block scrolling on page intro 14076 14077 14078})( jQuery ); 14079 14080(function($){ 14081 'use strict'; 14082 14083 // Do not assign at all if no touch events 14084 var hasTouchscreen = ('ontouchstart' in document.documentElement); 14085 if(!hasTouchscreen) return; 14086 14087 $('.mk-main-navigation .menu-item-has-children').each( normalizeClick ); 14088 14089 function normalizeClick() { 14090 $(this).on('click', handleClick); 14091 } 14092 14093 // Most reasonable way to normalize click and let hover state on first touch yet don't break experience on multiple input devices is to actually check against desired effect. 14094 // Better way would be to check against expected state but JS doesn't detect :hover so it would be nice to have it normalized globally like expecting .hover class in css 14095 function handleClick(e) { 14096 var $this = $(e.currentTarget); 14097 var $child = $this.find('> ul'); 14098 var isVisible = $child.css('display') !== 'none'; 14099 14100 if(!isVisible) { 14101 e.preventDefault(); 14102 e.stopPropagation(); 14103 } 14104 } 14105 14106}(jQuery)); 14107(function($) { 14108 'use strict'; 14109 14110 var _ajaxUrl = MK.core.path.ajaxUrl; 14111 var _instances = {}; 14112 14113 MK.utils.ajaxLoader = function ajaxLoader(el) { 14114 // retrun a cached instance to have control over state from within multiple places 14115 // we may need for example to reset pageId when do filtering. It is really one instance that controls both filtering and pagination / load more 14116 var id = '#' + ($(el).attr('id')); 14117 if(typeof _instances[id] !== 'undefined') return _instances[id]; 14118 14119 // else lets start new instance 14120 this.id = id; 14121 this.el = el; 14122 this.isLoading = false; 14123 this.xhrCounter = 0; 14124 }; 14125 14126 MK.utils.ajaxLoader.prototype = { 14127 init: function init() { 14128 // prevent double initialization of we return an instance 14129 if ( this.initialized && typeof window.vc_iframe === 'undefined' ) { 14130 return; 14131 } 14132 14133 this.createInstance(); 14134 this.cacheElements(); 14135 14136 this.initialized = true; 14137 }, 14138 14139 cacheElements: function cacheElements() { 14140 this.$container = $(this.el); 14141 this.id = '#' + (this.$container.attr('id')); 14142 this.categories = this.$container.data('loop-categories'); 14143 14144 this.data = {}; 14145 this.data.action = 'mk_load_more'; 14146 this.data.query = this.$container.data('query'); 14147 this.data.atts = this.$container.data('loop-atts'); 14148 this.data.loop_iterator = this.$container.data('loop-iterator'); 14149 this.data.author = this.$container.data('loop-author'); 14150 this.data.posts = this.$container.data('loop-posts'); 14151 this.data.safe_load_more = this.$container.siblings('#safe_load_more').val(); 14152 this.data._wp_http_referer = this.$container.siblings('input[name="_wp_http_referer"]').val(); 14153 this.data.paged = 1; 14154 this.data.maxPages = this.$container.data('max-pages'); 14155 this.data.term = this.categories; 14156 }, 14157 14158 createInstance: function() { 14159 _instances[this.id] = this; 14160 }, 14161 14162 load: function load(unique) { 14163 var self = this; 14164 var seq = ++this.xhrCounter; 14165 this.isLoading = true; 14166 14167 // If mk-ajax-loaded-posts span exists, get the post ids 14168 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 14169 var loaded_posts = this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts'); 14170
14171 // Do not send looaded posts for Classic Pagination Navigation 14172 if ( this.$container.attr('data-pagination-style') != 1 ) { 14173 self.data.loaded_posts = loaded_posts.split(','); 14174 } 14175 } 14176 14177 return $.when( 14178 $.ajax({ 14179 url : _ajaxUrl, 14180 type : "POST", 14181 data : self.data 14182 }) 14183 ).done(function(response) { 14184 self.onDone(response, unique, seq); 14185 }); 14186 }, 14187 14188 onDone: function(response, unique, seq) { 14189 if(seq === this.xhrCounter) { 14190 var self = this; 14191 14192 response = $.parseJSON(response); 14193 response.unique = unique; 14194 response.id = this.id; 14195 14196 // If mk-ajax-loaded-posts span exists, update current post ids 14197 // with new post ids from server's response 14198 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 14199 this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts', response.loaded_posts); 14200 } 14201 14202 this.setData({ 14203 maxPages: response.maxPages, 14204 found_posts: response.found_posts, 14205 loop_iterator: response.i 14206 }); 14207 14208 // Preload images first by creating object from returned content. 14209 // mk_imagesLoaded() method will create a promise that gets resolved when all images inside are loaded. 14210 // Our ajaxLoad is somehow more similar to window.onload event now. 14211 $(response.content).mk_imagesLoaded().then(function() { 14212 MK.utils.eventManager.publish('ajaxLoaded', response); 14213 self.isLoading = false; 14214 self.initNewComponents(); 14215 }); 14216 14217 } else console.log('XHR request nr '+ seq +' aborted'); 14218 14219 }, 14220 14221 setData: function setData(atts) { 14222 for(var att in atts) { 14223 if(att === 'term' && atts[att] === '*') this.data.term = ''; 14224 else this.data[att] = atts[att]; 14225 } 14226 }, 14227 14228 getData: function getData(att) { 14229 return this.data[att]; 14230 }, 14231 14232 initNewComponents: function initNewComponents() { 14233 // Legacy scripts reinit 14234 window.ajaxInit(); 14235 setTimeout(window.ajaxDelayedInit, 1000); 14236 // New way to init apended things 14237 MK.core.initAll(this.el); 14238 } 14239 }; 14240 14241}(jQuery)); 14242(function( $ ) { 14243 'use strict'; 14244 14245 var val = MK.val; 14246 14247 MK.component.FullHeight = function( el ) { 14248 var $window = $( window ), 14249 $this = $( el ), 14250 config = $this.data( 'fullheight-config' ), 14251 container = document.getElementById( 'mk-theme-container' ), 14252 minH = (config && config.min) ? config.min : 0, 14253 winH = null, 14254 height = null, 14255 update_count = 0, 14256 testing = MK.utils.getUrlParameter('testing'), 14257 offset = null; 14258 14259 // We need to provide height on the same specificity level for workaround to IE bug 14260 // connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview 14261 // stackoverflow.com/questions/19371626/flexbox-not-centering-vertically-in-ie 14262 if(MK.utils.browser.name === ('IE' || 'Edge')) $this.css( 'height', '1px' ); 14263 14264 var update = function() { 14265 14266 if(update_count === 0) { 14267 winH = $window.height(); 14268 // for correct calculate 14269 offset = $this.offset().top - 1; 14270 height = Math.max(minH, winH - val.offsetHeaderHeight( offset )); 14271 $this.css( 'min-height', height ); 14272 if(testing !== undefined ) 14273 update_count++; 14274 } 14275 14276 }; 14277 14278 // TODO remove scroll listener by dynamic offset reader 14279 var init = function() { 14280 update(); 14281 $window.on( 'resize', update ); 14282 $window.on( 'scroll', update ); 14283 window.addResizeListener( container, update ); 14284 }; 14285 14286 return { 14287 init : init 14288 }; 14289 }; 14290 14291})( jQuery ); 14292 14293 14294(function( $ ) { 14295 'use strict'; 14296 14297 /** 14298 * A fork from original VC function. 14299 * The original one is adding some padding which causes extra padding 14300 * on our templates so we customized the codes. 14301 * 14302 * @since 5.9.8 14303 * @since 6.0.1 Improve logic to handle LTR responsive state. 14304 * @since 6.0.1 Fix missing padding-left if row has its own padding-left when 14305 * Vertical Header is active. 14306 */ 14307 function MkfullWidthRow() { 14308 var $windowWidth = $(document).width(); 14309 var $elements = $('[data-mk-full-width="true"]'); 14310 var direction = $('body.rtl').length ? 'right' : 'left'; 14311 var verticalHeader = $('body.vertical-header-enabled').length ? true : false;
14312 var verticalHeaderWidth = ( $( '.header-style-4 .mk-header-inner' ).outerWidth() > 270 ) ? 0 : 270; 14313 var verticalHeaderRtl = $('body.rtl').length ? -1 : 1; 14314 var verticalHeaderRtlWidth = $('body.rtl.vertical-header-enabled').length ? verticalHeaderWidth : 0; 14315 var verticalHeaderRight = $('body.vertical-header-right').length ? -1 : 1; 14316 var verticalHeaderWidthBoxed = 0; 14317 var boxed = $('body.mk-boxed-enabled').length; 14318 var boxedOffset = ( boxed ) ? ( $(window).width() - $('#theme-page').outerWidth() ) / 2 : 0; 14319 var boxedMaxWidth = ( boxed ) ? $('#theme-page').outerWidth() : 'auto'; 14320 14321 if ( verticalHeader && boxed ) { 14322 verticalHeaderWidthBoxed = ( $( '.header-style-4 .mk-header-inner' ).outerWidth() > 270 ) ? 0 : verticalHeaderRtl * verticalHeaderRight * 135; 14323 } 14324 14325 var transparentHeader = $('.transparent-header').length; 14326 if ( transparentHeader > 0 ) { 14327 verticalHeaderWidthBoxed = 0; 14328 } 14329 14330 $.each($elements, function(key, item) { 14331 var $el = $(this); 14332 var css; 14333 $el.addClass("vc_hidden"); 14334 var $el_full = $el.next(".vc_row-full-width"); 14335 if ($el_full.length || ($el_full = $el.parent().next(".vc_row-full-width")), $el_full.length) { 14336 var el_margin_left = parseInt($el.css("margin-left"), 10), 14337 el_margin_right = parseInt($el.css("margin-right"), 10), 14338 offset = 0 - $el_full.offset().left - el_margin_left, 14339 width = $(window).width(); 14340 if (css = { 14341 position: "relative", 14342 "box-sizing": "border-box", 14343 width: $(window).width(), 14344 maxWidth: boxedMaxWidth 14345 }, 14346 css[direction] = offset + boxedOffset + verticalHeaderWidthBoxed + ( verticalHeaderRight * verticalHeaderRtlWidth ), 14347 $el.css(css), !$el.data("mkStretchContent")) { 14348 var padding = -1 * offset; 14349 0 > padding && (padding = 0); 14350 var paddingRight = width - padding - $el_full.width() + el_margin_left + el_margin_right; 14351 padding = padding - paddingRight; 14352 if ( 0 > paddingRight && (paddingRight = 0) ) { 14353 css = {}; 14354 if ( 'right' === direction ) { 14355 css["padding-left"] = padding + "px"; 14356 css["padding-right"] = 0; 14357 } else { 14358 css["padding-right"] = padding + "px"; 14359 css["padding-left"] = 0; 14360 } 14361 14362 $el.css(css) 14363 } 14364 } 14365 14366 /** 14367 * Fix AM-2974 14368 * 14369 * When user set padding-left or padding-right in the row, both of them 14370 * will be prioritized as important styles. It breaks padding-left set 14371 * by Vertical Header to make the row content fit with the content 14372 * container. We need to repopulate the padding-{side - based on Vertical 14373 * Header position} value. 14374 * 14375 * Works when: 14376 * - Vertical Header is active 14377 * - Main layout is not boxed 14378 * - Page Section is full layout 14379 * - Row is full width layout and content 14380 * - Row has its own padding-left and padding-right 14381 * - There is no additional padding added 14382 * 14383 * How it works: 14384 * Get current row padding-left and vertical header width. If both of them 14385 * are equal, it means row position is correct related to Vertical Header. 14386 * If not, we should sum both of them, then declare new padding-{side} 14387 * style attribute. 14388 * 14389 * Tested with: 14390 * - Main Layout: Full & Boxed Layout 14391 * - Page Section: Full Layout and Non Full Layout 14392 * - Row: Full Width, Full Width Content, and Non Full Width 14393 * - Header: Non vertical headers 14394 * - Transparent Header 14395 * - RTL and LTR 14396 */ 14397 if ( verticalHeader && ! boxed && ! transparentHeader && ! css.hasOwnProperty( 'padding-left' ) && ! css.hasOwnProperty( 'padding-right' )) { 14398 // Get row padding-{side} and Vertical Header width. 14399 var side = 'left'; 14400 if ( verticalHeaderRight === -1 ) { 14401 side = 'right'; 14402 } 14403 var el_padding_dir = parseInt( $el.css( 'padding-' + side ), 10 ); 14404 var header_padding_dir = $( '.header-style-4 .mk-header-inner' ).outerWidth(); 14405 // Compare both of row and vertical header padding-{side}. 14406 if ( el_padding_dir != header_padding_dir) { 14407 if($windowWidth > mk_responsive_nav_width) { 14408 $el[0].style.setProperty( 'padding-' + side, header_padding_dir + 'px', 'important' ); 14409 } else { 14410 $el[0].style.removeProperty( 'padding-' + side); 14411 } 14412 14413 14414 // Reset padding-{side} for page section. 14415 var $el_page_section = $el.find( '.mk-page-section.full_layout' ); 14416 if ( $el_page_section.length > 0 ) { 14417 $el_page_section[0].style.setProperty( 'padding-' + side, 'unset', 'important' ); 14418 } 14419 } 14420 } 14421 14422 $el.attr("data-mk-full-width-init", "true"), $el.removeClass("vc_hidden"), $(document).trigger("vc-full-width-row-single", { 14423 el: $el, 14424 offset: offset, 14425 marginLeft: el_margin_left, 14426 marginRight: el_margin_right, 14427 elFull: $el_full, 14428 width: width 14429 }) 14430 } 14431 }
14431), $(document).trigger("mk-full-width-row", $elements); 14432 } 14433 14434 MkfullWidthRow(); 14435 var debounceResize = null; 14436 $(window).on("resize", function() { 14437 if( debounceResize !== null ) { clearTimeout( debounceResize ); } 14438 debounceResize = setTimeout( MkfullWidthRow, 100 ); 14439 }); 14440 14441})( jQuery ); 14442 14443MK.component.BackgroundImageSetter = (function ($) { 14444 14445 'use strict'; 14446 14447 var module = {}; 14448 14449 14450 /*---------------------------------------------------------------------------------*/ 14451 /* Private Variables 14452 /*---------------------------------------------------------------------------------*/ 14453 14454 /** 14455 * Take all elements with data-mk-img-set attribute and evaluate best image according to given device orientation and resolution, 14456 * sets style for backround-image on the same node element. * 14457 */ 14458 14459 var $win = $(window), 14460 // $layers = $('[data-mk-img-set]'), 14461 screen = getScreenSize(), 14462 orientation = getOrientation(), 14463 device = getDevice(), 14464 lastOrientation = orientation, 14465 lastDevice = device; 14466 14467 14468 /*---------------------------------------------------------------------------------*/ 14469 /* Private Methods 14470 /*---------------------------------------------------------------------------------*/ 14471 14472 function run($layers) { 14473 $layers.filter( function() { 14474 return !this.hasAttribute("mk-img-loaded"); 14475 }).each(applyBg); 14476 } 14477 14478 // Keep our main side effect out of calculations so they can be run once before loop of applying bg as a result 14479 function applyBg() { 14480 var $this = $(this), 14481 imgs = $this.data('mk-img-set'); 14482 14483 $this.css('background-image', 'url('+ module.getImage(imgs) +')'); 14484 $this.find('.mk-adaptive-image').attr('src', module.getImage(imgs)); 14485 } 14486 14487 // Keep track of current screen size while resizing but update device reference 14488 // and reapply backgrounds only when we discover switch point 14489 function handleResize($layers) { 14490 updateScreenSize(); 14491 if(hasSwitched()) { 14492 updateDevice(); 14493 run($layers); 14494 } 14495 } 14496 14497 function getScreenSize() { 14498 return { 14499 w: $win.width(), 14500 h: $win.height() 14501 }; 14502 } 14503 14504 // Name our device classes and add them id which simply means which one is wider 14505 function getDevice() { 14506 if (screen.w > 1024) return {class: 'desktop', id: 2}; 14507 else if(screen.w > 736) return {class: 'tablet', id: 1}; 14508 else return {class: 'mobile', id: 0}; 14509 } 14510 14511 function getOrientation() { 14512 if(screen.w > screen.h) return 'landscape'; 14513 else return 'portrait'; 14514 } 14515 14516 function updateScreenSize() { 14517 screen = getScreenSize(); 14518 } 14519 14520 function updateDevice() { 14521 if(lastOrientation !== orientation) orientation = lastOrientation; 14522 // Switch device only if going from smaller size to bigger. 14523 // Bigger to smaller is perfectly handled by browsers and doesn't require change and reupload 14524 if(lastDevice.id > device.id) device = lastDevice; 14525 } 14526 14527 function hasSwitched() { 14528 lastOrientation = getOrientation(); 14529 lastDevice = getDevice(); 14530 14531 if(lastOrientation !== orientation || lastDevice.class !== device.class) return true; 14532 else return false; 14533 } 14534 14535 14536 /*---------------------------------------------------------------------------------*/ 14537 /* Public Methods 14538 /*---------------------------------------------------------------------------------*/ 14539 14540 // As desired image might be not available we have to evaluate the best match. 14541 module.getImage = function (imgs) { 14542 if (imgs['responsive'] === 'false') { 14543 return (imgs['landscape']['desktop']) ? imgs['landscape']['desktop'] : (imgs['landscape']['external'] ? imgs['landscape']['external'] : ''); 14544 14545 } 14546 var hasOrientation = !!imgs[orientation]; 14547 // there are only two orientations now and we may get them by string name if both are there 14548 // or by index of 0 if only one is available. Note Objects has no lexical order so we need to grab key name by its index. 14549 // Also we may have external file for each orientation which we don't scale internaly so we grab it as it is. If nothing found return an empty string 14550 var imgOriented = imgs[ (hasOrientation ? orientation : Object.keys(imgs)[0]) ], 14551 imgExact = (imgOriented[device.class]) ? imgOriented[device.class] : (imgOriented['external'] ? imgOriented['external'] : ''); 14552 return imgExact; 14553 } 14554 14555 module.init = function ($layers) { 14556 14557 // Run and bind 14558 run($layers); 14559 $layers.attr('mk-img-loaded', ''); 14560 }; 14561 14562 module.onResize = function ($layers) { 14563 $win.on('resize', MK.utils.throttle( 500, function() { 14564 handleResize($layers); 14565 })); 14566 }; 14567 14568 return module; 14569 14570}(jQuery)); 14571 14572 14573jQuery(function($) { 14574
14575 var init = function init() { 14576 // Get All Layers, Excluding Edge Slider and Page Section 14577 $allLayers = $('[data-mk-img-set]').filter(function(index) { 14578 return !$(this).hasClass('mk-section-image') && !$(this).hasClass('background-layer') && !$(this).hasClass('mk-video-section-touch'); 14579 });; 14580 14581 // Handle the resize 14582 MK.component.BackgroundImageSetter.onResize($allLayers); 14583 14584 // Set all the BG Layers 14585 MK.component.BackgroundImageSetter.init($allLayers); 14586 } 14587 init(); 14588 14589 $(window).on('vc_reload', init); 14590 14591}); 14592 14593(function( $ ) { 14594 'use strict'; 14595 14596 var core = MK.core, 14597 utils = MK.utils, 14598 path = MK.core.path; 14599 14600 14601 MK.ui.FullScreenGallery = function( element, settings ) { 14602 this.element = element; 14603 this.config = settings; 14604 14605 this.isFullScreen = false; 14606 }; 14607 14608 14609 // preload slick PLUGIN TO USE THIS 14610 MK.ui.FullScreenGallery.prototype = { 14611 dom : { 14612 fullScrBtn : '.slick-full-screen', 14613 exitFullScrBtn : '.slick-minimize', 14614 playBtn : '.slick-play', 14615 pauseBtn : '.slick-pause', 14616 shareBtn : '.slick-share', 14617 socialShare : '.slick-social-share', 14618 wrapper : '.slick-slider-wrapper', 14619 slider : '.slick-slides', 14620 slides : '.slick-slide', 14621 dots : '.slick-dot', 14622 active : '.slick-active', 14623 hiddenClass : 'is-hidden', 14624 dataId : 'slick-index' 14625 }, 14626 14627 tpl: { 14628 dot : '<div class="slick-dot"></div>', 14629 next : '<a href="javascript:;" class="slick-next"> <svg width="33px" height="65px"> <polyline fill="none" stroke="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points=" 0.5,0.5 32.5,32.5 0.5,64.5"/> </svg> </a>', 14630 prev : '<a href="javascript:;" class="slick-prev"> <svg width="33px" height="65px"> <polyline fill="none" stroke="#FFFFFF" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points=" 32.5,64.5 0.5,32.5 32.5,0.5"/> </svg> </a>' 14631 }, 14632 14633 init : function() { 14634 var self = this; 14635 14636 // core.loadDependencies([ path.plugins + 'slick.js' ], function() { 14637 self.cacheElements(); 14638 self.getViewportSizes(); 14639 self.updateSizes( 'window' ); 14640 self.create(); 14641 // update cache with elements propagated by plugin 14642 self.updateCacheElements(); 14643 self.createPagination(); 14644 self.bindEvents(); 14645 // }); 14646 }, 14647 14648 create : function() { 14649 var self = this; 14650 14651 this.slick = this.$gallery.slick({ 14652 dots: true, 14653 arrows: true, 14654 infinite: true, 14655 speed: 300, 14656 slidesToShow: 1, 14657 centerMode: true, 14658 centerPadding: '0px', 14659 variableWidth: true, 14660 autoplay: false, 14661 autoplaySpeed: 3000, 14662 useTransform: true, 14663 prevArrow: self.tpl.prev, 14664 nextArrow: self.tpl.next, 14665 customPaging: function(slider, i) { 14666 return self.tpl.dot; 14667 }, 14668 }); 14669 }, 14670 14671 cacheElements : function() { 14672 this.$window = $( window ); 14673 this.$gallery = $( this.element ); 14674 14675 this.$fullScrBtn = $( this.dom.fullScrBtn ); 14676 this.$exitFullScrBtn = $( this.dom.exitFullScrBtn ); 14677 this.$playBtn = $( this.dom.playBtn ); 14678 this.$pauseBtn = $( this.dom.pauseBtn ); 14679 this.$shareBtn = $( this.dom.shareBtn ); 14680 this.$socialShare = $( this.dom.socialShare ); 14681 14682 this.$wrapper = $( this.dom.wrapper ); 14683 this.$slider = $( this.dom.slider ); 14684 this.$slides = $( this.dom.slides ); 14685 this.$imgs = this.$slides.find( 'img' ); 14686 // store reference to initial images without slides appended by pugin 14687 // - needed for creating of pagination 14688 this.$originalImgs = this.$imgs; 14689 }, 14690 14691 updateCacheElements : function() { 14692 this.$slides = $( this.dom.slides ); 14693 this.$imgs = this.$slides.find( 'img' ); 14694 this.$dots = $( this.dom.dots ); 14695 }, 14696 14697 bindEvents : function() { 14698 var self = this; 14699 this.$fullScrBtn.on( 'click', this.toFullScreen.bind( this ) ); 14700 this.$exitFullScrBtn.on( 'click', this.exitFullScreen.bind( this ) ); 14701 this.$playBtn.on( 'click', this.play.bind( this ) ); 14702 this.$pauseBtn.on( 'click', this.pause.bind( this ) ); 14703 this.$shareBtn.on( 'click', this.toggleShare.bind( this ) ); 14704 this.$socialShare.on( 'click', 'a', this.socialShare.bind( this ) ); 14705 this.$window.on( 'resize', this.onResize.bind( this ) ); 14706 this.$window.on( 'keydown', function(e) { 14707 if(e.keyCode === 39) self.$gallery.slick('slickNext'); 14708 if(e.keyCode === 37) self.$gallery.slick('slickPrev'); 14709 }); 14710 $( document ).on( 'fullscreenchange mozfullscreenchange webkitfullscreenchange msfullcreenchange', this.exitFullScreen.bind( this ) ); 14711 }, 14712 14713 getViewportSizes : function() { 14714 this.screen = { 14715 w: screen.width, 14716 h: screen.height 14717 }; 14718 this.window = { 14719 w: this.$window.width(), 14720 h: this.$window.height() 14721 }; 14722 }, 14723 14724 updateSizes : function( viewport ) { 14725 this.$wrapper.width( this[ viewport ].w ); 14726 this.$wrapper.height( '100%' ); 14727 this.$imgs.height( '100%'); 14728 }, 14729 14730 createPagination : function() { 14731 var self = this; 14732 this.$dots.each( function( i ) { 14733 var img = self.$originalImgs.eq( i ).attr( 'src' ); 14734 14735 $( this ).css({ 14736 'background-image': 'url('+ img +')' 14737 }); 14738 }); 14739 }, 14740 14741 play : function(e) { 14742 e.preventDefault(); 14743 this.$playBtn.addClass( this.dom.hiddenClass ); 14744 this.$pauseBtn.removeClass( this.dom.hiddenClass ); 14745 $( this.element ).slick( 'slickPlay' ); 14746 }, 14747 14748 pause : function(e) { 14749 e.preventDefault(); 14750 this.$pauseBtn.addClass( this.dom.hiddenClass ); 14751 this.$playBtn.removeClass( this.dom.hiddenClass ); 14752 $( this.element ).slick( 'slickPause' ); 14753 }, 14754 14755 toggleShare : function(e) { 14756 e.preventDefault(); 14757 this.$socialShare.toggleClass( this.dom.hiddenClass ); 14758 }, 14759 14760 getCurentId : function() { 14761 return this.$slides.filter( this.dom.active ).data( this.dom.dataId ); 14762 }, 14763 14764 toFullScreen : function() { 14765 var self = this; 14766 14767 this.$fullScrBtn.addClass( this.dom.hiddenClass ); 14768 this.$exitFullScrBtn.removeClass( this.dom.hiddenClass ); 14769 14770 this.$slider.hide().fadeIn( 500 ); 14771 utils.launchIntoFullscreen( document.documentElement ); 14772 this.updateSizes( 'screen' ); 14773 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14774 14775 // Update state with delay so we avoid triggering exitFullScreen fn from 14776 // fullscreenchange event 14777 setTimeout( function() { 14778 self.isFullScreen = true; 14779 }, 1000); 14780 }, 14781 14782 exitFullScreen : function() { 14783 if( this.isFullScreen ) { 14784 this.$exitFullScrBtn.addClass( this.dom.hiddenClass ); 14785 this.$fullScrBtn.removeClass( this.dom.hiddenClass ); 14786 14787 utils.exitFullscreen(); 14788 this.updateSizes( 'window' ); 14789 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14790 14791 this.isFullScreen = false;
14792 } 14793 14794 }, 14795 14796 onResize : function() { 14797 this.getViewportSizes(); 14798 this.updateSizes( this.isFullScreen ? 'screen' : 'window' ); 14799 $( this.element ).slick( 'refresh' ); 14800 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14801 this.updateCacheElements(); 14802 this.createPagination(); 14803 }, 14804 14805 socialShare : function( e ) { 14806 e.preventDefault(); 14807 var $this = $( e.currentTarget ), 14808 network = $this.data( 'network' ), 14809 id = this.config.id, 14810 url = this.config.url, 14811 title = this.$wrapper.find( '.slick-title' ).text(), 14812 name; 14813 var picture = this.$slides.filter( this.dom.active ).children().first().attr( 'src' ); 14814 switch( network ) { 14815 case 'facebook': 14816 url = 'https://www.facebook.com/sharer/sharer.php?picture=' + picture+'&u=' + url + '#id=' + id; 14817 name = 'Facebook Share'; 14818 break; 14819 case 'twitter': 14820 url = 'http://twitter.com/intent/tweet?text=' + url + '#id=' + id; 14821 name = 'Twitter Share'; 14822 break; 14823 case 'pinterest': 14824 url = 'http://pinterest.com/pin/create/bookmarklet/?media=' + picture + '&url=' + url + '&is_video=false&description=' + title; 14825 // other available link paranmeters: media, description 14826 name = 'Pinterest Share'; 14827 break; 14828 14829 } 14830 14831 window.open( url, name, "height=380 ,width=660, resizable=0, toolbar=0, menubar=0, status=0, location=0, scrollbars=0" ); 14832 } 14833 }; 14834 14835})( jQuery ); 14836(function($) { 14837 'use strict'; 14838 14839 MK.component.Grid = function( el ) { 14840 var $container = $(el); 14841 var config = $container.data( 'grid-config' ); 14842 var isSlideshow = $container.closest('[data-mk-component="SwipeSlideshow"]').length; 14843 var miniGridConfig = { 14844 container: el, 14845 item: config.item + ':not(.is-hidden)', 14846 gutter: 0 14847 }; 14848 14849 var init = function init(){ 14850 // Flags for cancelling usage goes first : 14851 // Quit early if we discover that Grid is used inside SwipeSlideshow as it brings bug with crossoverriding positioning 14852 // + grid is not really needed as we have single row all handled by slider. 14853 // It happens only in woocommerce carousel as of hardcoded Grid in loop-start.php 14854 if(isSlideshow) return; 14855 MK.core.loadDependencies([ MK.core.path.plugins + 'minigrid.js' ], create); 14856 }; 14857 14858 // Remove el hidden without adding proper class 14859 var prepareForGrid = function prepareForGrid() { 14860 var $item = $(this); 14861 var isHidden = ($item.css('display') === 'none'); 14862 if(isHidden) $item.addClass('is-hidden'); 14863 else $item.removeClass('is-hidden'); 14864 }; 14865 14866 var create = function create() { 14867 var timer = null; 14868 14869 function draw() { 14870 // Prevent plugin breaking when feeding it with hidden elements 14871 $container.find(config.item).each( prepareForGrid ); 14872 minigrid(miniGridConfig); 14873 } 14874 14875 function redraw() { 14876 if (timer) clearTimeout(timer); 14877 timer = setTimeout(draw, 100); 14878 } 14879 14880 // init 14881 draw(); 14882 // If reinitializing drop existing event handler 14883 $(window).off('resize', redraw); 14884 $(window).on('resize', redraw); 14885 MK.utils.eventManager.subscribe('item-expanded', redraw); 14886 MK.utils.eventManager.subscribe('ajaxLoaded', redraw); 14887 MK.utils.eventManager.subscribe('staticFilter', redraw); 14888 }; 14889 14890 return { 14891 init : init 14892 }; 14893 }; 14894 14895})(jQuery); 14896 14897 14898 14899 14900 14901 14902 14903 14904/** 14905 * ICON FACTORY @ Maki 14906 * 14907 * Javascript part that translates css styles into link to server side manufacturer. 14908 * By passing extra args we generate icons as needed on server and send them back with ajax and attch them to DOM here. 14909 * 14910 * This is temporary solution that helps us switching from font icons to singular svgs without too match overhead. The finalized solution shoulnd be 99% server side. 14911 * Our main templating is happening there. We should have an icon_factory(name, properties) function that we can call directly in templates to serve us icons. 14912 * Post load, js soluition should target only elements that we have no direct access to. We set it as TODO for future as part of progressive development. 14913 * When correct factory is done we should be able to use it here and there to progressively kill overhead generated by current solution until we could totally switch it. 14914 * 14915 * Prons: 14916 * - automated process where we can mimic old behaviour 14917 * - possible bugs are easy to patch 14918 * - far samller download size for assets needed by theme (no font families css and fonts itself to download, which are currently blocking page load time) 14919 * - icon generation moved to server side where our main templating is already happening 14920 * - we can easily switch how our Icon should be outputed - inline, img, object, iframe. Whatever our situation requires (img for responsive icons for example) 14921 * 14922 * Cons: 14923 * - possible bugs based on different nature of svgs / fonts. Need to be manually fixed
14924 * - some manual work to straighten things - litle css refactors here and there 14925 * - as the mapping the icons happens in server side, it may cause CPU bottlenecks and should be cashed in DB for repetitve usage. 14926 * 14927 * 14928 * TODO Refactor to config obj as parameter for easier passing it. Also decouple more - especially loops 14929 */ 14930(function ($) { 14931 'use strict'; 14932 14933 // As long as we can manage this manually it is better like so. We avoid delay based on waiting for ajax to finish 14934 // $.get( MK.core.path.theme + '/assets/icons/icon-families.json', createAll); 14935 var families = [ 14936 ['awesome-icons' , 'mk-icon-'], // [family, prefix] 14937 ['icomoon' , 'mk-moon-'], 14938 ['pe-line-icons' , 'mk-li-'], 14939 ['theme-icons' , 'mk-jupiter-icon-'] 14940 ]; 14941 14942 // Our css contains this extends for particular families of fonts. For now we automate the process and extend it here as well 14943 var extend = { 14944 'awesome-icons' : [], 14945 'icomoon' : [], 14946 'pe-line-icons' : [], 14947 'theme-icons' : [] 14948 }; 14949 14950 // Cache holder for quick access to repeating icons. config / svg 14951 var _cache = {}; 14952 var _cacheId = 0; 14953 // Merge configs to call it with single ajax 14954 var _config = []; 14955 // Counter for all families. We merge all our families for single ajax request and countdown when to trigger the download. 14956 var _roundCount = 0; 14957 // Mapper for icons - config / array with nodes 14958 var _iconMap = {}; 14959 14960 14961 // Closure to merge $icons together and to run the download when we counted down icon families to 0 14962 var getIconsSprite = (function() { 14963 var $icons = null; 14964 var iterator = 0; 14965 14966 function run(callback) { 14967 var config = encodeURIComponent(JSON.stringify(_config)); 14968 14969 $.ajax({ 14970 url : MK.core.path.ajaxUrl, 14971 // dataType: 'xml', 14972 method: 'POST', 14973 data: {action : 'mk_get_icon', iterator: iterator++, config: config}, 14974 success: function(sprite) { 14975 callback(sprite, $icons); 14976 _config = []; 14977 _iconMap = {}; 14978 $icons = null; 14979 }, 14980 error: function(err) { 14981 console.log('Icon load problem'); 14982 } 14983 }); 14984 } 14985 14986 return function(callback, $els, count) { 14987 if(!$icons) $icons = $els; 14988 else $icons.add($els); 14989 // console.log(count); 14990 if(!count) run(callback); 14991 }; 14992 }()); 14993 14994 // Wait for DOM manipulations and run where really needed 14995 $(window).on('load', function() { 14996 setTimeout(function() { 14997 createAll(document); 14998 if($('.mk-header').length) createAll('.mk-header'); 14999 if($('.js-flexslider, .mk-flexslider').length) createAll('.js-flexslider, .mk-flexslider'); 15000 if($('.mk-accordion').length) createAll('.mk-accordion'); 15001 }, 1000); 15002 }); 15003 15004 MK.utils.eventManager.subscribe('ajaxLoaded', function() { 15005 // Wait for actuall DOM insertion 15006 setTimeout(createAll, 100, '.js-loop'); 15007 }); 15008 15009 MK.utils.eventManager.subscribe('ajax-preview', function() { 15010 setTimeout(createAll, 100, '.ajax-container'); 15011 }); 15012 15013 MK.utils.eventManager.subscribe('photoAlbum-open', function() { 15014 setTimeout(createAll, 100, '.gallery-share'); 15015 }); 15016 15017 MK.utils.eventManager.subscribe('quickViewOpen', function() { 15018 setTimeout(createAll, 300, '.mk-modal-content'); 15019 }); 15020 15021 // Our main runner that creates all icons in given scope 15022 function createAll(scope) { 15023 // iterate through families 15024 for(var i = 0, l = families.length; i < l; i++) { 15025 var family = families[i][0]; 15026 var prefix = families[i][1]; 15027 var $icons = getIcons(family, prefix, scope); 15028 if($icons.length) { 15029 _roundCount++; 15030 // apply arguments from current loop iteration but send createIcons to event loop so it will get trigerred in the same order as this loop finish. 15031 // We need it to pass all $icons first so we can merge all families before ajax request. 15032 setTimeout(createIcons, 0, $icons, family, prefix); 15033 } 15034 } 15035 } 15036 15037 // Grab all els in given scope that contains our prefix, anywhere in class attribute. Extend if needed 15038 function getIcons(family, prefix, scope) { 15039 var $scope = $(scope); 15040 var $icons = $scope.find('[class*='+ prefix +']'); 15041 var extraClassNames = extend[family]; 15042 // if nothing to extend just return what we have till now 15043 if(!extraClassNames) return $icons; 15044 // Extend it with our css garbish
15045 extraClassNames.forEach(function(className) { 15046 var $icon = $scope.find(className); 15047 $icons = $icons.add($icon); 15048 }); 15049 // serve all 15050 return $icons; 15051 } 15052 15053 15054 function createIcons($icons, family, prefix, i, unicode) { 15055 var id = i || 0; 15056 var icon = $icons[id]; 15057 15058 // quick check point, if no more icons break recursion 15059 if(!icon) { 15060 _roundCount--; 15061 getIconsSprite(insertIcons, $icons, _roundCount, _config); 15062 return; 15063 } 15064 15065 var css = getComputedStyle( icon, ':before'); 15066 var classAttr = icon.getAttribute('class'); 15067 var name = (classAttr) ? matchClass(classAttr.split(' '), prefix) : false; 15068 var h = getComputedStyle(icon).fontSize; 15069 var config = createConfig(css, name, family, unicode, h); 15070 var cache = JSON.stringify(config); 15071 15072 if(!config) { 15073 // Yep, we have a mess so sometimes we try to call for an icon where it should not exist (no class match or unicode found) 15074 // As we expect empty response from server, we just skip to next icon 15075 15076 } else if(_cache[cache]) { 15077 // Use cached icon when possible 15078 // 15079 if(typeof _iconMap[cache] === 'undefined') _iconMap[cache] = [$icons.eq(id)]; 15080 else _iconMap[cache].push($icons.eq(id)); 15081 15082 } else { 15083 if(typeof _iconMap[cache] === 'undefined') _iconMap[cache] = [$icons.eq(id)]; 15084 else _iconMap[cache].push($icons.eq(id)); 15085 15086 _cache[cache] = _cacheId.toString(); // number as string so we could do [var] key lookup after reversal 15087 config.id = _cacheId; // extend config for server only 15088 _config.push(config); 15089 15090 _cacheId++; // increment after caching 15091 } 15092 15093 createIcons($icons, family, prefix, ++id); 15094 } 15095 15096 function insertIcons(sprite, $icons) { 15097 var $sprite = $(sprite); 15098 var $svgs = $sprite.find('svg'); 15099 var idMap = invert(_cache); 15100 15101 $sprite.each(function() { 15102 var $svg = $(this); 15103 var id = $svg.attr('data-cacheid'); // read as string 15104 var configKey = idMap[id]; 15105 15106 _cache[configKey] = this; 15107 // console.log(id, this) 15108 }); 15109 15110 // console.log('sprite',sprite) 15111 // 15112 15113 Object.keys(_iconMap).forEach(function (cacheKey) { 15114 _iconMap[cacheKey].forEach(function($icons) { 15115 $icons.each(function() { 15116 var $svg = $(_cache[cacheKey]).clone(); 15117 var $icon = $(this); 15118 15119 // console.log($icon[0], _cache[cacheKey]); 15120 15121 function remove() { 15122 // exclude problematic stuff 15123 if($icon.parents('.pricing-features')) return; 15124 $icon.not('.mk-jupiter-icon-xing') 15125 .not('.mk-jupiter-icon-square-xing') 15126 .not('.mk-jupiter-icon-simple-xing') 15127 .find('.mk-svg-icon') 15128 .not('[data-name="mk-moon-zoom-in"]') 15129 .remove(); // and remove else to reapply styles with new loaded svg 15130 } 15131 15132 if($svg.length ) remove(); 15133 15134 if(!$icon.find('svg').length) { 15135 if($icon.parents('.widget ul').length) $icon.prepend($svg); 15136 else $icon.append($svg); 15137 } 15138 }); 15139 }); 15140 }); 15141 15142 // Notify rest of app 15143 MK.utils.eventManager.publish('iconsInsert'); 15144 } 15145 15146 // Prepare configuration for server side. We need to grab icons and manipulate them a little there 15147 function createConfig(css, name, family, unicode, height) { 15148 var hasGradient = checkGradient(css); 15149 var hasDirection = extractGradient('direction', css.background); 15150 15151 var config = { 15152 family: family, 15153 // this is mostly for Extends. Because we've been customizing singular elements and assigning content unicode manually, 15154 // now we don't have any proper anchor for usage with our json map 15155 unicode: (unicode) ? unicode : decodeUnicode(css.content), 15156 name: name, 15157 // fill: css.color, 15158 gradient_type: hasGradient ? extractGradient('type' , css.background) : false, 15159 gradient_start: hasGradient ? extractGradient('start', css.background) : false, 15160 gradient_stop: hasGradient ? extractGradient('stop' , css.background) : false, 15161 gradient_direction: hasDirection ? extractGradient('direction', css.background).replace(' ', '-') : false, 15162 height: height 15163 }; 15164 15165 // If there is no name or unicode the whole config is invalid 15166 if(!config.name && !config.unicode) return false;
15167 else return config; 15168 } 15169 15170 // Extract class name that represents the icon that we try to generate 15171 function matchClass(classes, prefix) { 15172 // var missing = ''; 15173 for(var i = 0, l = classes.length; i < l; i++) { 15174 if(classes[i].indexOf(prefix) !== -1) return classes[i]; // return first matched class name 15175 // else missing += ' .' + classes[i]; // collect not matching classes 15176 } 15177 // if we didn't returned anything within loop we output notification here. Keep it for testing only 15178 // console.log('Possibility of missing icons for element with classes:' + missing); 15179 } 15180 15181 // Check if background conatains gradient declaration and return bg string if so or false 15182 function checkGradient(css) { 15183 var bg = css.background; 15184 if(bg.indexOf('radial') !== -1 || bg.indexOf('linear') !== -1) return bg; 15185 else return false; 15186 } 15187 15188 // Extract background attribiutes from bg string. 15189 // Multipurpose function which returns part we ask for 15190 function extractGradient(attr, grad) { 15191 // quit if gradient is falsy 15192 // console.log(grad); 15193 if(!grad) return false; 15194 15195 var isRadial = grad.indexOf('radial') !== -1; 15196 var isLinear = grad.indexOf('linear') !== -1; 15197 var hasDirection = grad.indexOf('(to') !== -1; 15198 var f, t; // from, to 15199 15200 if(attr === 'type') { 15201 if(isRadial) return 'radial'; 15202 if(isLinear) return 'linear'; 15203 15204 } else if(attr === 'start') { 15205 f = getStrPosition(grad, 'rgb(', 1); 15206 t = getStrPosition(grad, '0%' , 1); 15207 15208 } else if(attr === 'stop') { 15209 f = getStrPosition(grad, 'rgb(', 2); 15210 t = getStrPosition(grad, '100%', 1); 15211 15212 } else if(attr === 'direction') { 15213 if(!hasDirection) return false; 15214 f = getStrPosition(grad, '(to', 1) + 4; 15215 t = getStrPosition(grad, ', rgb(', 1); 15216 15217 } else { 15218 return false; 15219 } 15220 15221 return grad.slice(f, t); 15222 } 15223 15224 // Helper for getting indexOf but with lookup for instance of the string 15225 // str - string to search 15226 // m - what we're looking for 15227 // i - instance, starting at 1 15228 function getStrPosition(str, m, i) { 15229 return str.split(m, i).join(m).length; 15230 } 15231 15232 // JS outputs unicode results rather than code representation. This helper function does the job 15233 function decodeUnicode(content) { 15234 // escape() kills \ escape signature and outputs it as %u 15235 // str replace to drop special chars 15236 // and we bring it all to lowercase to match filenames we keep on server 15237 if(content && content !== 'none') return escape(content).replace(/%22/g, '').replace('%u', '').toLowerCase(); 15238 else return false; // just in case we haven't provided content to work with 15239 } 15240 15241 function invert(obj) { 15242 var new_obj = {}; 15243 for (var prop in obj) { 15244 if(obj.hasOwnProperty(prop)) { 15245 new_obj[obj[prop]] = prop; 15246 } 15247 } 15248 return new_obj; 15249 } 15250 15251}(jQuery)); 15252/*! 15253 * imagesLoaded PACKAGED v4.1.1 15254 * JavaScript is all like "You images are done yet or what?" 15255 * MIT License 15256 */ 15257 15258!function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=0,o=i[n];e=e||[];for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o];s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){"use strict";"function"==typeof define&&define.amd?define(["ev-emitter/ev-emitter"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}(window,function(t,e){function i(t,e){for(var i in e)t[i]=e[i];return t}function n(t){var e=[];if(Array.isArray(t))e=t;else if("number"==typeof t.length)for(var i=0;i<t.length;i++)e.push(t[i]);else e.push(t);return e}function o(t,e,r){return this instanceof o?("string"==typeof t&&(t=document.querySelectorAll(t)),this.elements=n(t),this.options=i({},this.options),"function"==typeof e?r=e:i(this.options,e),r&&this.on("always",r),this.getImages(),h&&(this.jqDeferred=new h.Deferred),void setTimeout(function(){this.check()}.bind(this))):new o(t,e,r)}function r(t){this.img=t}function s(t,e){this.url=t,this.element=e,this.img=new Image}var h=t.jQuery,a=t.console;o.prototype=Object.create(e.prototype),o.prototype.options={},o.prototype.getImages=function(){this.images=[],this.elements.forEac
15258h(this.addElementImages,this)},o.prototype.addElementImages=function(t){"IMG"==t.nodeName&&this.addImage(t),this.options.background===!0&&this.addElementBackgroundImages(t);var e=t.nodeType;if(e&&d[e]){for(var i=t.querySelectorAll("img"),n=0;n<i.length;n++){var o=i[n];this.addImage(o)}if("string"==typeof this.options.background){var r=t.querySelectorAll(this.options.background);for(n=0;n<r.length;n++){var s=r[n];this.addElementBackgroundImages(s)}}}};var d={1:!0,9:!0,11:!0};return o.prototype.addElementBackgroundImages=function(t){var e=getComputedStyle(t);if(e)for(var i=/url\((['"])?(.*?)\1\)/gi,n=i.exec(e.backgroundImage);null!==n;){var o=n&&n[2];o&&this.addBackground(o,t),n=i.exec(e.backgroundImage)}},o.prototype.addImage=function(t){var e=new r(t);this.images.push(e)},o.prototype.addBackground=function(t,e){var i=new s(t,e);this.images.push(i)},o.prototype.check=function(){function t(t,i,n){setTimeout(function(){e.progress(t,i,n)})}var e=this;return this.progressedCount=0,this.hasAnyBroken=!1,this.images.length?void this.images.forEach(function(e){e.once("progress",t),e.check()}):void this.complete()},o.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount==this.images.length&&this.complete(),this.options.debug&&a&&a.log("progress: "+i,t,e)},o.prototype.complete=function(){var t=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){var e=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[e](this)}},r.prototype=Object.create(e.prototype),r.prototype.check=function(){var t=this.getIsImageComplete();return t?void this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),void(this.proxyImage.src=this.img.src))},r.prototype.getIsImageComplete=function(){return this.img.complete&&void 0!==this.img.naturalWidth},r.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.img,e])},r.prototype.handleEvent=function(t){var e="on"+t.type;this[e]&&this[e](t)},r.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},r.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},r.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},s.prototype=Object.create(r.prototype),s.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url;var t=this.getIsImageComplete();t&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},s.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},s.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},o.makeJQueryPlugin=function(e){e=e||t.jQuery,e&&(h=e,h.fn.imagesLoaded=function(t,e){var i=new o(this,t,e);return i.jqDeferred.promise(h(this))})},o.makeJQueryPlugin(),o}); 15259 15260(function($, window){ 15261 'use strict'; 15262 15263 var scrollY = MK.val.scroll; 15264 var dynamicHeight = MK.val.dynamicHeight; 15265 15266 var $window = $(window); 15267 var $containers = $('.js-loop'); 15268 15269 $containers.each( pagination ); 15270 15271 $window.on( 'vc_reload', function() { 15272 $('.js-loop').each( pagination ); 15273 } ); 15274 15275 function pagination() { 15276 var unique = Date.now(); 15277 var $container = $(this); 15278 var $superContainer = $container.parent(); // should contain clearing so it stretches with floating children 15279 var $loadBtn = $container.siblings('.js-loadmore-holder').find('.js-loadmore-button'); 15280 var $loadScroll = $('.js-load-more-scroll'); 15281 var style = $container.data('pagination-style'); 15282 var maxPages = $container.data('max-pages'); 15283 var id = '#' + ($container.attr('id')); 15284 var ajaxLoader = new MK.utils.ajaxLoader(id); 15285 var isLoadBtn = (style === 2); 15286 var isInfiniteScroll = (style === 3); // add flag for last container 15287 var scrollCheckPoint = null; 15288 var isHandlerBinded = false;
15289 15290 ajaxLoader.init(); 15291 15292 init(); 15293 15294 function init() { 15295 MK.utils.eventManager.subscribe('ajaxLoaded', onLoad); 15296 bindHandlers(); 15297 if( isInfiniteScroll ) scrollCheckPoint = spyScrollCheckPoint(); 15298 15299 $window.on( 'vc_reload', function() { 15300 $window.off('scroll', handleScroll); 15301 } ); 15302 } 15303 15304 function bindHandlers() { 15305 if( isLoadBtn ) $loadBtn.on('click', handleClick); 15306 if( isInfiniteScroll ) $window.on('scroll', handleScroll); 15307 isHandlerBinded = true; 15308 } 15309 15310 function unbindHandlers() { 15311 if( isLoadBtn ) $loadBtn.off('click', handleClick); 15312 if( isInfiniteScroll ) $window.off('scroll', handleScroll); 15313 isHandlerBinded = false; 15314 } 15315 15316 function handleClick(e) { 15317 e.preventDefault(); 15318 if(!ajaxLoader.isLoading) loadMore(); 15319 } 15320 15321 function handleScroll() { 15322 if((scrollY() > scrollCheckPoint()) && !ajaxLoader.isLoading) loadMore(); 15323 } 15324 15325 function loadMore() { 15326 loadingIndicatorStart(); 15327 var page = ajaxLoader.getData('paged'); 15328 ajaxLoader.setData({paged: ++page}); 15329 ajaxLoader.load(unique); 15330 } 15331 15332 function onLoad(e, response) { 15333 if( typeof response !== 'undefined' && response.id === id) { 15334 // Checking found posts helps to fix all pagination styles 15335 if( ajaxLoader.getData('found_posts') <= 0 && ajaxLoader.getData('paged') >= ajaxLoader.getData('maxPages')) loadingIndicatorHide(); 15336 else loadingIndicatorShow(); 15337 if(response.unique === unique) $container.append(response.content); 15338 loadingIndicatorStop(); 15339 } 15340 } 15341 15342 function loadingIndicatorStart() { 15343 if(isLoadBtn) $loadBtn.addClass('is-active'); 15344 else if(isInfiniteScroll) MK.ui.loader.add('.js-load-more-scroll'); 15345 15346 } 15347 15348 function loadingIndicatorStop() { 15349 if(isLoadBtn) $loadBtn.removeClass('is-active'); 15350 else if(isInfiniteScroll) MK.ui.loader.remove('.js-load-more-scroll'); 15351 } 15352 15353 function loadingIndicatorShow() { 15354 if(isHandlerBinded) return; 15355 if(isLoadBtn) $loadBtn.show(); 15356 else if(isInfiniteScroll) $loadScroll.show(); 15357 bindHandlers(); 15358 } 15359 15360 function loadingIndicatorHide() { 15361 if(!isHandlerBinded) return; 15362 if(isLoadBtn) $loadBtn.hide(); 15363 else if(isInfiniteScroll) $loadScroll.hide(); 15364 unbindHandlers(); 15365 } 15366 15367 15368 function spyScrollCheckPoint() { 15369 var containerO = 0; 15370 var containerH = dynamicHeight( $superContainer ); 15371 var winH = dynamicHeight( window ); 15372 15373 var setVals = function() { 15374 containerO = $superContainer.offset().top; 15375 }; 15376 15377 setVals(); 15378 $window.on('resize', function() { requestAnimationFrame(setVals); }); 15379 15380 return function() { 15381 return (containerH() + containerO) - (winH() * 2); 15382 }; 15383 } 15384 } 15385 15386})(jQuery, window); 15387(function($) { 15388 'use strict'; 15389 15390 // Check if it's inside hidden parent 15391 // Cannot be position: fixed 15392 function isHidden(el) { 15393 return (el.offsetParent === null); 15394 } 15395 15396 MK.component.Masonry = function(el) { 15397 var $window = $(window); 15398 var $container = $(el); 15399 var config = $container.data( 'masonry-config' ); 15400 var $masonryItems = $container.find(config.item); 15401 var cols = config.cols || 8; 15402 var $filterItems = null; // assign only when apply filter 15403 var wall = null; 15404 15405 var init = function init() { 15406 MK.core.loadDependencies([ MK.core.path.plugins + 'freewall.js' ], onDepLoad); 15407 }; 15408 15409 var onDepLoad = function onDepLoad() { 15410 masonry(); 15411 15412 // Events 15413 $window.on('resize', onResize); 15414 MK.utils.eventManager.subscribe('ajaxLoaded', onPostAddition); 15415 MK.utils.eventManager.subscribe('staticFilter', resize); 15416 }; 15417 15418 var masonry = function masonry() { 15419 // Quit for hidden elements for now. 15420 if(isHidden(el)) return; 15421 15422 var newCols; 15423 if(window.matchMedia( '(max-width:600px)' ).matches) newCols = 2; 15424 else if(window.matchMedia( '(max-width:850px)' ).matches) newCols = 4; 15425 else newCols = cols; 15426 15427 var colW = $container.width() / newCols; 15428 15429 wall = new Freewall( config.container ); 15430 15431 // We need to pass settings to a plugin via reset method. Strange but works fine. 15432 wall.reset({
15433 selector: config.item + ':not(.is-hidden)', 15434 gutterX: 0, // set default gutter to 0 and again - apply margins to item holders in css 15435 gutterY: 0, 15436 cellW: colW, 15437 cellH: colW 15438 }); 15439 15440 wall.fillHoles(); 15441 wall.fitWidth(); 15442 15443 $masonryItems.each(function() { 15444 $(this).data('loaded', true); 15445 }); 15446 }; 15447 15448 15449 // Clear attributes after the plugin. It's API method dosn't handle it properly 15450 var destroyContainer = function destroyContainer() { 15451 $container.removeAttr('style') 15452 .removeData('wall-height') 15453 .removeData('wall-width') 15454 .removeData('min-width') 15455 .removeData('total-col') 15456 .removeData('total-row') 15457 .removeAttr('data-wall-height') 15458 .removeAttr('data-wall-width') 15459 .removeAttr('data-min-width') 15460 .removeAttr('data-total-col') 15461 .removeAttr('data-total-row'); 15462 }; 15463 15464 var destroyItem = function destroyItem() { 15465 var $item = $(this); 15466 $item.removeAttr('style') 15467 .removeData('delay') 15468 .removeData('height') 15469 .removeData('width') 15470 .removeData('state') 15471 .removeAttr('data-delay') 15472 .removeAttr('data-height') 15473 .removeAttr('data-width') 15474 .removeAttr('data-state'); 15475 }; 15476 15477 var destroyAll = function destroyAll() { 15478 if( !wall ) return; 15479 wall.destroy(); // API destroy 15480 destroyContainer(); 15481 $masonryItems.each( destroyItem ); // run our deeper destroy 15482 }; 15483 15484 var onResize = function onResize() { 15485 requestAnimationFrame(resize); 15486 }; 15487 15488 var refresh = function refresh() { 15489 if( !wall ) return; 15490 setTimeout(wall.fitWidth.bind(wall), 5); 15491 }; 15492 15493 var resize = function resize() { 15494 destroyAll(); 15495 masonry(); 15496 }; 15497 15498 var onPostAddition = function onPostAddition() { 15499 $masonryItems = $container.find(config.item); 15500 15501 $masonryItems.each(function() { 15502 var $item = $(this), 15503 isLoaded = $item.data('loaded'); 15504 15505 if(!isLoaded) $item.css('visibility', 'hidden'); 15506 }); 15507 15508 15509 $container.mk_imagesLoaded().then(function() { 15510 destroyAll(); 15511 masonry(); 15512 }); 15513 }; 15514 15515 return { 15516 init : init 15517 }; 15518 }; 15519 15520}(jQuery)); 15521(function($) { 15522 'use strict'; 15523 15524 MK.component.Pagination = function(el) { 15525 this.el = el; 15526 }; 15527 15528 MK.component.Pagination.prototype = { 15529 init: function init() { 15530 this.cacheElements(); 15531 this.bindEvents(); 15532 this.onInitLoad(); 15533 }, 15534 15535 cacheElements: function cacheElements() { 15536 this.lastId = 1; 15537 this.unique = Date.now(); 15538 this.$pagination = $(this.el); 15539 this.$container = this.$pagination.prev('.js-loop'); 15540 this.$pageLinks = this.$pagination.find('.js-pagination-page'); 15541 this.$nextLink = this.$pagination.find('.js-pagination-next'); 15542 this.$prevLink = this.$pagination.find('.js-pagination-prev'); 15543 this.$current = this.$pagination.find('.js-current-page'); 15544 this.$maxPages = this.$pagination.find('.pagination-max-pages'); // TODO change in DOM and here to js class 15545 this.containerId = '#' + this.$container.attr('id'); 15546 this.pagePathname = window.location.pathname; 15547 this.pageSearch = window.location.search; 15548 this.popState = false; 15549 this.ajaxLoader = new MK.utils.ajaxLoader('#' + this.$container.attr('id')); 15550 this.ajaxLoader.init(); 15551 }, 15552 15553 bindEvents: function bindEvents() { 15554 this.$pageLinks.on('click', this.pageClick.bind(this)); 15555 this.$nextLink.on('click', this.nextClick.bind(this)); 15556 this.$prevLink.on('click', this.prevClick.bind(this)); 15557 MK.utils.eventManager.subscribe('ajaxLoaded', this.onLoad.bind(this)); 15558 }, 15559 15560 pageClick: function pageClick(e) { 15561 e.preventDefault(); 15562 var $this = $(e.currentTarget); 15563 var id = parseFloat($this.attr('data-page-id')); 15564 15565 if(id > this.ajaxLoader.getData('maxPages') || id < 1) return; 15566 this.load(id, $this); 15567 this.updatePagedNumUrl( id ); 15568 }, 15569 15570 nextClick: function nextClick(e) { 15571 e.preventDefault(); 15572 if(this.ajaxLoader.getData('paged') === this.ajaxLoader.getData('maxPages')) return; 15573 this.load(++this.lastId, $(e.currentTarget)); 15574 this.updatePagedNumUrl( this.lastId ); 15575 }, 15576 15577 prevClick: function prevClick(e) { 15578 e.preventDefault(); 15579 if(this.ajaxLoader.getData('paged') === 1) return; 15580 this.load(--this.lastId, $(e.currentTarget)); 15581 this.updatePagedNumUrl( this.lastId ); 15582 }, 15583 15584 load: function load(id, $el) { 15585 this.lastId = id; 15586 this.ajaxLoader.setData({paged: id}); 15587 this.ajaxLoader.load(this.unique); 15588 this.removeIndicator(); 15589 MK.ui.loader.add($el); 15590 }, 15591 15592 onLoad: function success(e, response) { 15593 if( typeof response !== 'undefined' && response.id === this.containerId) { 15594 this.updatePagination(); 15595 this.lastId = this.ajaxLoader.getData('paged'); 15596 15597 if(response.unique === this.unique) { 15598 this.removeIndicator(); 15599 this.scrollPage(); 15600 this.$container.html(response.content); 15601 } 15602 } 15603 }, 15604 15605 updatePagination: function updatePagination() { 15606 var self = this; 15607 15608 // Hide / show arrows 15609 var isFirst = (this.ajaxLoader.getData('paged') === 1); 15610 var isLast = (this.ajaxLoader.getData('paged') === this.ajaxLoader.getData('maxPages')); 15611 15612 if(isFirst) this.$prevLink.addClass('is-vis-hidden'); 15613 else this.$prevLink.removeClass('is-vis-hidden'); 15614 15615 if(isLast) this.$nextLink.addClass('is-vis-hidden'); 15616 else this.$nextLink.removeClass('is-vis-hidden'); 15617 15618 // X of Y 15619 this.$current.html(this.ajaxLoader.getData('paged')); 15620 this.$maxPages.html(this.ajaxLoader.getData('maxPages')); 15621 15622 // Move overfloating items 15623 var displayItems = 10; 15624 var centerAt = displayItems / 2; 15625 15626 if(this.ajaxLoader.getData('maxPages') > displayItems) { 15627 this.$pageLinks.each(function(i) { 15628 15629 var id = self.lastId - centerAt; 15630 id = Math.max(id, 1); 15631 id = Math.min(id, self.ajaxLoader.getData('maxPages') - displayItems + 1); 15632 id = id + i; 15633 15634 $(this).html( id ).attr('data-page-id', id).show(); 15635 15636 if(i === 0 && id > 1) $(this).html('...'); 15637 if(i === displayItems - 1 && id < self.ajaxLoader.getData('maxPages')) $(this).html('...'); 15638 }); 15639 } else { 15640 this.$pageLinks.each(function(i) { 15641 var $link = $(this); 15642 var id = i + 1; 15643 15644 $link.html(id).attr('data-page-id', id); 15645 15646 if( self.ajaxLoader.getData('maxPages') === 1) { 15647 self.$pageLinks.hide(); 15648 } else { 15649 if(i > self.ajaxLoader.getData('maxPages') - 1) $link.hide(); 15650 else $link.show(); 15651 } 15652 15653 }); 15654 } 15655 15656 // Highlight current only 15657 this.$pageLinks.filter('[data-page-id="' + this.ajaxLoader.getData('paged') + '"]' ).addClass('current-page') 15658 .siblings().removeClass('current-page'); 15659 15660 }, 15661 15662 scrollPage: function scrollPage() { 15663 var containerOffset = this.$container.offset().top; 15664 var offset = containerOffset - MK.val.offsetHeaderHeight( containerOffset ) - 20; 15665 15666 this.$container.find('a:first').focus() 15667 MK.utils.scrollTo( offset ); 15668 }, 15669 15670 removeIndicator: function removeIndicator() { 15671 MK.ui.loader.remove('.js-pagination-page, .js-pagination-next, .js-pagination-prev'); 15672 }, 15673 15674 /** 15675 * Set some actions when archive/category page is loaded. Actions list: 15676 * - Select the correct paged ID on pagination list. 15677 * - Set current paged ID on the label. 15678 * - Add event listener onpopstate for handling prev/next button of Browser URL. 15679 * - Set info for updatePagedNumUrl() about request comes from popstate. 15680 * 15681 * @since 5.9.8 15682 */ 15683 onInitLoad: function onInitLoad() { 15684 var initPagedID = this.$pagination.data( 'init-pagination' ); 15685 if ( initPagedID && initPagedID > 1 ) { 15686 this.$current.html( initPagedID ); 15687 this.$pageLinks.filter( '[data-page-id="' + initPagedID + '"]' ).addClass( 'current-page' ).siblings().removeClass( 'current-page' ); 15688 } 15689 15690 // Run popstate only if it's supported by the browser. 15691 if ( 'onpopstate' in window ) { 15692 var thisPop = this; 15693 window.onpopstate = function( event ) { 15694 var id = 1; 15695 15696 // At start, state is always null. So, we should check it before processing. 15697 if ( typeof event.state === 'object' && event.state ) { 15698 var state = event.state; 15699 15700 // Set paged ID for updating page. 15701 if ( state.hasOwnProperty( 'MkPagination' ) ) { 15702 var currentState = state.MkPagination; 15703 if ( currentState.hasOwnProperty( 'paged' ) ) { 15704 id = parseFloat( currentState.paged ); 15705 } 15706 } 15707 } else { 15708 id = parseFloat( thisPop.getURLPagedID() ); 15709 } 15710 15711 // Tell updatePagedNumUrl() if request come from popstate. 15712 thisPop.popState = true; 15713 thisPop.$pageLinks.filter( '[data-page-id="' + id + '"]' ).trigger( 'click' ); 15714 } 15715 } 15716 }, 15717 15718 /** 15719 * Update current pagination browser URL by adding/changing paged number. Only run if 15720 * the browser support pushState and the request not coming from popstate. 15721 * 15722 * WordPress has some ways to set paged number: 15723 * 1. page/[number], paged=[number] will be directed here. 15724 * 2. page=[number] 15725 * So, we should check which one the request is used here. 15726 * 15727 * @since 5.9.8 15728 */ 15729 updatePagedNumUrl: function updatePagedNumUrl( id ) { 15730 // Check pushState browser support and ignore if request come from popstate. 15731 if ( 'history' in window && 'pushState' in history && id && ! this.popState ) { 15732 var fullPage = this.pagePathname + this.pageSearch; 15733 var isQueryPage = false;
15734 15735 // Style 1 - /page/[number], as default value. 15736 var newPage = 'page/' + id + '/'; 15737 var expPage = /page\/\d+\/?/; 15738 var result = this.pagePathname.match( /\/page\/\d+/ ); 15739 var isPagedExist = ( result ) ? true : false; 15740 15741 // Style 2 - ?page=[number], only run if /page/ is not exist and URL query var exist. 15742 if ( ! isPagedExist && this.pageSearch ) { 15743 isQueryPage = this.pageSearch.match( /page\=\d+/ ); 15744 if ( isQueryPage ) { 15745 newPage = 'page=' + id; 15746 expPage = /page\=\d+/; 15747 } 15748 } 15749 15750 // If page number is 1, remove paged number from URL. 15751 if ( id === 1 ) { 15752 newPage = ''; 15753 if ( isQueryPage ) { 15754 expPage = ( this.pageSearch.match( /\&+/ ) ) ? /page\=\d+\&?/ : /\?page\=\d+\&?/; 15755 } 15756 } 15757 15758 // Set new pathname. Do replacement only if the new pathname contains paged number. 15759 var newURL = this.pagePathname + newPage + this.pageSearch; 15760 if ( fullPage.match( expPage ) ) { 15761 newURL = fullPage.replace( expPage, newPage ); 15762 } 15763 15764 // Set history state and return popstate back to fal
15764se. 15765 var historyState = { 15766 MkPagination: { 15767 url: newURL, 15768 paged: id 15769 } 15770 } 15771 this.popState = false; 15772 15773 // Push new pathname to display/hide the paged number. 15774 window.history.pushState( historyState, null, newURL ); 15775 } 15776 this.popState = false; 15777 }, 15778 15779 /** 15780 * Get current URL page ID. Notes: 15781 * 1. page/[number], paged=[number] will be directed here. 15782 * 2. page=[number] 15783 * 15784 * @return {integer} Current paged ID. Default 1. 15785 */ 15786 getURLPagedID: function getURLPagedID() { 15787 var pathname = window.location.pathname; 15788 var search = window.location.search; 15789 var pagedId = 1; 15790 var result = ''; 15791 var isPagedExist = false; 15792 15793 // Search based on style 1. 15794 result = pathname.match( /\/page\/(\d+)/ ); 15795 if ( result ) { 15796 isPagedExist = true; 15797 pagedId = ( result.hasOwnProperty( 1 ) ) ? result[1] : 1; 15798 } 15799 15800 // Search based on style 2. 15801 if ( ! isPagedExist && search ) { 15802 result = search.match( /page\=(\d+)/ ); 15803 if ( result ) { 15804 isPagedExist = true; 15805 pagedId = ( result.hasOwnProperty( 1 ) ) ? result[1] : 1; 15806 } 15807 } 15808 15809 return pagedId; 15810 } 15811 }; 15812 15813}(jQuery)); 15814(function( $ ) { 15815 'use strict'; 15816 15817 var val = MK.val, 15818 utils = MK.utils; 15819 15820 MK.component.Parallax = function( el ) { 15821 var self = this, 15822 $this = $( el ), 15823 obj = $this[0], 15824 $window = $( window ), 15825 container = document.getElementById( 'mk-theme-container' ), 15826 config = $this.data( 'parallax-config' ), 15827 $holder = $( config.holder ), 15828 headerHeight = null, 15829 offset = null, 15830 elHeight = null, 15831 ticking = false, 15832 isMobile = null; 15833 15834 15835 var clientRect = null; 15836 15837 var update = function() { 15838 // Clear styles to check for natural styles 15839 // then apply position and size 15840 obj.style.transform = null; 15841 obj.style.top = null; 15842 obj.style.bottom = null; 15843 15844 isMobile = MK.utils.isMobile(); 15845 15846 if( isMobile ) { 15847 $this.css( 'height', '' ); 15848 return; 15849 } 15850 15851 clientRect = $this[ 0 ].getBoundingClientRect(); 15852 offset = clientRect.top; 15853 elHeight = clientRect.height; 15854 headerHeight = val.offsetHeaderHeight( offset ); 15855 offset = offset - headerHeight + val.scroll(); 15856 15857 setPosition(); 15858 setSize( ); 15859 }; 15860 15861 15862 var h = 0, 15863 winH = 0, 15864 proportion = 0, 15865 height = 0; 15866 15867 // Position and background attachement should me moved to CSS but we repair it high specificity here as styles are not reliable currently 15868 var setSize = function() { 15869 $this.css( 'height', '' ); 15870 winH = $window.height() - headerHeight; 15871 h = obj.getBoundingClientRect().height; 15872 15873 if( config.speed <= 1 && config.speed > 0 ) { 15874 if( offset === 0 ) { 15875 $this.css({ 15876 backgroundAttachment: 'scroll', 15877 'will-change': 'transform' 15878 }); 15879 } else { 15880 $this.css({ 15881 height : h + ( (winH - h) * config.speed ), 15882 backgroundAttachment: 'scroll', 15883 'will-change': 'transform' 15884 }); 15885 } 15886 15887 } else if ( config.speed > 1 && h <= winH ) { 15888 $this.css({ 15889 // good for full heights - 2 because it's viewable by 2 screen heights 15890 height: ( winH + ( ( winH * config.speed ) - winH ) * 2 ), 15891 top: -( ( winH * config.speed ) - winH ), 15892 backgroundAttachment: 'scroll', 15893 'will-change': 'transform' 15894 }); 15895 15896 } else if ( config.speed > 1 && h > winH ) { 15897 proportion = h / winH; 15898 height = ( winH + ( ( winH * config.speed ) - winH ) * (1 + proportion) ); 15899 15900 $this.css({ 15901 height: height, 15902 top: -( height - (winH * config.speed) ), 15903 backgroundAttachment: 'scroll', 15904 'will-change': 'transform' 15905 }); 15906 15907 } else if ( config.speed < 0 && h >= winH ) { 15908 height = h * (1 - config.speed); 15909 $this.css({ 15910 height: height + (height - h), 15911 top: h - height, 15912 backgroundAttachment: 'scroll', 15913 'will-change': 'transform' 15914 }); 15915 15916 } else if ( config.speed < 0 && h < winH ) { 15917 // candidate to change 15918 var display = (winH + h) / winH; 15919 height = h * -config.speed * display; 15920 $this.css({ 15921 height: h + (height * 2), 15922 top: -height, 15923 backgroundAttachment: 'scroll', 15924 'will-change': 'transform' 15925 }); 15926 } 15927 }; 15928 15929 15930 var currentPoint = null, 15931 progressVal = null, 15932 startPoint = null, 15933 endPoint = null, 15934 $opacityLayer = config.opacity ? $this.find( config.opacity ) : null, 15935 scrollY = null; 15936 15937 var setPosition = function() { 15938 startPoint = offset - winH; 15939 endPoint = offset + elHeight + winH - headerHeight; 15940 scrollY = val.scroll(); 15941 15942 if( scrollY < startPoint || scrollY > endPoint ) { 15943 ticking = false;
15944 return; 15945 } 15946 15947 currentPoint = (( -offset + scrollY ) * config.speed); 15948 15949 $this.css({ 15950 '-webkit-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15951 '-moz-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15952 '-ms-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15953 '-o-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15954 'transform': 'translateY(' + currentPoint + 'px) translateZ(0)' 15955 }); 15956 15957 ticking = false; 15958 }; 15959 15960 15961 var requestTick = function() { 15962 if( !ticking && !isMobile ) { 15963 ticking = true; 15964 window.requestAnimationFrame( setPosition ); 15965 } 15966 }; 15967 15968 15969 var init = function() { 15970 // Disable scroll effects when smooth scroll is disabled 15971 if( !MK.utils.isSmoothScroll ) { return; } 15972 15973 update(); 15974 setTimeout(update, 100); 15975 $window.on( 'load', update ); 15976 $window.on( 'resize', update ); 15977 window.addResizeListener( container, update ); 15978 15979 $window.on( 'scroll', requestTick ); 15980 }; 15981 15982 15983 return { 15984 init : init 15985 }; 15986 }; 15987 15988})( jQuery ); 15989(function($) { 15990 'use strict'; 15991 15992 MK.component.Preloader = function(el) { 15993 this.el = el; 15994 }; 15995 15996 MK.component.Preloader.prototype = { 15997 init: function init() { 15998 this.cacheElements(); 15999 this.bindEvents(); 16000 }, 16001 16002 cacheElements: function cacheElements() { 16003 this.$preloader = $(this.el); 16004 }, 16005 16006 bindEvents: function bindEvents() { 16007 this.onLoad(); // all components inited on page load 16008 }, 16009 16010 onLoad: function onLoad() { 16011 setTimeout(this.hidePreloader.bind(this), 300); 16012 }, 16013 16014 hidePreloader: function hidePreloader() { 16015 this.$preloader.hide(); 16016 } 16017 }; 16018 16019}(jQuery)); 16020 16021(function($) { 16022 'use strict'; 16023 16024 // Image added for proportional scaling 16025 MK.ui.loader = { 16026 tpl : function() { 16027 return '<div class="mk-loading-indicator">' + 16028 '<div class="mk-loading-indicator__inner">' + 16029 '<div class="mk-loading-indicator__icon"></div>' + 16030 '<img style="height:100%; width:auto;" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">' + 16031 '</div>' + 16032 '</div>'; 16033 }, 16034 16035 add : function(item) { 16036 $(item).append(this.tpl); 16037 }, 16038 16039 remove : function(item) { 16040 if(item) $(item).find('.mk-loading-indicator').remove(); 16041 else $('.mk-loading-indicator').remove(); 16042 } 16043 }; 16044 16045}(jQuery)); 16046(function( $ ) { 16047 // IE / Edge fix for fixed positioned elements 16048 // MS clip path doesnt redraw properly so we expirience similar bug like with background attachement fixed on Chrome 16049 if (MK.utils.browser.name === 'Edge' || MK.utils.browser.name === 'IE') { 16050 var val = 1; 16051 var $edgeClipper = $('.mk-slider-slide'); // edge slider 16052 var $sectionClipper = $('.clipper-true'); // edge slider 16053 var $bgLayer = $('.background-layer'); // page section 16054 16055 var onScroll = function onScroll() { 16056 val *= -1; 16057 if( $edgeClipper.length ) $edgeClipper.each( redraw ); 16058 if( $sectionClipper.length ) $sectionClipper.each( redraw ); 16059 if( $bgLayer.length ) $bgLayer.each( redraw ); 16060 }; 16061 16062 var redraw = function redraw() { 16063 $(this).css('margin-top', val / 100); 16064 }; 16065 16066 $(window).on("scroll", function () { 16067 window.requestAnimationFrame(onScroll); 16068 }); 16069 } 16070}(jQuery)); 16071MK.component.ResponsiveImageSetter = (function ($) { 16072 16073 'use strict'; 16074 16075 var module = {}; 16076 16077 16078 /*---------------------------------------------------------------------------------*/ 16079 /* Private Variables 16080 /*---------------------------------------------------------------------------------*/ 16081 16082 var viewportClass = getViewportClass(); 16083 var isRetina = window.devicePixelRatio >= 2; 16084 16085 16086 /*---------------------------------------------------------------------------------*/ 16087 /* Private Methods 16088 /*---------------------------------------------------------------------------------*/ 16089 16090 function run($imgs) { 16091 $imgs.filter( function() { 16092 return !this.hasAttribute("mk-img-src-setted"); 16093 }).each(setSrcAttr); 16094 } 16095 16096 function setSrcAttr() { 16097 var $img = $(this); 16098 var set = $img.data('mk-image-src-set'); 16099 // Set src attribute to img link suitable for our logic. It will load the image. 16100 if(set['responsive'] === 'false' && isRetina && set['2x']) $img.attr('src', set['2x']); 16101 else if(set['responsive'] === 'false') $img.attr('src', set.default); 16102 else if(viewportClass === 1 && isRetina && set['2x']) $img.attr('src', set['2x']); // default x2 for retina 16103 else if(viewportClass === 0 && set.mobile) $img.attr('src', set.mobile); 16104 else $img.attr('src', set.default); 16105 16106 $img.load(function() { 16107 $(window).trigger('mk-image-loaded') 16108 }) 16109 } 16110 16111 function getViewportClass() { 16112 if(window.matchMedia('(max-width: 736px)').matches) return 0; 16113 else return 1; 16114 } 16115 16116 function handleResize($imgs) { 16117 if(!$imgs.length) return;
16117 // Do not run if empty collection 16118 var currentViewportClass = getViewportClass(); 16119 // We don't need to reload bigger images when screen size is decreasing as browser already performs resize operation. 16120 // Run update on for increasing screen size 16121 if( currentViewportClass > viewportClass) { 16122 viewportClass = currentViewportClass; // update for further reference 16123 run($imgs); 16124 } 16125 } 16126 16127 16128 /*---------------------------------------------------------------------------------*/ 16129 /* Public Methods 16130 /*---------------------------------------------------------------------------------*/ 16131 16132 module.init = function ($imgs) { 16133 16134 // Do not run if empty collection 16135 if(!$imgs.length) return; 16136 16137 // Run and bind to events 16138 run($imgs); 16139 $imgs.attr('mk-img-src-setted', ''); 16140 16141 }; 16142 16143 module.onResize = function ($imgs) { 16144 16145 $(window).on( 'resize', MK.utils.throttle( 500, function() { 16146 handleResize($imgs); 16147 })); 16148 16149 16150 }; 16151 16152 16153 module.handleAjax = function () { 16154 setTimeout(function ajaxDelayedCallback() { // give it a chance to insert content first 16155 var $newImgs = $('img[data-mk-image-src-set]').filter( function() { 16156 return !this.hasAttribute("mk-lazyload"); 16157 }); 16158 if(!$newImgs.length) return; 16159 run($newImgs); 16160 }, 100); 16161 } 16162 16163 16164 return module; 16165 16166}(jQuery)); 16167 16168 16169jQuery(function($) { 16170 16171 var init = function init() { 16172 // Get All Responsive Images 16173 $allImages = $('img[data-mk-image-src-set]').filter(function(index) { 16174 var isNotPortfolioImage = !$(this).hasClass('portfolio-image'), 16175 isNotBlogImage = $(this).closest('.mk-blog-container').length == 0, 16176 isNotSwiperImage = !$(this).hasClass('swiper-slide-image'), 16177 isNotGalleryImage = !$(this).hasClass('mk-gallery-image'); 16178 return isNotPortfolioImage && isNotBlogImage && isNotSwiperImage && isNotGalleryImage; 16179 });; 16180 16181 // Handle the resize 16182 MK.component.ResponsiveImageSetter.onResize($allImages); 16183 16184 MK.component.ResponsiveImageSetter.init($allImages); 16185 16186 MK.utils.eventManager.subscribe('ajaxLoaded', MK.component.ResponsiveImageSetter.handleAjax ); // ajax loops 16187 MK.utils.eventManager.subscribe('ajax-preview', MK.component.ResponsiveImageSetter.handleAjax ); // ajax portfolio 16188 MK.utils.eventManager.subscribe('quickViewOpen', MK.component.ResponsiveImageSetter.handleAjax ); 16189 }
16190 init(); 16191 $(window).on('vc_reload', init); 16192 16193}); 16194 16195 16196 16197(function($) { 16198 'use strict'; 16199 16200 // 16201 // Constructor 16202 // 16203 // ///////////////////////////////////////////////////////// 16204 16205 MK.ui.Slider = function( container, config ) { 16206 16207 var defaults = { 16208 slide : '.mk-slider-slide', 16209 nav : '.mk-slider-nav', 16210 effect : 'roulete', 16211 ease : 'easeOutQuart', // should not be changed, remove 16212 slidesPerView : 1, 16213 slidesToView : 1, 16214 transitionTime : 700, 16215 displayTime : 3000, 16216 autoplay : true, 16217 hasNav : true, 16218 hasPagination : true, 16219 paginationTpl : '<span></span>', 16220 paginationEl : '#pagination', 16221 draggable : true, 16222 fluidHeight : false, 16223 pauseOnHover : false, 16224 lazyload : false, 16225 activeClass : 'is-active', 16226 edgeSlider : false, 16227 spinnerTpl : '<div class="mk-slider-spinner-wrap"><div class="mk-slider-spinner-fallback"></div><svg class="mk-slider-spinner" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"><circle class="mk-slider-spinner-path" fill="none" stroke-width="4" stroke-linecap="round" cx="33" cy="33" r="30"></circle></svg></div>', 16228 onInitialize : function() {}, 16229 onAfterSlide : function( id ) {}, 16230 onBeforeSlide : function( id ) {} 16231 }; 16232 16233 this.state = { 16234 id : 0, 16235 moveForward : true, 16236 running : false, 16237 zIFlow : null, 16238 stop : false, 16239 }; 16240 16241 this.config = $.extend( defaults, config ); 16242 this.container = container; 16243 16244 this.initPerView = this.config.slidesPerView; 16245 16246 // Timer holder 16247 this.activeTimer = null; 16248 this.autoplay = null; 16249 this.timer = null; 16250 this.timerRemaining = parseInt(this.config.displayTime); 16251 16252 // Boolean 'Em All, Making sure it's not string 16253 this.config.lazyload = JSON.parse(this.config.lazyload); 16254 this.config.edgeSlider = JSON.parse(this.config.edgeSlider); 16255 16256 // Image Loader Instance 16257 this.imageLoader = null; 16258 16259 // Add abort command to imagesLoaded, Placing it inside script makes it to work with different versions 16260 // of imagesLoaded if loaded by other Plugins 16261 imagesLoaded.prototype.abort = function() { 16262 this.progress = this.complete = function() { }; 16263 }; 16264 }; 16265 16266 16267 16268 // 16269 // Shared methods 16270 // 16271 // ///////////////////////////////////////////////////////// 16272 16273 MK.ui.Slider.prototype = { 16274 16275 init : function() { 16276 this.setPerViewItems(); 16277 this.cacheElements(); 16278 this.getSlideSize(); 16279 this.bindEvents(); 16280 this.setSize(); 16281 this.setPos(); 16282 16283 // Hack for preparing 'prev' on first click if needed 16284 this.updateId( -1 ); 16285 this.updateId( 1 ); 16286 16287 this.val = this.dynamicVal(); 16288 this.timeline = this.prepareTimeline( this.config.transitionTime ); 16289 16290 this.timeline.build(); 16291 16292 if( this.config.hasPagination ) { this.buildPagination(); } 16293 16294 if( this.config.autoplay && document.hasFocus() ) { this.setTimer(); } 16295 16296 if( typeof this.config.onInitialize === 'function' ) { 16297 this.config.onInitialize( this.slides ); 16298 } 16299 16300 if( this.config.fluidHeight === true ) { 16301 $( this.slides ).height( 'auto' ); 16302 $( this.container ).css( 'transition', 'height ' + 200 + 'ms ease-out' ); 16303 this.setHeight( 0 ); 16304 } 16305 16306 16307 if( this.config.fluidHeight === 'toHighest' ) { 16308 this.setHeightToHighest(); 16309 } 16310 16311 // Create timer per slide if required 16312 $(this.slides).each(this.createTimer); 16313 16314 // If it's Edge Slider and Lazy Load is enabled 16315 if ( this.config.lazyload && this.config.edgeSlider ) { 16316 16317 // If It's not a Video Slide 16318 if ( $(this.slides[this.state.id]).find('video').length === 0 ) { 16319 // Set the first slide's BG image 16320 var $slideImg = $(this.slides[this.state.id]).children('[data-mk-img-set]'); 16321 MK.component.BackgroundImageSetter.init( $slideImg ); 16322 } 16323 $(this.config.spinnerTpl).prependTo( this.$slides ); 16324 16325 } else { 16326 16327 // Set all slides's BG images
16328 MK.component.BackgroundImageSetter.init( $(this.slides).children('[data-mk-img-set]') ); 16329 16330 } 16331 16332 16333 16334 }, 16335 16336 16337 cacheElements : function () { 16338 this.container = this.isNode( this.container ) ? this.container 16339 : document.querySelectorAll( this.container )[0]; 16340 this.slides = this.container.querySelectorAll( this.config.slide ); 16341 this.$slides = $(this.slides); 16342 16343 if( this.config.hasNav ) { this.$nav = $( this.config.nav ); } 16344 if( this.config.hasPagination ) { this.$pagination = $( this.config.paginationEl ); } 16345 }, 16346 16347 16348 bindEvents : function() { 16349 var $window = $( window ); 16350 16351 if( this.config.slidesPerView > 1 ) { $window.on( 'resize', this.setPerViewItems.bind( this ) ); } 16352 if( this.config.hasNav ) { this.eventsNav(); } 16353 if( this.config.hasPagination ) { this.eventsPag(); } 16354 if( this.config.draggable ) { this.dragHandler(); } 16355 if( this.config.autoplay ) { 16356 $window.on( 'focus', this.windowActive.bind( this ) ); 16357 $window.on( 'blur', this.windowInactive.bind( this ) ); 16358 } 16359 if( this.config.pauseOnHover ) { 16360 $(this.container).on( 'mouseleave', this.setTimer.bind( this ) ); 16361 $(this.container).on( 'mouseenter', this.unsetTimer.bind( this ) ); 16362 } 16363 if( this.config.fluidHeight === 'toHighest' ) { 16364 $window.on( 'resize', this.setHeightToHighest.bind( this ) ); 16365 } 16366 }, 16367 16368 16369 setPerViewItems: function() { 16370 if(window.matchMedia( '(max-width: 500px)' ).matches) { this.config.slidesPerView = 1; } 16371 else if(window.matchMedia( '(max-width: 767px)' ).matches && this.initPerView >= 2 ) { this.config.slidesPerView = 2; } 16372 else if(window.matchMedia( '(max-width: 1024px)' ).matches && this.initPerView >= 3 ) { this.config.slidesPerView = 3; } 16373 else { this.config.slidesPerView = this.initPerView; } 16374 16375 if( typeof this.slides === 'undefined' ) return; 16376 this.getSlideSize(); 16377 this.setSize(); 16378 this.setPos(); 16379 this.timeline = this.prepareTimeline( this.config.transitionTime ); 16380 this.timeline.build(); 16381 }, 16382 16383 16384 eventsNav : function() { 16385 this.$nav.on( 'click', 'a', this.handleNav.bind( this ) ); 16386 }, 16387 16388 16389 eventsPag : function() { 16390 this.$pagination.on( 'click', 'a', this.handlePagination.bind( this ) ); 16391 }, 16392 16393 16394 handleNav : function( e ) { 16395 e.preventDefault(); 16396 16397 if( this.state.running ) { return; } 16398 this.state.running = true; 16399 16400 var $this = $( e.currentTarget ), 16401 moveForward = $this.data( 'direction' ) === 'next'; 16402 16403 16404 if( this.config.autoplay ) { 16405 this.unsetTimer(); 16406 setTimeout( this.setTimer.bind( this ), this.config.transitionTime ); 16407 } 16408 16409 this.state.moveForward = moveForward; 16410 this.timeline.build(); 16411 this.timeline.play(); 16412 16413 this.setActive( this.nextId( moveForward ? 1 : -1 ) ); 16414 if( this.config.fluidHeight ) { this.setHeight( this.nextId( moveForward ? 1 : -1 ) ); } 16415 }, 16416 16417 16418 handlePagination : function( e ) { 16419 e.preventDefault(); 16420 16421 var $this = $( e.currentTarget ), 16422 id = $this.index(); 16423 16424 this.goTo( id ); 16425 }, 16426 16427 16428 reset: function() { 16429 this.state.stop = true; 16430 this.state.id = 0; 16431 this.setPos(); 16432 this.unsetTimer(); 16433 this.setTimer(); 16434 }, 16435 16436 16437 goTo : function(id) { 16438 if( this.state.running ) { return; } 16439 this.state.running = true; 16440 16441 var lastId = this.state.id; 16442 16443 if( lastId === id ) { 16444 return; 16445 } else if( lastId < id ) { 16446 this.state.moveForward = true; 16447 } else { 16448 this.state.moveForward = false; 16449 } 16450 16451 if( this.config.autoplay ) { 16452 this.unsetTimer(); 16453 setTimeout( this.setTimer.bind( this ), this.config.transitionTime ); 16454 } 16455 16456 this.timeline.build( Math.abs( lastId - id ) ); 16457 this.timeline.play(); 16458 16459 this.setActive( id ); 16460 if( this.config.fluidHeight ) { this.setHeight( id ); } 16461 }, 16462 16463 16464 windowActive : function() { 16465 this.setTimer(false, true); 16466 $(this.container).removeClass('is-paused'); 16467 }, 16468 16469 16470 windowInactive : function() { 16471 this.unsetTimer(); 16472 $(this.container).addClass('is-paused'); 16473 }, 16474 16475 16476 updateId : function( val ) { 16477 this.state.id = this.nextId(val); 16478 }, 16479 16480 nextId : function( val ) { 16481 var len = this.slides.length, 16482 insertVal = this.state.id + val; 16483 insertVal = ( insertVal >= 0 ) ? insertVal : len + val; 16484 insertVal = ( insertVal >= len ) ? 0 : insertVal; 16485 16486 return insertVal; 16487 }, 16488 16489 16490 setStyle : function( obj, style ) { 16491 var hasT = style.transform, 16492 t = { 16493 x : ( hasT ) ? style.transform.translateX : null, 16494 y : ( hasT ) ? style.transform.translateY : null, 16495 scale : ( hasT ) ? style.transform.scale : null, 16496 rotate : ( hasT ) ? style.transform.rotate : null, 16497 rotateX : ( hasT ) ? style.transform.rotateX : null, 16498 rotateY : ( hasT ) ? style.transform.rotateY : null 16499 }, 16500 z = 'translateZ(0)', 16501 x = (t.x) ? 'translateX(' + t.x + '%)' : 'translateX(0)', 16502 y = (t.y) ? 'translateY(' + t.y + '%)' : 'translateY(0)', 16503 s = (t.scale) ? 'scale(' + t.scale + ')' : 'scale(1)', 16504 r = (t.rotate) ? 'rotate(' + t.rotate + 'deg)' : 'rotate(0)', 16505 rX = (t.rotateX) ? 'rotateX(' + t.rotateX + 'deg)' : '', 16506 rY = (t.rotateY) ? 'rotateY(' + t.rotateY + 'deg)' : '', 16507 16508 o = style.opacity, 16509 h = style.height, 16510 w = style.width; 16511 16512 var c = z + x + y + s + r + rX + rY; 16513 16514 if( c.length ) { 16515 obj.style.webkitTransform = c; 16516 obj.style.msTransform = c; 16517 obj.style.transform = c; 16518 } 16519 16520 if( typeof o === 'number' ) { obj.style.opacity = o; } 16521 if( h ) { obj.style.height = h + '%'; } 16522 if( w ) { obj.style.width = w + '%'; } 16523 }, 16524 16525 16526 setPos : function() { 16527 if( typeof this.slides === 'undefined' ) return; 16528 var id = this.state.id, 16529 i = 0, 16530 len = this.slides.length, 16531 animation = this.animation[ this.config.effect ], 16532 axis = animation.axis, 16533 animNext = animation.next, 16534 animActi = animation.active, 16535 animPrev = animation.prev, 16536 perView = this.config.slidesPerView, 16537 slideId = null, 16538 zIFlow = null, 16539 style = {}; 16540 16541 style.transform = {}; 16542 16543 16544 for( ; i < len; i += 1 ) { 16545 if(i < perView) { 16546 // Position for visible slides. Apply active styles 16547 style = animActi; 16548 style.transform[ 'translate' + axis ] = i * 100; 16549 } else { 16550 // Rest slides move after edge based on axis and moveForward. Apply Next / Prev styles 16551 style = this.state.moveForward ? animNext : animPrev; 16552 style.transform[ 'translate' + axis ] = this.state.moveForward ? perView * 100 : -100; 16553 } 16554 16555 this.slides[ i ].style.zIndex = 0; 16556 16557 slideId = ( i + id ) % len; 16558 this.setStyle( this.slides[ slideId ], style ); 16559 } 16560 }, 16561 16562 16563 // When we're setting animation along Y axis we're going to set up height 16564 // otherwise width. It is shared amongst all slides 16565 setSize : function() { 16566 if( typeof this.slides === 'undefined' ) return; 16567 var i = 0, 16568 len = this.slides.length, 16569 axis = this.animation[ this.config.effect ].axis, 16570 slideSize = this.slideSize, 16571 style = {}; 16572 16573 if( axis === 'Y' ) { 16574 style.height = slideSize[ axis ]; 16575 } else { 16576 style.width = slideSize[ axis ]; 16577 } 16578 16579 for( ; i < len; i += 1 ) { 16580 this.setStyle( this.slides[ i ], style ); 16581 } 16582 }, 16583 16584 16585 setHeight : function( id ) { 16586 var $slides = $( this.slides ), 16587 $activeSlide = $slides.eq( id ); 16588 16589 var currentHeight = $activeSlide.height(); 16590 $( this.container ).height( currentHeight ); 16591 }, 16592 16593 16594 setHeightToHighest : function() { 16595 // this is becouse of alliginig woocommrece carousel. Too much DOM 16596 // Refactor someday 16597 var $slides = $( this.slides ), 16598 height = 0; 16599 16600 $slides.each(function() { 16601 height = Math.max(height, $(this).find('> div').outerHeight()); 16602 }); 16603 16604 $( this.container ).height( height ); 16605 }, 16606 16607 16608 // Little utility inspired by GreenSock. 16609 // We export this to this.timeline on init. 16610 prepareTimeline : function( time ) { 16611 var self = this, 16612 iteration = 0, 16613 totalIter = time / (1000 / 60), 16614 animLoop = [], 16615 aL = 0, // animation length 16616 loops = 1, 16617 ease = this.config.ease, 16618 currentStyle, timeProg, 16619 build, move, add, play, reverse, progress, kill; 16620 16621 16622 // Build constants, run them only once 16623 // take out possibly 16624 var len = this.slides.length, 16625 perView = this.config.slidesPerView, 16626 animation = this.animation[ this.config.effect ], 16627 animAxis = animation.axis, 16628 animNext = animation.next, 16629 animActi = animation.active, 16630 animPrev = animation.prev, 16631 style = {}, 16632 slideId = null, 16633 zIFlow = null; 16634 16635 style.transform = {}; 16636 16637 16638 build = function( repeats ) { 16639 var currentEase = ease; 16640 loops = repeats || loops; 16641 16642 // console.log('build', loops); 16643 16644 if( !loops ) { return; } 16645 if( loops > 1 ) { 16646 currentEase = 'linearEase'; 16647 } 16648 16649 // clean before running new build 16650 kill(); 16651 // set new positions 16652 self.setPos(); 16653 16654 var id = self.state.id, 16655 moveForward = self.state.moveForward, 16656 i = 0, 16657 axisMove = (moveForward) ? -100 : 100; 16658
16659 for( ; i <= perView; i += 1 ) { 16660 slideId = ( (moveForward) ? i + id : i + id - 1 ) % len; 16661 slideId = ( slideId < 0 ) ? len + slideId : slideId; 16662 16663 if( i === 0 ) { 16664 style = moveForward ? animPrev : animActi; 16665 } else if( i === perView ) { 16666 style = moveForward ? animActi : animNext; 16667 } else { 16668 style = animActi; 16669 } 16670 16671 zIFlow = (self.state.moveForward) ? animNext.zIndex : animPrev.zIndex; 16672 if( zIFlow ) { 16673 // console.log( zIFlow ); 16674 self.slides[ slideId ].style.zIndex = (zIFlow === '+') ? i + 1 : len - i; 16675 } 16676 16677 style.transform[ 'translate' + animAxis ] = axisMove; 16678 add( self.slides[ slideId ], style, currentEase ); 16679 } 16680 }; 16681 16682 add = function( slide, toStyles, ease ) { 16683 if( typeof slide === 'undefined' ) { 16684 throw 'Add at least one slide'; 16685 } 16686 16687 var fromStyles = slide.style, 16688 style = self.refStyle( toStyles, fromStyles ); 16689 16690 animLoop.push( [slide, style, ease] ); 16691 aL += 1; 16692 }; 16693 16694 move = function( startProg, mode ) { 16695 if (isTest) return; 16696 var currentTotalIter = totalIter; 16697 16698 if( loops > 1 ) { 16699 currentTotalIter = totalIter / 5; 16700 } 16701 16702 if( !self.state.running ) { self.state.running = true; } 16703 16704 if( startProg ) { 16705 // update iteration val to cached outside var 16706 // ceil to handle properly play after mouse up / touch end 16707 iteration = Math.ceil(startProg * currentTotalIter); 16708 } 16709 16710 timeProg = iteration / currentTotalIter; 16711 progress( timeProg ); 16712 16713 // Break loop 16714 if( iteration >= currentTotalIter && mode === 'play' || 16715 iteration <= 0 && mode === 'reverse' ) { 16716 16717 self.state.running = false; 16718 iteration = 0; 16719 kill(); 16720 self.updateId( self.state.moveForward ? 1 : -1 ); 16721 // If we're creating multiple animation loop we trigger outside only first pass to start all game. 16722 // the rest are triggered as callback 16723 loops -= 1; 16724 if( loops > 0 ) { 16725 build(); 16726 play(); 16727 } 16728 16729 // if we run all loops reset back the default value 16730 if( !loops ) { 16731 loops = 1; 16732 self.timerRemaining = parseInt(self.config.displayTime); 16733 self.config.onAfterSlide( self.state.id ); 16734 } 16735 16736 return; 16737 } 16738 16739 // Run in given mode 16740 if( mode === 'play') { 16741 iteration += 1; 16742 } else { 16743 iteration -= 1; 16744 } 16745 16746 requestAnimationFrame( function() { 16747 if(self.state.stop) return; 16748 move( 0, mode ); 16749 }); 16750 }; 16751 16752 play = function( startProg ) { 16753 16754 var $nextSlide = $(self.slides[ self.nextId(self.state.moveForward ? 1 : -1) ] ); 16755 16756 // If it's Edge Slider and Lazy Load is enabled and It's not a Video Slide 16757 if ( self.config.lazyload && self.config.edgeSlider ) { 16758 16759 // Set the next slide's BG Image 16760 var $slideImg = $nextSlide.find('[data-mk-img-set]'); 16761 if ( $slideImg.length ) { 16762 MK.component.BackgroundImageSetter.init( $slideImg ); 16763 } 16764 16765 } 16766 16767 self.config.onBeforeSlide( self.nextId(self.state.moveForward ? 1 : -1) ); 16768 var start = startProg || 0; 16769 iteration = 0; 16770 self.state.stop = false; 16771 move( start, 'play' ); 16772 16773 }; 16774 16775 reverse = function( startProg ) { 16776 var start = startProg || 1; 16777 move( start, 'reverse' ); 16778 }; 16779 16780 progress = function( progVal ) { 16781 var aI = 0, 16782 currentStyle; 16783 16784 for( aI; aI < aL; aI++ ) { 16785 if( progVal !== 1 && progVal !== 0 ) { 16786 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], animLoop[ aI ][ 2 ] ); 16787 } else if( progVal === 1) { 16788 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], 'linearEase' ); 16789 } else if ( progVal === 0 ) { 16790 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], 'linearEase' ); 16791 } 16792 self.setStyle( animLoop[ aI ][ 0 ], currentStyle ); 16793 } 16794 }; 16795 16796 // Clear previous loop 16797 kill = function() { 16798 animLoop = []; 16799 aL = 0; 16800 }; 16801 16802 16803 return { 16804 build : build, 16805 add : add, 16806 play : play, 16807 reverse : reverse, 16808 progress : progress 16809 }; 16810 }, 16811 16812 16813 // Build reference styles. 16814 // Return object with array containig initial style and change of its value 16815 // as required for easing functions 16816 refStyle : function( toStyles, fromStyles ) { 16817 var axis = this.animation[ this.config.effect ].axis, 16818 style = {}, 16819 initVal, changeVal, endVal, dynamicEnd, styleProp, transProp, transform; 16820 16821 for( styleProp in toStyles ) { 16822 16823 if( styleProp === 'transform' ) { 16824 transform = this.getTransforms( fromStyles ); 16825 style.transform = {}; 16826 16827 for( transProp in toStyles.transform ) { 16828 // don't care about z 16829 if( transProp === 'translateZ' ) { continue; } 16830 16831 initVal = transform[ transProp ] || 0; // if it is undefined it means it's 0 16832 dynamicEnd = ( transProp === 'translate' + axis ) ? initVal : 0; 16833 endVal = toStyles.transform[ transProp ] + dynamicEnd; // it is dynamic, based on slide position in current set 16834 changeVal = endVal - initVal; 16835 style.transform[ transProp ] = [ initVal, changeVal ]; 16836 } 16837 } else if( styleProp === 'zIndex' ) { 16838 // console.log( 'z' ); 16839 continue; 16840 } else { 16841 initVal = parseFloat( fromStyles[ styleProp ] ) || 0; // if it is undefined it means it's 0 16842 endVal = toStyles[ styleProp ]; 16843 changeVal = endVal - initVal; 16844 style[ styleProp ] = [ initVal, changeVal ]; 16845 } 16846 } 16847 16848 return style; 16849 }, 16850 16851 16852 currentStyle : function( progress, style, ease ) { 16853 var self = this, 16854 currentStyle = {}, 16855 currentVals, styleProp, transProp, prog; 16856 16857 // Redo same loop but construct currentStyle object out of cached values 16858 for( styleProp in style ) { 16859 16860 if( styleProp === 'transform' ) { 16861 currentStyle.transform = {}; 16862 16863 for( transProp in style.transform ) { 16864 // remove this line. double check first if needed by logging 16865 if( transProp === 'translateZ' ) { continue; } 16866 16867 currentVals = style.transform[ transProp ]; 16868 currentStyle.transform[ transProp ] = 16869 // (currentIteration, startValue, changeInValue, totalIterations) 16870 self.ease[ ease ]( progress, currentVals[ 0 ], currentVals[ 1 ], 1 ); 16871 } 16872 } else { 16873 currentVals = style[ styleProp ]; 16874 currentStyle[ styleProp ] = 16875 self.ease[ ease ]( progress, currentVals[ 0 ], currentVals[ 1 ], 1 ); 16876 } 16877 } 16878 16879 return currentStyle; 16880 }, 16881 16882 16883 setActive : function( id ) { 16884 var $slides = $( this.slides ), 16885 className = this.config.activeClass; 16886 16887 $slides.removeClass( className ); 16888 16889 if( this.config.hasPagination ) { 16890 var $pagination = this.$pagination.find( 'a' ); 16891 $pagination.removeClass( className ); 16892 $pagination.eq( id ).addClass( className ); 16893 } 16894 16895 if( this.activeTimer ) { 16896 clearTimeout( this.activeTimer ); 16897 if ( this.imageLoader ) { 16898 this.imageLoader.abort(); 16899 } 16900 } 16901 16902 16903 var self = this; 16904 16905 this.activeTimer = setTimeout( function() { 16906 16907 var $currentSlide = $slides.eq( id ); 16908 16909 if ( self.config.lazyload && self.config.edgeSlider ) { // If it's Edge Slider and Lazy Load is enabled 16910
16911 if ( $currentSlide.find('.mk-section-video').length && $currentSlide.children('.mk-video-section-touch').length ) { // If it's a Video Slide and has a Preview image 16912 16913 var imgSet = $currentSlide.children('.mk-video-section-touch').data('mk-img-set'); 16914 var exactImg = MK.component.BackgroundImageSetter.getImage( imgSet ); 16915 var $bgImage = $('<img>').attr('src', exactImg ); 16916 16917 self.imageLoader = imagesLoaded( $bgImage[0], function( instance ) { 16918 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 16919 setTimeout( function() { 16920 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 16921 }, 200); 16922 $currentSlide.addClass( className ); 16923 }); 16924 16925 } else if ( $currentSlide.find('.mk-section-video').length && $currentSlide.children('.mk-video-section-touch').length === 0 ) { // If it's a Video Slide and has NOT a Preview image 16926 16927 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 16928 setTimeout( function() { 16929 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 16930 }, 200); 16931 $currentSlide.addClass( className ); 16932 16933 } else { // If it's a Image Slide 16934 16935 if ( $currentSlide.children('[data-mk-img-set]').length ) { 16936 // Get the matching Image URL to start lazy loading 16937 var imgSet = $currentSlide.children('[data-mk-img-set]').data('mk-img-set'); 16938 var exactImg = MK.component.BackgroundImageSetter.getImage( imgSet ); 16939 var $bgImage = $('<img>').attr('src', exactImg ); 16940 16941 // Prevent counting time on slide until the image loads 16942 self.unsetTimer(); 16943 self.imageLoader = imagesLoaded( $bgImage[0], function( instance ) { 16944 // Hide spinner, Continue counting time on slide and show the content 16945 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 16946 setTimeout( function() { 16947 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 16948 }, 200); 16949 self.setTimer(false, false, $currentSlide.data('timer') || Number(self.config.displayTime) ); 16950 $currentSlide.addClass( className ); 16951 }); 16952 } else { 16953 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 16954 setTimeout( function() { 16955 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 16956 }, 200); 16957 self.setTimer(false, false, $currentSlide.data('timer') || Number(self.config.displayTime) ); 16958 $currentSlide.addClass( className ); 16959 } 16960 16961 } 16962 16963 } else { 16964 16965 $currentSlide.addClass( className ); 16966 16967 } 16968 16969 16970 }, this.config.transitionTime ); 16971 }, 16972 16973 createTimer : function() { 16974 16975 var $slide = $(this), 16976 video = $slide.find('video').get(0); 16977 16978 if(video) { 16979 // A hacky but reliable way to ge the video duration 16980 var interval = setInterval( function() { 16981 // If the metadata is ready 16982 if ( video.readyState > 0 ) { 16983 $slide.data('timer', (video.duration * 1000)); 16984 $slide.attr('data-timer', (video.duration * 1000)); 16985 clearInterval(interval); 16986 } 16987 }, 100); 16988 } 16989 16990 }, 16991 16992 setTimer : function( isFirst, isPaused, fixed_time ) { 16993 // check for custom timer 16994 var customTimer = this.$slides.eq(this.nextId(this.state.moveForward ? 1 : -1)).data('timer'), 16995 trans = parseInt( this.config.transitionTime ), 16996 interval = customTimer ? customTimer : parseInt( this.config.displayTime ), 16997 timer = interval + trans; 16998 16999 var self = this, 17000 first = isFirst || true, 17001 fixed_time = fixed_time || 0, 17002 create, run; 17003 17004 this.timer = true; 17005 this.lastSetTimer = Date.now(); 17006 17007 create = function() { 17008 17009 if( self.autoplay ) { clearTimeout( self.autoplay ); } 17010 if( !self.timer ) { 17011 return; 17012 } 17013 self.state.moveForward = true; 17014 self.timeline.build(); 17015 self.timeline.play(); 17016 self.setActive( self.nextId( 1 ) ); 17017 if( self.config.fluidHeight ) { self.setHeight( self.nextId( 1 ) ); } 17018 first = false; 17019 self.lastSetTimer = Date.now(); 17020 17021 run(); 17022 }; 17023 17024 run = function(newInterval) { 17025 // check for custom timer 17026 customTimer = self.$slides.eq(self.nextId(self.state.moveForward ? 1 : -1)).data('timer'); 17027 interval = customTimer ? customTimer : parseInt( self.config.displayTime ); 17028 timer = interval + trans; // update timer with current val 17029 17030 var time = newInterval || timer; 17031 self.autoplay = setTimeout( create, time ); 17032 }; 17033 17034 if ( fixed_time ) { 17035 run( fixed_time ); 17036 } else if (isPaused) { 17037 run( this.timerRemaining ); 17038 } else { 17039 run(); 17040 } 17041 }, 17042 17043 17044 unsetTimer : function() { 17045 this.timer = false;
17046 this.lastUnsetTimer = Date.now(); 17047 this.timerRemaining -= this.lastUnsetTimer - this.lastSetTimer; 17048 if( this.autoplay ) { clearTimeout( this.autoplay ); } 17049 }, 17050 17051 17052 buildPagination : function() { 17053 var i = 0, 17054 len = this.slides.length, 17055 tpl = ''; 17056 17057 for( ; i < len; i += 1 ) { 17058 tpl += '<a href="javascript:;">' + this.config.paginationTpl + '</a>'; 17059 } 17060 17061 this.$pagination.html( tpl ); 17062 this.setActive( 0 ); 17063 }, 17064 17065 17066 getSlideSize : function() { 17067 this.slideSize = { 17068 X: 100 / this.config.slidesPerView, 17069 Y: 100 / this.config.slidesPerView 17070 }; 17071 }, 17072 17073 17074 getTransforms : function( style ) { 17075 // console.log( style ); 17076 var transform = style.transform || style.webkitTransform || style.mozTransform, 17077 regex = /(\w+)\(([^)]*)\)/g, 17078 match, 17079 T = {}; 17080 17081 if( typeof transform !== 'string' ) { 17082 throw 'Transform prop is not a string.'; 17083 } 17084 17085 if( !transform ) { return; } 17086 17087 // Run regex assignment 17088 while( match = regex.exec( transform ) ) { 17089 T[ match[ 1 ] ] = parseFloat( match[ 2 ] ); 17090 } 17091 17092 return T; 17093 }, 17094 17095 isNode : function( o ) { 17096 return ( 17097 typeof Node === "object" ? o instanceof Node : 17098 o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string" 17099 ); 17100 }, 17101 17102 17103 dragHandler : function() { 17104 var self = this, 17105 $container = $( this.container ), 17106 prevBuild = false, 17107 nextBuild = false, 17108 dragging = false, 17109 buffor = 5, // helpful for decoupling with click events 17110 dragStart, dragMove, dragEnd, progress; 17111 17112 progress = function( moveX ) { 17113 return moveX / self.val.viewportW(); 17114 }; 17115 17116 dragStart = function( moveX, startX ) { 17117 // console.log( 'start', moveX, startX ); 17118 }; 17119 17120 dragMove = function( moveX ) { 17121 // console.log('move'); 17122 if( self.state.running ) return; 17123 17124 // Don't need to check for existance here 17125 17126 if( moveX < -buffor ) { 17127 17128 if( !nextBuild ) { 17129 self.state.moveForward = true; 17130 self.timeline.build(); 17131 nextBuild = true; 17132 prevBuild = false; 17133 self.unsetTimer(); 17134 } else { 17135 // turn progress into positive val 17136 self.timeline.progress( -progress( moveX ) ); 17137 } 17138 dragging = true; 17139 } else if( moveX > buffor ) { 17140 17141 if( !prevBuild ) { 17142 self.state.moveForward = false; 17143 self.timeline.build(); 17144 prevBuild = true; 17145 nextBuild = false; 17146 self.unsetTimer(); 17147 } else { 17148 self.timeline.progress( progress( moveX ) ); 17149 } 17150 dragging = true; 17151 } 17152 }; 17153 17154 dragEnd = function( moveX ) { 17155 if( dragging ) { 17156 var prog = progress( moveX ), 17157 absProg = prog < 0 ? -prog : prog; 17158 17159 if( absProg > 0.1 ) { 17160 self.timeline.play( absProg ); 17161 self.setActive( self.nextId( prog < 0 ? 1 : -1 ) ); 17162 if( self.config.fluidHeight ) { self.setHeight( self.nextId( prog < 0 ? 1 : -1 ) ); } 17163 } else { 17164 self.timeline.reverse( absProg ); 17165 // eventually move this to reverse callbacks 17166 if(prog < 0) { 17167 self.updateId( -1 ); 17168 } else { 17169 self.updateId( 1 ); 17170 } 17171 } 17172 17173 prevBuild = false; 17174 nextBuild = false; 17175 dragging = false; 17176 if( self.config.autoplay ) { self.setTimer( false ); } 17177 } 17178 }; 17179 17180 this.drag( $container, dragStart, dragMove, dragEnd ); 17181 }, 17182 17183 17184 drag : function( $el, startFn, moveFn, stopFn ) { 17185 17186 var touchX, touchY, movX, movY, go, evt, 17187 prevent, start, move, stop; 17188 17189 prevent = function( e ) { 17190 e.preventDefault(); 17191 }; 17192 17193 start = function( e ) { 17194 // $el.on("touchmove", prevent); 17195 $el.on("mousemove", prevent); 17196 $el.on("touchmove", move); 17197 $el.on("mousemove", move); 17198 17199 evt = (e.type === 'touchstart') ? e.originalEvent.touches[0] : e; 17200 touchX = evt.pageX; 17201 17202 if(typeof startFn === 'function') { 17203 startFn(movX, touchX); 17204 } 17205 }; 17206 17207 move = function( e ) { 17208 evt = (e.type === 'touchmove') ? e.originalEvent.touches[0] : e; 17209 movX = evt.pageX - touchX; 17210 17211 if(typeof moveFn === 'function') { 17212 moveFn(movX); 17213 } 17214 }; 17215 17216 stop = function( e ) { 17217 // $el.off("touchmove", prevent); 17218 $el.off("mousemove", prevent); 17219 $el.off("touchmove", move); 17220 $el.off("mousemove", move); 17221 17222 if(typeof stopFn === 'function') { 17223 stopFn(movX); 17224 } 17225 }; 17226 17227 $el.on("touchstart", start); 17228 $el.on("mousedown", start); 17229 $el.on("touchend", stop); 17230 $el.on("touchleave", stop); 17231 $el.on("touchcancel", stop);
17232 $el.on("mouseup", stop); 17233 $el.on("mouseleave", stop); 17234 }, 17235 17236 17237 dynamicVal : function() { 17238 var $window = $( window ), 17239 update, 17240 getViewportW, viewportW; 17241 17242 update = function() { 17243 viewportW = $window.width(); 17244 }; 17245 17246 getViewportW = function() { 17247 return viewportW; 17248 }; 17249 17250 update(); 17251 $window.on( 'load', update ); 17252 $window.on( 'resize', update ); 17253 17254 return { 17255 viewportW : getViewportW 17256 }; 17257 } 17258 }; 17259 17260 17261 17262 // 17263 // Set of default animations 17264 // 17265 // ///////////////////////////////////////////////////////// 17266 17267 MK.ui.Slider.prototype.animation = { 17268 17269 slide : { 17270 axis : 'X', 17271 next : { transform: {} }, 17272 active : { transform: {} }, 17273 prev : { transform: {} } 17274 }, 17275 17276 vertical_slide : { 17277 axis : 'Y', 17278 next : { transform: {} }, 17279 active : { transform: {} }, 17280 prev : { transform: {} } 17281 }, 17282 17283 perspective_flip : { 17284 axis : 'Y', 17285 next : { 17286 transform: { 17287 rotateX : 80 17288 } 17289 }, 17290 active : { 17291 transform: { 17292 rotateX : 0 17293 } 17294 }, 17295 prev : { 17296 transform: { 17297 rotateX : 0 17298 } 17299 } 17300 }, 17301 17302 zoom : { 17303 axis : 'Z', 17304 next: { 17305 opacity : 0, 17306 transform : { 17307 scale : 0.9 17308 } 17309 }, 17310 active: { 17311 opacity : 1, 17312 transform : { 17313 scale : 1 17314 } 17315 }, 17316 prev: { 17317 opacity : 0, 17318 transform : { 17319 scale : 1.1 17320 } 17321 } 17322 }, 17323 17324 fade : { 17325 axis : 'Z', 17326 next: { 17327 opacity : 0, 17328 transform : {} 17329 }, 17330 active: { 17331 opacity : 1, 17332 transform : {} 17333 }, 17334 prev: { 17335 opacity : 0, 17336 transform : {} 17337 } 17338 }, 17339 17340 kenburned : { 17341 axis : 'Z', 17342 next: { 17343 opacity : 0, 17344 transform : {} 17345 }, 17346 active: { 17347 opacity : 1, 17348 transform : {} 17349 }, 17350 prev: { 17351 opacity : 0, 17352 transform : {} 17353 } 17354 }, 17355 17356 zoom_out : { 17357 axis : 'Z', 17358 next: { 17359 zIndex : '+', 17360 opacity : 1, 17361 transform : { 17362 translateY : 100, 17363 scale : 1 17364 } 17365 }, 17366 active: { 17367 opacity : 1, 17368 transform : { 17369 translateY : 0, 17370 scale : 1 17371 } 17372 }, 17373 prev: { 17374 zIndex : '+', 17375 opacity : 0, 17376 transform : { 17377 translateY : 0, 17378 scale : 0.5 17379 } 17380 } 17381 }, 17382 17383 // Problem with Z-Flow 17384 horizontal_curtain : { 17385 axis : 'Z', 17386 next: { 17387 zIndex : '+', 17388 transform : { 17389 translateX : 100, 17390 } 17391 }, 17392 active: { 17393 transform : { 17394 translateX : 0, 17395 } 17396 }, 17397 prev: { 17398 zIndex : '+', 17399 transform : { 17400 translateX : -70, 17401 } 17402 } 17403 }, 17404 17405 roulete : { 17406 axis : 'X', 17407 next: { 17408 opacity : 0.5, 17409 transform : { 17410 scale : 0.5, 17411 rotate : 10, 17412 translateY : 20 17413 } 17414 }, 17415 active: { 17416 opacity : 1, 17417 transform : { 17418 scale : 1, 17419 rotate : 0, 17420 translateY : 0 17421 } 17422 }, 17423 prev: { 17424 opacity : 0.3, 17425 transform : { 17426 scale : 0.5, 17427 rotate : -10, 17428 translateY : 20 17429 } 17430 } 17431 } 17432 }; 17433 17434 17435 17436 // 17437 // Penner's easing library 17438 // 17439 // ///////////////////////////////////////////////////////// 17440 17441 MK.ui.Slider.prototype.ease = { 17442 /* 17443 *
17444 * TERMS OF USE - EASING EQUATIONS 17445 * 17446 * Open source under the BSD License. 17447 * 17448 * Copyright © 2001 Robert Penner 17449 * All rights reserved. 17450 * 17451 * Redistribution and use in source and binary forms, with or without modification, 17452 * are permitted provided that the following conditions are met: 17453 * 17454 * Redistributions of source code must retain the above copyright notice, this list of 17455 * conditions and the following disclaimer. 17456 * Redistributions in binary form must reproduce the above copyright notice, this list 17457 * of conditions and the following disclaimer in the documentation and/or other materials 17458 * provided with the distribution. 17459 * 17460 * Neither the name of the author nor the names of contributors may be used to endorse 17461 * or promote products derived from this software without specific prior written permission. 17462 * 17463 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17464 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17465 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 17466 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17467 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 17468 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 17469 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17470 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 17471 * OF THE POSSIBILITY OF SUCH DAMAGE. 17472 * 17473 */ 17474 linearEase : function(currentIteration, startValue, changeInValue, totalIterations) { 17475 return changeInValue * currentIteration / totalIterations + startValue; 17476 }, 17477 17478 easeInQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17479 return changeInValue * (currentIteration /= totalIterations) * currentIteration + startValue; 17480 }, 17481 17482 easeOutQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17483 return -changeInValue * (currentIteration /= totalIterations) * (currentIteration - 2) + startValue; 17484 }, 17485 17486 easeInOutQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17487 if ((currentIteration /= totalIterations / 2) < 1) { 17488 return changeInValue / 2 * currentIteration * currentIteration + startValue; 17489 } 17490 return -changeInValue / 2 * ((--currentIteration) * (currentIteration - 2) - 1) + startValue; 17491 }, 17492 17493 easeInCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17494 return changeInValue * Math.pow(currentIteration / totalIterations, 3) + startValue; 17495 }, 17496 17497 easeOutCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17498 return changeInValue * (Math.pow(currentIteration / totalIterations - 1, 3) + 1) + startValue; 17499 }, 17500 17501 easeInOutCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17502 if ((currentIteration /= totalIterations / 2) < 1) { 17503 return changeInValue / 2 * Math.pow(currentIteration, 3) + startValue; 17504 } 17505 return changeInValue / 2 * (Math.pow(currentIteration - 2, 3) + 2) + startValue; 17506 }, 17507 17508 easeInQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17509 return changeInValue * Math.pow (currentIteration / totalIterations, 4) + startValue; 17510 }, 17511 17512 easeOutQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17513 return -changeInValue * (Math.pow(currentIteration / totalIterations - 1, 4) - 1) + startValue; 17514 }, 17515 17516 easeInOutQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17517 if ((currentIteration /= totalIterations / 2) < 1) { 17518 return changeInValue / 2 * Math.pow(currentIteration, 4) + startValue; 17519 } 17520 return -changeInValue/2 * (Math.pow(currentIteration - 2, 4) - 2) + startValue; 17521 }, 17522 17523 easeInQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17524 return changeInValue * Math.pow (currentIteration / totalIterations, 5) + startValue; 17525 }, 17526 17527 easeOutQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17528 return changeInValue * (Math.pow(currentIteration / totalIterations - 1, 5) + 1) + startValue; 17529 }, 17530 17531 easeInOutQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17532 if ((currentIteration /= totalIterations / 2) < 1) { 17533 return changeInValue / 2 * Math.pow(currentIteration, 5) + startValue; 17534 } 17535 return changeInValue / 2 * (Math.pow(currentIteration - 2, 5) + 2) + startValue; 17536 }, 17537 17538 easeInSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17539 return changeInValue * (1 - Math.cos(currentIteration / totalIterations * (Math.PI / 2))) + startValue; 17540 }, 17541 17542 easeOutSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17543 return changeInValue * Math.sin(currentIteration / totalIterations * (Math.PI / 2)) + startValue; 17544 }, 17545 17546 easeInOutSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17547 return changeInValue / 2 * (1 - Math.cos(Math.PI * currentIteration / totalIterations)) + startValue; 17548 }, 17549 17550 easeInExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17551 return changeInValue * Math.pow(2, 10 * (currentIteration / totalIterations - 1)) + startValue; 17552 }, 17553 17554 easeOutExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17555 return changeInValue * (-Math.pow(2, -10 * currentIteration / totalIterations) + 1) + startValue; 17556 }, 17557 17558 easeInOutExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17559 if ((currentIteration /= totalIterations / 2) < 1) { 17560 return changeInValue / 2 * Math.pow(2, 10 * (currentIteration - 1)) + startValue; 17561 } 17562 return changeInValue / 2 * (-Math.pow(2, -10 * --currentIteration) + 2) + startValue; 17563 }, 17564 17565 easeInCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17566 return changeInValue * (1 - Math.sqrt(1 - (currentIteration /= totalIterations) * currentIteration)) + startValue; 17567 }, 17568 17569 easeOutCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17570 return changeInValue * Math.sqrt(1 - (currentIteration = currentIteration / totalIterations - 1) * currentIteration) + startValue; 17571 }, 17572 17573 easeInOutCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17574 if ((currentIteration /= totalIterations / 2) < 1) { 17575 return changeInValue / 2 * (1 - Math.sqrt(1 - currentIteration * currentIteration)) + startValue; 17576 } 17577 return changeInValue / 2 * (Math.sqrt(1 - (currentIteration -= 2) * currentIteration) + 1) + startValue; 17578 } 17579 }; 17580 17581})(jQuery); 17582(function( $ ) { 17583 'use strict'; 17584 17585 var utils = MK.utils; 17586 var val = MK.val; 17587 17588 /** 17589 * Keep track of top Level sections so we can easly skip to next one. 17590 * We must be explicit about DOM level to nested sections. 17591 * The list of sections is static. If you'd need to refreh it on ajax etc do it with pub/sub (not really needed now). 17592 * We keep track for the same sections in Footer for mutating window location with '!loading' to prevent native anchor behaviour. 17593 */ 17594 var $topLevelSections = $('#theme-page > .vc_row, #theme-page > .mk-main-wrapper-holder, #theme-page > .mk-page-section'); 17595 17596 $( document ).on( 'click', '.mk-skip-to-next', function() { 17597 var $this = $( this ), 17598 /** 17599 * Static height of button + the space to the bottom of the container. 17600 * 17601 * @TODO Possible to calculate dynamically. 17602 */ 17603 btnHeight = $this.hasClass( 'edge-skip-slider' ) ? 150 : 76, 17604 offset = $this.offset().top + btnHeight,
17605 nextOffset = utils.nextHigherVal( utils.offsets( $topLevelSections ), [offset] ); 17606 17607 utils.scrollTo( nextOffset - val.offsetHeaderHeight( nextOffset ) ); 17608 }); 17609 17610})( jQuery ); 17611 17612/* Social Share */ 17613/* -------------------------------------------------------------------- */ 17614(function($) { 17615 'use strict'; 17616 17617 MK.component.SocialShare = function( el ) { 17618 var networks = { 17619 twitter : 'http://twitter.com/intent/tweet?text={title}&url={url}', 17620 pinterest : 'http://pinterest.com/pin/create/button/?url={url}&media={image}&description={title}', 17621 facebook : 'https://www.facebook.com/sharer/sharer.php?u={url}', 17622 googleplus : 'https://plus.google.com/share?url={url}', 17623 linkedin : 'http://www.linkedin.com/shareArticle?mini=true&url={url}&title={title}&summary={desc}', 17624 digg : 'http://digg.com/submit?url={url}&title={title}', 17625 reddit : 'http://reddit.com/submit?url={url}&title={title}', 17626 }; 17627 17628 this.networks = networks; 17629 this.el = el; 17630 }; 17631 17632 17633 MK.component.SocialShare.prototype = { 17634 17635 init : function() { 17636 this.cacheElements(); 17637 this.bindEvents(); 17638 }, 17639 17640 cacheElements : function() { 17641 this.$this = $( this.el ); 17642 17643 17644 }, 17645 17646 bindEvents : function() { 17647 var thisObject = this; 17648 var tempClass = ""; 17649 $.each(this.networks, function( key, value ) { 17650 thisObject.$tempClass = $('.' + key + '-share'); 17651 thisObject.$tempClass.click(thisObject.openSharingDialog.bind(self, this, key)); 17652 17653 }); 17654 }, 17655 17656 openSharingDialog : function(url, site, args) { 17657 var urlWrapper = url; 17658 var rx = new RegExp('\{[a-z]*\}','g'), res; 17659 var match = rx.exec(url); 17660 while (match != null) { 17661 var pureAttr = match[0].replace("{", "").replace("}" , ""); 17662 var attValue = $(args.currentTarget).attr('data-' + pureAttr); 17663 if (attValue === undefined || attValue === null) { 17664 attValue = ""; 17665 } 17666 attValue = attValue.replace('#', '%23'); // fix for twitter description 17667 urlWrapper = urlWrapper.replace(match, attValue); 17668 match = rx.exec(url); 17669 } 17670 17671 window.open(urlWrapper, site + "Window", "height=320,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17672 17673 }, 17674 17675 17676 }; 17677 17678 17679 ////////////////////////////////////////// 17680 // 17681 // Apply to: 17682 // 17683 // /////////////////////////////////////// 17684 17685 var $body = $('body'); 17686 17687 if(!$body.length) return; 17688 17689 $body.each(function() { 17690 var socialShare = new MK.component.SocialShare(this); 17691 socialShare.init(); 17692 }); 17693 17694})(jQuery); 17695 17696 17697 17698//function mk_social_share() { 17699// 17700// "use strict";
17701// 17702// $('.twitter-share').on('click', function () { 17703// var $url = $(this).attr('data-url'), 17704// $title = $(this).attr('data-title'); 17705// 17706// window.open('http://twitter.com/intent/tweet?text=' + $title + ' ' + $url, "twitterWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17707// return false; 17708// }); 17709// 17710// $('.pinterest-share').on('click', function () { 17711// var $url = $(this).attr('data-url'), 17712// $title = $(this).attr('data-title'), 17713// $image = $(this).attr('data-image'); 17714// window.open('http://pinterest.com/pin/create/button/?url=' + $url + '&media=' + $image + '&description=' + $title, "twitterWindow", "height=320,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17715// return false; 17716// }); 17717// 17718// $('.facebook-share').on('click', function () { 17719// var $url = $(this).attr('data-url'); 17720// window.open('https://www.facebook.com/sharer/sharer.php?u=' + $url, "facebookWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17721// return false; 17722// }); 17723// 17724// $('.googleplus-share').on('click', function () { 17725// var $url = $(this).attr('data-url'); 17726// window.open('https://plus.google.com/share?url=' + $url, "googlePlusWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17727// return false; 17728// }); 17729// 17730// $('.linkedin-share').on('click', function () { 17731// var $url = $(this).attr('data-url'), 17732// $title = $(this).attr('data-title'), 17733// $desc = $(this).attr('data-desc'); 17734// window.open('http://www.linkedin.com/shareArticle?mini=true&url=' + $url + '&title=' + $title + '&summary=' + $desc, "linkedInWindow", "height=380,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17735// return false; 17736// }); 17737//} 17738 17739 17740 17741 17742(function($) { 17743 'use strict'; 17744 17745 MK.component.Sortable = function(el) { 17746 this.el = el; 17747 }; 17748 17749 MK.component.Sortable.prototype = { 17750 init: function init() { 17751 this.cacheElements(); 17752 this.bindEvents(); 17753 }, 17754 17755 cacheElements: function cacheElements() { 17756 this.unique = Date.now(); 17757 this.$filter = $(this.el); 17758 this.config = this.$filter.data('sortable-config'); 17759 17760 this.ajaxLoader = new MK.utils.ajaxLoader(this.config.container); 17761 this.ajaxLoader.init(); 17762 17763 this.$container = $( this.config.container ); 17764 this.$navItems = this.$filter.find('a'); 17765 this.$filterItems = this.$container.find(this.config.item); 17766 }, 17767 17768 bindEvents: function bindEvents() { 17769 this.$navItems.on('click', this.handleClick.bind(this)); 17770 MK.utils.eventManager.subscribe('ajaxLoaded', this.onLoad.bind(this)); 17771 }, 17772 17773 handleClick: function handleClick(e) { 17774 e.preventDefault(); 17775 17776 var $item = $(e.currentTarget); 17777 var term = $item.data('filter'); 17778 17779 this.$navItems.removeClass('current'); 17780 $item.addClass('current'); 17781 17782 if(this.config.mode === 'ajax') this.inDB(term, $item); 17783 else this.inPage(term); 17784 }, 17785 17786 inDB: function inDB(term, $item) { 17787 // Add load indicator only for long requests 17788 MK.ui.loader.remove(this.$filter); 17789 MK.ui.loader.add($item); 17790 17791 // If mk-ajax-loaded-posts span exists and one of the filter is clicked, 17792 // clear post ids 17793 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 17794 this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts', ''); 17795 } 17796 17797 this.ajaxLoader.setData({ 17798 paged: 1, 17799 term: term 17800 }); 17801 this.ajaxLoader.load(this.unique); 17802 }, 17803 17804 inPage: function inPage(term) { 17805 var $filterItems = this.$container.find(this.config.item); 17806 $filterItems.removeClass('is-hidden'); // show all first 17807 // Replace all ', ' with ', .'. It's used to add '.' as class selector of each category. 17808 var className = term.replace( /, /g, ", ." ); 17809 if(term !== '*') $filterItems.not( '.' + className ).addClass('is-hidden'); // hide filtered 17810 MK.utils.eventManager.publish('staticFilter'); 17811 }, 17812 17813 onLoad: function onLoad(e, response) { 17814 if(this.config.mode === 'static') { 17815 this.$navItems.removeClass('current').first().addClass('current'); 17816 } 17817 if( typeof response !== 'undefined' && response.id === this.config.container) { 17818 MK.ui.loader.remove(this.$filter); 17819 if(response.unique === this.unique) { 17820 this.$container.html(response.content); 17821 this.ajaxLoader.setData({paged: 1}); 17822 } 17823 } 17824 } 17825 }; 17826 17827})(jQuery); 17828(function($) { 17829 'use strict'; 17830 17831 MK.component.Tabs = function( el ) { 17832 var defaults = { 17833 activeClass : 'is-active' 17834 }; 17835 17836 this.config = defaults; 17837 this.el = el; 17838 }; 17839 17840 MK.component.Tabs.prototype = { 17841 17842 init : function() { 17843 this.cacheElements(); 17844 this.bindEvents(); 17845 }, 17846 17847 cacheElements : function() { 17848 this.$this = $( this.el ); 17849 this.$tabs = this.$this.find( '.mk-tabs-tab' ); 17850 this.$panes = this.$this.find( '.mk-tabs-pane' ); 17851 this.currentId = 0; 17852 }, 17853 17854 bindEvents : function() { 17855 var self = this; 17856 17857 this.$tabs.on( 'click', this.switchPane.bind( this ) ); 17858 }, 17859 17860 switchPane : function( evt ) { 17861 evt.preventDefault(); 17862 17863 var clickedId = $( evt.currentTarget ).index(); 17864 17865 this.hide( this.currentId ); 17866 this.show( clickedId ); 17867 17868 // Update current id 17869 this.currentId = clickedId; 17870 17871 // Notify rest of the app 17872 MK.utils.eventManager.publish('item-expanded'); 17873 }, 17874 17875 show : function( id ) { 17876 this.$tabs.eq( id ).addClass( this.config.activeClass ); 17877 this.$panes.eq( id ).addClass( this.config.activeClass ); 17878 }, 17879 17880 hide : function( id ) { 17881 this.$tabs.eq( id ).removeClass( this.config.activeClass ); 17882 this.$panes.eq( id ).removeClass( this.config.activeClass ); 17883 } 17884 }; 17885 17886})(jQuery); 17887 17888 17889/* Tabs */ 17890/* -------------------------------------------------------------------- */ 17891 17892function mk_tabs() { 17893 17894 // "use strict"; 17895 17896 // if ($.exists('.mk-tabs, .mk-news-tab, .mk-woo-tabs')) { 17897 // $(".mk-tabs, .mk-news-tab, .mk-woo-tabs").tabs(); 17898 17899 // $('.mk-tabs').on('click', function () { 17900 // $('.mk-theme-loop').isotope('layout'); 17901 // }); 17902 17903 // $('.mk-tabs.vertical-style').each(function () { 17904 // $(this).find('.mk-tabs-pane').css('minHeight', $(this).find('.mk-tabs-tabs').height() - 1); 17905 // }); 17906 17907 // } 17908} 17909 17910function mk_tabs_responsive(){ 17911 // $('.mk-tabs, .mk-news-tab').each(function () { 17912 // $this = $(this); 17913 // if ($this.hasClass('mobile-true')) { 17914 // if (window.matchMedia('(max-width: 767px)').matches) 17915 // { 17916 // $this.tabs("destroy"); 17917 // } else { 17918 // $this.tabs(); 17919 // } 17920 // } 17921 // }); 17922 17923} 17924 17925 17926(function($) { 17927 'use strict'; 17928 17929 $(document).on('click', function(e) { 17930 $('.mk-toggle-trigger').removeClass('mk-toggle-active'); 17931 }); 17932 17933 function toggle(e) { 17934 e.preventDefault(); 17935 e.stopPropagation(); 17936 var $this = $(e.currentTarget); 17937 17938 if (!$this.hasClass('mk-toggle-active')) { 17939 17940 $('.mk-box-to-trigger').fadeOut(200); 17941 $this.parent().find('.mk-box-to-trigger').fadeIn(250); 17942 $('.mk-toggle-trigger').removeClass('mk-toggle-active'); 17943 $this.addClass('mk-toggle-active'); 17944 17945 } else { 17946 17947 $('.mk-box-to-trigger').fadeOut(200); 17948 $this.removeClass('mk-toggle-active'); 17949 17950 } 17951 } 17952 17953 function assignToggle() { 17954 // wait for ajax response propagation and insertion 17955 setTimeout(function() { 17956 $('.mk-toggle-trigger').off('click', toggle); 17957 $('.mk-toggle-trigger').on('click', toggle); 17958 }, 100); 17959 } 17960 17961 assignToggle(); 17962 MK.utils.eventManager.subscribe('ajaxLoaded', assignToggle); 17963 MK.utils.eventManager.subscribe('ajax-preview', assignToggle); 17964 17965 $(window).on('vc_reload', function(){ 17966 assignToggle(); 17967 MK.utils.eventManager.subscribe('ajaxLoaded', assignToggle); 17968 MK.utils.eventManager.subscribe('ajax-preview', assignToggle); 17969 }); 17970 17971}(jQuery)); 17972(function($) { 17973 'use strict'; 17974 17975 var $iframes = $('iframe'); 17976 17977 $iframes.each(function() { 17978 var $iframe = $(this); 17979 var parent = $iframe.parent().get(0); 17980 var tagName = parent.tagName; 17981 17982 if(tagName === 'P') $iframe.wrap('<div class="mk-video-container"></div>'); 17983 }); 17984 17985}(jQuery)); 17986(function( $ ) { 17987 'use strict'; 17988 if( MK.utils.isMobile() ) { 17989 $('.mk-animate-element').removeClass('mk-animate-element'); 17990 return; 17991 }
17992 var init = function init() { 17993 var $rootLevelEls = $('.js-master-row, .widget'); 17994 $rootLevelEls.each( spyViewport ); 17995 $rootLevelEls.each( function rootLevelEl() { 17996 var $animateEl = $(this).find( '.mk-animate-element' ); 17997 $animateEl.each( spyViewport ); 17998 /** 17999 * Firefox has known issue where horizontal scrollbar will appear if an 18000 * element uses animation CSS. The solution should be set the element 18001 * position as fixed or overflow-x as hidden. Position fixed is not possible 18002 * to use because it's only cause other big problems. The best way is 18003 * set overflow-x as hidden in the page content container #theme-page. 18004 * 18005 * NOTE: The problem is spotted on Right To Left viewport only. So, it's 18006 * limited to '.right-to-left' selector only for now to avoid other 18007 * problems. Please extend the functionallity if it's happen in 18008 * other viewport animation effect. 18009 */ 18010 var browserName = MK.utils.browser.name; 18011 if ( browserName === 'Firefox' ) { 18012 var $rightToLeft = $( this ).find( '.right-to-left' ); 18013 if ( $rightToLeft.length > 0 ) { 18014 $( '#theme-page' ).css( 'overflow-x', 'hidden' ); 18015 } 18016 } 18017 }); 18018 }; 18019 var spyViewport = function spyViewport(i) { 18020 var self = this; 18021 MK.utils.scrollSpy( this, { 18022 position : 'bottom', 18023 threshold : 200, 18024 after : function() { 18025 animate.call(self, i); 18026 } 18027 }); 18028 }; 18029 var animate = function animate(i) { 18030 var $this = $(this); 18031 setTimeout(function() { 18032 $this.addClass( 'mk-in-viewport' ); 18033 }, 100 * i); 18034 }; 18035 $(window).on('load vc_reload', init); 18036}(jQuery)); 18037 18038function product_loop_add_cart() { 18039 var $body = $('body'); 18040 $body.on('click', '.add_to_cart_button', function() { 18041 var icon = '<svg class="mk-svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M437.011 74.99c-46.326-46.328-110.318-74.99-181.011-74.99-109.744 0-203.345 69.064-239.749 166.094l59.938 22.477c27.302-72.773 97.503-124.571 179.811-124.571 53.02 0 101.01 21.5 135.753 56.247l-71.753 71.753h192v-192l-74.989 74.99zm-181.011 373.01c-53.02 0-101.013-21.496-135.756-56.244l71.756-71.756h-192v192l74.997-74.997c46.323 46.331 110.309 74.997 181.003 74.997 109.745 0 203.346-69.064 239.75-166.094l-59.938-22.477c-27.302 72.773-97.503 124.571-179.812 124.571z"/></svg>'; 18042 var $holder = $(this).parents('.product:eq(0)'); 18043 var $i = $holder.find('.product-loading-icon'); 18044 $holder.addClass('adding-to-cart').removeClass('added-to-cart'); 18045 $i.html(icon); 18046 }); 18047 $body.bind('added_to_cart', function() { 18048 var icon = '<svg class="mk-svg-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M432 64l-240 240-112-112-80 80 192 192 320-320z"/></svg>'; 18049 var $holder = $('.adding-to-cart'); 18050 var $i = $holder.find('.product-loading-icon'); 18051 $holder.removeClass('adding-to-cart').addClass('added-to-cart'); 18052 $i.html(icon); 18053 }); 18054} 18055(function($) { 18056 'use strict'; 18057 18058 /** 18059 * Entry point of application. Runs all components 18060 */ 18061 $( window ).on( 'load', function() { 18062 MK.core.initAll( document ); 18063 MK.utils.scrollToURLHash(); 18064 // TODO move preloader to components and manage it state from within 18065 setTimeout( function() { 18066 MK.ui.preloader.hide(); // site wide 18067 $('.mk-preloader').hide(); // components 18068 $('body').removeClass('loading'); 18069 }, 150 ); 18070 }); 18071 18072 /** 18073 * VC frontend editor. Init all components. 18074 */ 18075 $( window ).on( 'vc_reload', function() { 18076 setTimeout(function(){ 18077 MK.core.initAll( document ); 18078 }, 100); 18079 }); 18080 18081 /** 18082 * Assign global click handlers 18083 */ 18084 $( document ).on( 'click', '.js-smooth-scroll, .js-main-nav a', smoothScrollToAnchor); 18085 $( '.side_dashboard_menu a' ).on( 'click', smoothScrollToAnchor); 18086 18087 function smoothScrollToAnchor( evt ) { 18088 var anchor = MK.utils.detectAnchor( this ); 18089 var $this = $(evt.currentTarget); 18090 var loc = window.location; 18091 var currentPage = loc.origin + loc.pathname; 18092 var href = $this.attr( 'href' ); 18093 var linkSplit = (href) ? href.split( '#' ) : ''; 18094 var hrefPage = linkSplit[0] ? linkSplit[0] : ''; 18095 var hrefHash = linkSplit[1] ? linkSplit[1] : ''; 18096 18097 if( anchor.length ) { 18098 if(hrefPage === currentPage || hrefPage === '') evt.preventDefault(); 18099 MK.utils.scrollToAnchor( anchor ); 18100 18101 } else if( $this.attr( 'href' ) === '#' ) { 18102 evt.preventDefault(); 18103 } 18104 } 18105 18106}(jQuery));})(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.