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 MK.utils.showBackgroundVideo = (function() { 2307 var isUserChoice = (mk_show_background_video === 'true'); 2308 2309 return isUserChoice; 2310 }()); 2311 2312}(jQuery)); 2313 2314(function($) { 2315 'use strict'; 2316 2317 var MK = window.MK || {}; 2318 MK.utils = window.MK.utils || {}; 2319 2320 /** 2321 * Basic implementation of pub / sub pattern to avoid tight coupling with direct module communication 2322 * @type {Object} 2323 */ 2324 MK.utils.eventManager = {}; 2325 2326 /** 2327 * Subscribe to custom event and run callbacks 2328 * @param {String} 2329 * @param {Function} 2330 * 2331 * @usage MK.utils.eventManager.subscribe('event', function(e, params) {} ) 2332 */ 2333 MK.utils.eventManager.subscribe = function(evt, func) { 2334 $(this).on(evt, func); 2335 }; 2336 2337 /** 2338 * Unsubscribe from custom event 2339 * @param {String} 2340 * @param {Function} 2341 */ 2342 MK.utils.eventManager.unsubscribe = function(evt, func) { 2343 $(this).off(evt, func); 2344 }; 2345 2346 /** 2347 * Publish custom event to notify appliaction about state change 2348 * @param {String} 2349 * 2350 * @usage MK.utils.eventManager.publish('event', { 2351 * param: val 2352 * }) 2353 */ 2354 MK.utils.eventManager.publish = function(evt, params) { 2355 $(this).trigger(evt, [params]); 2356 }; 2357 2358}(jQuery)); 2359(function($) { 2360 'use strict'; 2361 2362 var MK = window.MK || {}; 2363 MK.utils = window.MK.utils || {}; 2364 2365 /** 2366 * Control browser fullscreen mode 2367 * @type {Object} 2368 */ 2369 MK.utils.fullscreen = {}; 2370 2371 // TODO: move to namespace 2372 MK.utils.launchIntoFullscreen = function ( element ) { 2373 if(element.requestFullscreen) { 2374 element.requestFullscreen(); 2375 } else if(element.mozRequestFullScreen) { 2376 element.mozRequestFullScreen(); 2377 } else if(element.webkitRequestFullscreen) { 2378 element.webkitRequestFullscreen(); 2379 } else if(element.msRequestFullscreen) { 2380 element.msRequestFullscreen(); 2381 } 2382 }; 2383 2384 MK.utils.exitFullscreen = function () { 2385 if(document.exitFullscreen) { 2386 document.exitFullscreen(); 2387 } else if(document.mozCancelFullScreen) { 2388 document.mozCancelFullScreen(); 2389 } else if(document.webkitExitFullscreen) { 2390 document.webkitExitFullscreen(); 2391 } 2392 }; 2393 2394}(jQuery)); 2395(function($) { 2396 'use strict'; 2397 2398 var MK = window.MK || {}; 2399 MK.utils = window.MK.utils || {}; 2400 2401 MK.utils.misc = {}; 2402 // TODO: move to namespace 2403 2404 /** 2405 * Get all top offsets from jQuery collection 2406 * 2407 * @param {$Objects} 2408 * @return {Aray} 2409 */ 2410 MK.utils.offsets = function( $els ) { 2411 return $.map( $els, function( el ) { 2412 return $( el ).offset().top; 2413 }); 2414 }; 2415 2416 /** 2417 * Retrive from array of numbers first number that is higher than given parameter 2418 * 2419 * @param {Number} 2420 * @param {Array} 2421 * @return {Number} 2422 */ 2423 MK.utils.nextHigherVal = function( val, arr ) { 2424 var i = 0, 2425 higher = null; 2426 2427 var check = function() { 2428 if( val > arr[ i ]) { 2429 i += 1; 2430 check(); 2431 } else { 2432 higher = arr[ i ]; 2433 } 2434 }; 2435 check(); 2436 2437 return higher; 2438 }; 2439 2440 2441 MK.utils.throttle = function( delay, fn ) { 2442 var last; 2443 var deferTimer; 2444 2445 return function() { 2446 var context = this; 2447 var args = arguments; 2448 var now = +new Date; 2449 if( last && now < last + delay ) { 2450 clearTimeout( deferTimer ); 2451 deferTimer = setTimeout( function() { 2452 last = now; fn.apply( context, args ); 2453 }, delay ); 2454 } else { 2455 last = now; 2456 fn.apply( context, args ); 2457 } 2458 }; 2459 }; 2460 2461 MK.utils.isElementInViewport = function( el ) { 2462 var elemTop = el.getBoundingClientRect().top; 2463 var isVisible = (elemTop < window.innerHeight); 2464 return isVisible; 2465 }; 2466 2467})(jQuery); 2468(function($) { 2469 'use strict'; 2470 2471 var MK = window.MK || {}; 2472 MK.utils = window.MK.utils || {}; 2473 2474 /** 2475 * Scrolls page to static pixel offset 2476 * @param {Number} 2477 */ 2478 MK.utils.scrollTo = function( offset ) { 2479 $('html, body').stop().animate({ 2480 scrollTop: offset 2481 }, { 2482 duration: 1200, 2483 easing: "easeInOutExpo" 2484 }); 2485 }; 2486 2487 /** 2488 * Scrolls to element passed in as object or DOM reference 2489 * @param {String|Object} 2490 */ 2491 MK.utils.scrollToAnchor = function( hash ) { 2492 // Escape meta-chars from hash name only. 2493 hash = hash.substring(1).replace(/[!"#$%&'()*+,./:;<=>?@[\]^`{|}~]/g, "\\$&"); 2494 hash = "#" + hash; 2495 var $target = $( hash ); 2496 // console.log( hash ); 2497 2498 if( ! $target.length ) return; 2499 2500 var offset = $target.offset().top; 2501 offset = offset - MK.val.offsetHeaderHeight( offset ); 2502 2503 if( hash === '#top-of-page' ) window.history.replaceState( undefined, undefined, ' ' ); 2504 else window.history.replaceState( undefined, undefined, hash ); 2505 2506 MK.utils.scrollTo( offset ); 2507 }; 2508 2509 /** 2510 * Controls native scroll behaviour 2511 * @return {Object} => {disable, enable} 2512 */ 2513 MK.utils.scroll = (function() { 2514 // 37 - left arror, 38 - up arrow, 39 right arrow, 40 down arrow 2515 var keys = [38, 40]; 2516 2517 function preventDefault(e) { 2518 e = e || window.event; 2519 e.preventDefault(); 2520 e.returnValue = false; 2521 } 2522 2523 function wheel(e) { 2524 preventDefault(e); 2525 } 2526 2527 function keydown(e) { 2528 for (var i = keys.length; i--;) { 2529 if (e.keyCode === keys[i]) { 2530 preventDefault(e); 2531 return; 2532 } 2533 } 2534 } 2535 2536 function disableScroll() { 2537 if (window.addEventListener) { 2538 window.addEventListener('DOMMouseScroll', wheel, false); 2539 } 2540 window.onmousewheel = document.onmousewheel = wheel; 2541 document.onkeydown = keydown; 2542 } 2543 2544 function enableScroll() { 2545 if (window.removeEventListener) { 2546 window.removeEventListener('DOMMouseScroll', wheel, false); 2547 } 2548 window.onmousewheel = document.onmousewheel = document.onkeydown = null; 2549 } 2550 2551 return { 2552 disable : disableScroll, 2553 enable : enableScroll 2554 }; 2555 2556 })(); 2557 2558 /** 2559 * Checks if passed link element has anchor inside current page. Returns string like '#anchor' if so or false 2560 * @param {String|Object} 2561 * @return {String|Boolean} 2562 */ 2563 MK.utils.detectAnchor = function( el ) { 2564 var $this = $( el ), 2565 loc = window.location, 2566 currentPage = loc.origin + loc.pathname, 2567 href = $this.attr( 'href' ), 2568 linkSplit = (href) ? href.split( '#' ) : '', 2569 hrefPage = linkSplit[0] ? linkSplit[0] : '', 2570 hrefHash = linkSplit[1] ? linkSplit[1] : ''; 2571 2572 if( typeof hrefHash !== 'undefined' && hrefHash !== '' ) { 2573 return '#' + hrefHash; 2574 } else { 2575 return false;
2576 } 2577 }; 2578 2579 /** 2580 * This should be invoked only on page load. 2581 * Scrolls to anchor from address bar 2582 */ 2583 MK.utils.scrollToURLHash = function() { 2584 var loc = window.location, 2585 hash = loc.hash; 2586 2587 if ( hash.length && hash.substring(1).length ) { 2588 // !loading is added early after DOM is ready to prevent native jump to anchor 2589 hash = hash.replace( '!loading', '' ); 2590 2591 // Wait for one second before animating 2592 // Most of UI animations should be done by then and async operations complited 2593 setTimeout( function() { 2594 MK.utils.scrollToAnchor( hash ); 2595 }, 1000 ); 2596 2597 // Right after reset back address bar 2598 setTimeout( function() { 2599 window.history.replaceState(undefined, undefined, hash); 2600 }, 1001); 2601 } 2602 }; 2603 2604 /** 2605 * Scroll Spy implementation. Spy dynamic offsets of elements or static pixel offset 2606 * @param {Number|Element} 2607 * @param {Object} => callback object {before, active, after} 2608 */ 2609 MK.utils.scrollSpy = function( toSpy, config ) { 2610 var $window = $( window ), 2611 container = document.getElementById( 'mk-theme-container' ), 2612 isObj = ( typeof toSpy === 'object' ), 2613 offset = (isObj) ? MK.val.dynamicOffset( toSpy, config.position, config.threshold ) : function() { return toSpy; }, 2614 height = (isObj) ? MK.val.dynamicHeight( toSpy ) : function() { return 0; }, 2615 cacheVals = {}, 2616 _p = 'before'; // current position 2617 2618 var checkPosition = function() { 2619 var s = MK.val.scroll(), 2620 o = offset(), 2621 h = height(); 2622 2623 if( s < o && _p !== 'before' ) { 2624 // console.log( toSpy, 'before' ); 2625 if( config.before ) config.before(); 2626 _p = 'before'; 2627 } 2628 else if( s >= o && s <= o + h && _p !== 'active' ) { 2629 // console.log( toSpy, 'active' ); 2630 if( config.active ) config.active( o ); 2631 _p = 'active'; 2632 } 2633 else if( s > o + h && _p !== 'after' ) { 2634 // console.log( toSpy, 'after' ); 2635 if( config.after) config.after( o + h ); 2636 _p = 'after'; 2637 } 2638 }; 2639 2640 var rAF = function() { 2641 window.requestAnimationFrame( checkPosition ); 2642 }; 2643 2644 var exportVals = function() { 2645 return cacheVals; 2646 }; 2647 2648 var updateCache = function() { 2649 var o = offset(), 2650 h = height(); 2651 2652 cacheVals = { 2653 before : o - $window.height(), 2654 active : o, 2655 after : o + h 2656 }; 2657 }; 2658 2659 if( config.cache ) { 2660 config.cache( exportVals ); 2661 } 2662 2663 checkPosition(); 2664 $window.on( 'load', checkPosition ); 2665 $window.on( 'resize', checkPosition ); 2666 $window.on( 'mouseup', checkPosition ); 2667 window.addResizeListener( container, checkPosition ); 2668 2669 $window.on( 'scroll', rAF ); 2670 2671 updateCache(); 2672 $window.on( 'load', updateCache ); 2673 $window.on( 'resize', updateCache ); 2674 window.addResizeListener( container, updateCache ); 2675 }; 2676 2677}(jQuery)); 2678(function($) { 2679 'use strict'; 2680 2681 // Create delagation event handler to behave as "live" listener. We may provide new elements with ajax etc later 2682 // Just add js-taphover class whatever element you'd like to immidietely bring hover on touch devices 2683 $("body").on("click touchend", '.js-taphover', function (e) { 2684 var $link = $(e.currentTarget); // grab target 2685 var $target = $(e.target); 2686 2687 // Rather than ":hover" state we operate on ".hover" class which gives us more control and chance to emulate it on click 2688 // yet it is easy to reason about in our CSS 2689 if ($link.hasClass('hover')) { 2690 return true; 2691 } else if ( MK.utils.isMobile() ) { 2692 if ( ($target.hasClass('hover-icon') || $target.closest('.hover-icon').length) && !$target.closest('.js-taphover').hasClass('hover') ) { 2693 e.preventDefault(); 2694 } 2695 $link.addClass('hover'); 2696 $('.js-taphover').not(e.currentTarget).removeClass('hover'); // remove it from previous element 2697 e.stopPropagation(); // do not leak to document root if expected element was touched 2698 } 2699 }); 2700 2701 // Whenever click leaks to the root romve all hover classes 2702 $(document).on("click", function(e) { 2703 $('.js-taphover').removeClass('hover'); 2704 }); 2705 2706}(jQuery)); 2707// (function() { 2708// 'use strict'; 2709 2710// // Make sure the video behaves like background-size: cover 2711// window.videoCover = function( holderSelector, videoSelector ) { 2712// var videos = document.querySelectorAll( videoSelector ), 2713// holder = document.querySelectorAll( holderSelector )[0]; 2714 2715// [].forEach.call(videos, function(video) { 2716 2717// var videoAspectRatio; 2718
2719// resizeBackground(); 2720 2721// video.onloadedmetadata = function() { 2722// // get images aspect ratio 2723// videoAspectRatio = this.height / this.width; 2724// // attach resize event and fire it once 2725// window.onresize = resizeBackground; 2726// resizeBackground(); 2727// }; 2728 2729// function resizeBackground() { 2730// // get window size and aspect ratio 2731// var holderWidth = holder.innerWidth, 2732// holderHeight = holder.innerHeight, 2733// holderAspectRatio = holderHeight / holderWidth; 2734 2735// //compare holder ratio to image ratio so you know which way the image should fill 2736// if ( holderAspectRatio < videoAspectRatio ) { 2737// // we are fill width 2738// video.style.width = holderWidth + "px"; 2739// // and applying the correct aspect to the height now 2740// video.style.height = (holderWidth * videoAspectRatio) + "px"; // this can be margin if your element is not positioned relatively, absolutely or fixed 2741// // make sure image is always centered 2742// video.style.left = "0px";
2743// video.style.top = (holderHeight - (holderWidth * videoAspectRatio)) / 2 + "px"; 2744// } else { // same thing as above but filling height instead 2745// video.style.height = holderHeight + "px"; 2746// video.style.width = (holderHeight / videoAspectRatio) + "px"; 2747// video.style.left = (holderWidth - (holderHeight / videoAspectRatio)) / 2 + "px"; 2748// video.style.top = "0px"; 2749// } 2750// } 2751 2752// }); 2753// }; 2754 2755// }()); 2756// 2757// 2758// 2759// TODO it is temp only. make it as a plugin 2760 2761(function($) { 2762 'use strict'; 2763 2764 var $videoHolder = $('.mk-center-video'), 2765 $wrapper = $videoHolder.parent(), 2766 baseAspectRatio = 56.25; 2767 2768 var wrapperHeight, 2769 wrapperWidth, 2770 wrapperAspectRatio; 2771 2772 function calc() { 2773 wrapperHeight = $wrapper.height(); 2774 wrapperWidth = $wrapper.width(); 2775 wrapperAspectRatio = (wrapperHeight / wrapperWidth) * 100; 2776 } 2777 2778 function apply() { 2779 var width = (wrapperAspectRatio / baseAspectRatio) * 100, 2780 widthOverflow = (width - 100); 2781 2782 $videoHolder.css({ 2783 'padding-top': wrapperAspectRatio + '%', 2784 'width': width + '%', 2785 'left': -(widthOverflow / 2) + '%' 2786 }); 2787 } 2788 2789 function reset() { 2790 $videoHolder.css({ 2791 'padding-top': baseAspectRatio + '%', 2792 'width': 100 + '%', 2793 'left': 0 2794 }); 2795 } 2796 2797 function setCover() { 2798 reset(); 2799 calc(); 2800 if(wrapperAspectRatio > baseAspectRatio) apply(); 2801 } 2802 2803 $(window).on('load', setCover); 2804 $(window).on('resize', setCover); 2805 2806 2807}(jQuery)); 2808(function($) { 2809 'use strict'; 2810 2811 var MK = window.MK || {}; 2812 2813 /** 2814 * MK.val is collection of Lambdas responsible for returning up to date values of method type like scrollY or el offset. 2815 * The Lambda is responsible for keeping track of value of a particular property, usually takes as argument an object 2816 * (or DOM reference) and internally creates and updates data that is returned as primitive value - through variable reference. 2817 * 2818 * Benefits of this approach: 2819 * - reduced DOM reads 2820 * - auto-updating values without need for additional logic where methods are called 2821 * - updating values when needed to be updated not read 2822 * 2823 * Downsides: 2824 * - Memory overhead with closures and keeping state in memory ( still beter than read state from DOM, but use wisely - 2825 * do not use it when you really need static value on runtime ) 2826 */ 2827 MK.val = {}; 2828 2829 /** 2830 * Current window offsetY position 2831 * 2832 * @uses MK.val.scroll() 2833 * @return {number} 2834 */ 2835 MK.val.scroll = (function() { 2836 var offset = 0, 2837 $window = $(window), 2838 hasPageYOffset = (window.pageYOffset !== undefined), 2839 body = (document.documentElement || document.body.parentNode || document.body); // cross browser handling 2840 2841 var update = function() { 2842 offset = hasPageYOffset ? window.pageYOffset : body.scrollTop; 2843 }; 2844 2845 var rAF = function() { 2846 window.requestAnimationFrame(update); 2847 }; 2848 2849 update(); 2850 $window.on('load', update); 2851 $window.on('resize', update); 2852 $window.on('scroll', rAF); 2853 2854 return function() { 2855 return offset; 2856 }; 2857 })(); 2858 2859 2860 /** 2861 * Changes number of percent to pixels based on viewport height 2862 * 2863 * @uses MK.val.viewportPercentHeight({percent val}) 2864 * @param {number} 2865 * @return {number} 2866 */ 2867 MK.val.viewportPercentHeight = function(percent) { 2868 return $(window).height() * (percent / 100); 2869 }; 2870 2871 2872 /** 2873 * Wordpress adminbar height based on wp media queries 2874 * @return {Number} 2875 */ 2876 MK.val.adminbarHeight = function() { 2877 if (php.hasAdminbar) { 2878 // apply WP native media-query and sizes 2879 return (window.matchMedia('( max-width: 782px )').matches) ? 46 : 32; 2880 } else { 2881 return 0; 2882 } 2883 }; 2884 2885 2886 /** 2887 * Offset when header becomes sticky. Evaluates viewport % and header height to pixels for according options 2888 * @return {Number} 2889 */ 2890 MK.val.stickyOffset = (function() { 2891 var $header = $('.mk-header').not('.js-header-shortcode').first(); 2892 2893 // We need to have returning function even when header is disabled 2894 if (!$header.length) { 2895 return function() { 2896 return 0; 2897 }; 2898 } 2899 2900 2901 2902 var $toolbar = $header.find('.mk-header-toolbar'), 2903 config = $header.data(), 2904 hasToolbar = $toolbar.length, 2905 toolbarHeight = (hasToolbar) ? $toolbar.height() : 0, 2906 isVertical = (config.headerStyle === 4), 2907 headerHeight = (isVertical) ? 0 : config.height; 2908 2909 var type = ((typeof config.stickyOffset === 'number') ? 'number' : false) || 2910 ((config.stickyOffset === 'header') ? 'header' : false) || 2911 'percent'; 2912 2913 var stickyOffset = 0; 2914 var setOffset = function() { 2915 2916 //we calculate toolbar height for When the device is changed Size 2917 //Toolbar height in responsive state is 0 2918 toolbarHeight = (hasToolbar) ? $toolbar.height() : 0; 2919 2920 if (MK.utils.isResponsiveMenuState()) { 2921 headerHeight = config.responsiveHeight; 2922 2923 if (hasToolbar) { 2924 if ($toolbar.is(':hidden')) { 2925 toolbarHeight = 0; 2926 } 2927 } 2928 } 2929 2930 if (type === 'number') { 2931 stickyOffset = config.stickyOffset; 2932 } else if (type === 'header') { 2933 2934 stickyOffset = headerHeight + toolbarHeight + MK.val.adminbarHeight(); // add all header components here, make them 0 if needed 2935 2936 } else if (type === 'percent') { 2937 stickyOffset = MK.val.viewportPercentHeight(parseInt(config.stickyOffset)); 2938 } 2939 }; 2940 2941 setOffset(); 2942 $(window).on('resize', setOffset); 2943 2944 return function() { 2945 return stickyOffset; 2946 }; 2947 }()); 2948 2949 2950 2951 /** 2952 * Gets header height on particular offsetY position. Use to determine logic for fullHeight, smooth scroll etc. 2953 * Takes one parameter which is offset position we're interested in. 2954 * 2955 * @uses MK.val.offsetHeaderHeight({offset val}) 2956 * @param {number} 2957 * @return {number} 2958 */ 2959 MK.val.offsetHeaderHeight = (function() { // Closure avoids multiple DOM reads. We need to fetch header config only once. 2960 var $header = $('.mk-header').not('.js-header-shortcode').first(); 2961 2962 // We need to have returning function even when header is disabled 2963 if (!$header.length) { 2964 return function() { 2965 return 0; 2966 }; 2967 } 2968 2969 var $toolbar = $header.find('.mk-header-toolbar'), 2970 config = $header.data(), 2971 stickyHeight = config.stickyHeight, 2972 desktopHeight = config.height, 2973 mobileHeight = config.responsiveHeight, 2974 isTransparent = $header.hasClass('transparent-header'), 2975 isSticky = config.stickyStyle.length, 2976 isStickyLazy = config.stickyStyle === 'lazy', 2977 isVertical = config.headerStyle === 4, 2978 hasToolbar = $toolbar.length, 2979 toolbarHeight = hasToolbar ? $toolbar.height() : 0, 2980 bufor = 5; 2981 2982 /**
2983 * The sticky section of header style 2 has fixed height. 2984 * The stickey height option does not affect this header style. 2985 */ 2986 if ( config.headerStyle === 2 ) { 2987 stickyHeight = $header.find( '.mk-header-nav-container' ).outerHeight(); 2988 } 2989 2990 // if header has border bottom we can calculate that (for responsive state) 2991 var $innerHeader = $header.find('.mk-header-inner'); 2992 var hasInnerHeader = $innerHeader.length; 2993 2994 var headerHeight = function(offset) { 2995 2996 toolbarHeight = hasToolbar ? $toolbar.height() : 0 2997 var stickyOffset = MK.val.stickyOffset(); 2998 2999 3000 if (MK.utils.isResponsiveMenuState()) { // Header avaible only on top for mobile 3001 3002 if (hasToolbar && $toolbar.is(':hidden')) { 3003 toolbarHeight = 0; 3004 } 3005 3006 //in responsive state , .mk-header-holder position's changed to "relative" 3007 //and header's border affected to offset,so borders must be calculated 3008 var headerBorder = 0; 3009 headerBorder = parseInt($innerHeader.css('border-bottom-width')); 3010 3011 var totalHeight = mobileHeight + MK.val.adminbarHeight() + toolbarHeight + headerBorder; 3012 3013 if (offset <= totalHeight) return totalHeight; 3014 else return MK.val.adminbarHeight(); 3015 } else { 3016 if (offset <= stickyOffset) { 3017 if (isVertical) { 3018 if (hasToolbar) { 3019 return toolbarHeight + MK.val.adminbarHeight(); 3020 } else { 3021 return MK.val.adminbarHeight(); 3022 } 3023 } else if (isTransparent) { 3024 return MK.val.adminbarHeight(); 3025 } else { 3026 return desktopHeight + toolbarHeight + MK.val.adminbarHeight(); 3027 } // For any other return regular desktop height 3028 } else if (offset > stickyOffset) { 3029 if (isVertical) { 3030 return MK.val.adminbarHeight(); 3031 } else if (!isSticky) { 3032 return MK.val.adminbarHeight(); 3033 } else if (isStickyLazy) { 3034 return MK.val.adminbarHeight(); 3035 } else if (isSticky) { 3036 return stickyHeight + MK.val.adminbarHeight(); 3037 } 3038 } 3039 } 3040 // default to 0 to prevent errors ( need to return number ) 3041 // Anyway make sure all scenarios are covered in IFs 3042 return 0; 3043 }; 3044 3045 return function(offset) { 3046 return headerHeight(offset - MK.val.adminbarHeight()); 3047 }; 3048 })(); 3049 3050 3051 /** 3052 * Gets current offset of given element (passed as object or DOM reference) from top or bottom (default to top) 3053 * of screen with possible threshold (default to 0) 3054 * 3055 * @uses MK.val.dynamicOffset({obj reference}, {'top'|'bottom'}, {threshold val}) 3056 * @param {string|object} 3057 * @param {string} 3058 * @param {number} 3059 * @return {number} 3060 */ 3061 MK.val.dynamicOffset = function(el, position, threshold) { 3062 var $window = $(window), 3063 $el = $(el), 3064 pos = position || 'top', 3065 thr = threshold || 0, 3066 container = document.getElementById('mk-theme-container'), 3067 currentPos = 0; 3068 3069 var offset = 0, 3070 winH = 0, 3071 rect = 0, 3072 x = 0; 3073 3074 var update = function() { 3075 winH = $window.height(); 3076 rect = $el[0].getBoundingClientRect(); 3077 offset = (rect.top + MK.val.scroll()); 3078 x = (pos === 'top') ? MK.val.offsetHeaderHeight(offset) : winH + (rect.height - thr); 3079 currentPos = offset - x - 1; 3080 }; 3081 3082 update(); 3083 $window.on('load', update); 3084 $window.on('resize', update); 3085 window.addResizeListener(container, update); 3086 3087 return function() { 3088 return currentPos; 3089 }; 3090 }; 3091 3092 /** 3093 * Gets current height of given element (passed as object or DOM reference) 3094 * 3095 * @uses MK.val.dynamicHeight({obj reference}) 3096 * @param {string|object} 3097 * @return {number} 3098 */ 3099 MK.val.dynamicHeight = function(el) { 3100 var $window = $(window), 3101 $el = $(el), 3102 container = document.getElementById('mk-theme-container'), 3103 currentHeight = 0; 3104 3105 var update = function() { 3106 currentHeight = $el.outerHeight(); 3107 }; 3108 3109 update(); 3110 $window.on('load', update); 3111 $window.on('resize', update); 3112 window.addResizeListener(container, update); 3113 3114 return function() { 3115 return currentHeight; 3116 }; 3117 }; 3118 3119})(jQuery); 3120 3121/* 3122 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3123 * 3124 */ 3125jQuery.easing["jswing"] = jQuery.easing["swing"]; 3126jQuery.extend(jQuery.easing, { 3127 def: "easeOutQuad", 3128 swing: function (a, b, c, d, e) { 3129 return jQuery.easing[jQuery.easing.def](a, b, c, d, e) 3130 }, 3131 easeInQuad: function (a, b, c, d, e) { 3132 return d * (b /= e) * b + c 3133 }, 3134 easeOutQuad: function (a, b, c, d, e) { 3135 return -d * (b /= e) * (b - 2) + c 3136 }, 3137 easeInOutQuad: function (a, b, c, d, e) { 3138 if ((b /= e / 2) < 1) return d / 2 * b * b + c; 3139 return -d / 2 * (--b * (b - 2) - 1) + c 3140 }, 3141 easeInCubic: function (a, b, c, d, e) { 3142 return d * (b /= e) * b * b + c 3143 }, 3144 easeOutCubic: function (a, b, c, d, e) { 3145 return d * ((b = b / e - 1) * b * b + 1) + c 3146 }, 3147 easeInOutCubic: function (a, b, c, d, e) { 3148 if ((b /= e / 2) < 1) return d / 2 * b * b * b + c; 3149 return d / 2 * ((b -= 2) * b * b + 2) + c 3150 }, 3151 easeInQuart: function (a, b, c, d, e) { 3152 return d * (b /= e) * b * b * b + c 3153 }, 3154 easeOutQuart: function (a, b, c, d, e) { 3155 return -d * ((b = b / e - 1) * b * b * b - 1) + c 3156 }, 3157 easeInOutQuart: function (a, b, c, d, e) { 3158 if ((b /= e / 2) < 1) return d / 2 * b * b * b * b + c; 3159 return -d / 2 * ((b -= 2) * b * b * b - 2) + c 3160 }, 3161 easeInQuint: function (a, b, c, d, e) { 3162 return d * (b /= e) * b * b * b * b + c 3163 }, 3164 easeOutQuint: function (a, b, c, d, e) { 3165 return d * ((b = b / e - 1) * b * b * b * b + 1) + c 3166 }, 3167 easeInOutQuint: function (a, b, c, d, e) { 3168 if ((b /= e / 2) < 1) return d / 2 * b * b * b * b * b + c; 3169 return d / 2 * ((b -= 2) * b * b * b * b + 2) + c 3170 }, 3171 easeInSine: function (a, b, c, d, e) {
vendor: 5,544 bytes, lines 3172-3311
3172 return -d * Math.cos(b / e * (Math.PI / 2)) + d + c 3173 }, 3174 easeOutSine: function (a, b, c, d, e) { 3175 return d * Math.sin(b / e * (Math.PI / 2)) + c 3176 }, 3177 easeInOutSine: function (a, b, c, d, e) { 3178 return -d / 2 * (Math.cos(Math.PI * b / e) - 1) + c 3179 }, 3180 easeInExpo: function (a, b, c, d, e) { 3181 return b == 0 ? c : d * Math.pow(2, 10 * (b / e - 1)) + c 3182 }, 3183 easeOutExpo: function (a, b, c, d, e) { 3184 return b == e ? c + d : d * (-Math.pow(2, -10 * b / e) + 1) + c 3185 }, 3186 easeInOutExpo: function (a, b, c, d, e) { 3187 if (b == 0) return c; 3188 if (b == e) return c + d; 3189 if ((b /= e / 2) < 1) return d / 2 * Math.pow(2, 10 * (b - 1)) + c; 3190 return d / 2 * (-Math.pow(2, -10 * --b) + 2) + c 3191 }, 3192 easeInCirc: function (a, b, c, d, e) { 3193 return -d * (Math.sqrt(1 - (b /= e) * b) - 1) + c 3194 }, 3195 easeOutCirc: function (a, b, c, d, e) { 3196 return d * Math.sqrt(1 - (b = b / e - 1) * b) + c 3197 }, 3198 easeInOutCirc: function (a, b, c, d, e) { 3199 if ((b /= e / 2) < 1) return -d / 2 * (Math.sqrt(1 - b * b) - 1) + c; 3200 return d / 2 * (Math.sqrt(1 - (b -= 2) * b) + 1) + c 3201 }, 3202 easeInElastic: function (a, b, c, d, e) { 3203 var f = 1.70158; 3204 var g = 0; 3205 var h = d; 3206 if (b == 0) return c; 3207 if ((b /= e) == 1) return c + d; 3208 if (!g) g = e * .3; 3209 if (h < Math.abs(d)) { 3210 h = d; 3211 var f = g / 4 3212 } else var f = g / (2 * Math.PI) * Math.asin(d / h); 3213 return -(h * Math.pow(2, 10 * (b -= 1)) * Math.sin((b * e - f) * 2 * Math.PI / g)) + c 3214 }, 3215 easeOutElastic: function (a, b, c, d, e) { 3216 var f = 1.70158; 3217 var g = 0; 3218 var h = d; 3219 if (b == 0) return c; 3220 if ((b /= e) == 1) return c + d; 3221 if (!g) g = e * .3; 3222 if (h < Math.abs(d)) { 3223 h = d; 3224 var f = g / 4 3225 } else var f = g / (2 * Math.PI) * Math.asin(d / h); 3226 return h * Math.pow(2, -10 * b) * Math.sin((b * e - f) * 2 * Math.PI / g) + d + c 3227 }, 3228 easeInOutElastic: function (a, b, c, d, e) { 3229 var f = 1.70158; 3230 var g = 0; 3231 var h = d; 3232 if (b == 0) return c; 3233 if ((b /= e / 2) == 2) return c + d; 3234 if (!g) g = e * .3 * 1.5; 3235 if (h < Math.abs(d)) { 3236 h = d; 3237 var f = g / 4 3238 } else var f = g / (2 * Math.PI) * Math.asin(d / h); 3239 if (b < 1) return -.5 * h * Math.pow(2, 10 * (b -= 1)) * Math.sin((b * e - f) * 2 * Math.PI / g) + c; 3240 return h * Math.pow(2, -10 * (b -= 1)) * Math.sin((b * e - f) * 2 * Math.PI / g) * .5 + d + c 3241 }, 3242 easeInBack: function (a, b, c, d, e, f) { 3243 if (f == undefined) f = 1.70158; 3244 return d * (b /= e) * b * ((f + 1) * b - f) + c 3245 }, 3246 easeOutBack: function (a, b, c, d, e, f) { 3247 if (f == undefined) f = 1.70158; 3248 return d * ((b = b / e - 1) * b * ((f + 1) * b + f) + 1) + c 3249 }, 3250 easeInOutBack: function (a, b, c, d, e, f) { 3251 if (f == undefined) f = 1.70158; 3252 if ((b /= e / 2) < 1) return d / 2 * b * b * (((f *= 1.525) + 1) * b - f) + c; 3253 return d / 2 * ((b -= 2) * b * (((f *= 1.525) + 1) * b + f) + 2) + c 3254 }, 3255 easeInBounce: function (a, b, c, d, e) { 3256 return d - jQuery.easing.easeOutBounce(a, e - b, 0, d, e) + c 3257 }, 3258 easeOutBounce: function (a, b, c, d, e) { 3259 if ((b /= e) < 1 / 2.75) { 3260 return d * 7.5625 * b * b + c 3261 } else if (b < 2 / 2.75) { 3262 return d * (7.5625 * (b -= 1.5 / 2.75) * b + .75) + c 3263 } else if (b < 2.5 / 2.75) { 3264 return d * (7.5625 * (b -= 2.25 / 2.75) * b + .9375) + c 3265 } else { 3266 return d * (7.5625 * (b -= 2.625 / 2.75) * b + .984375) + c 3267 } 3268 }, 3269 easeInOutBounce: function (a, b, c, d, e) { 3270 if (b < e / 2) return jQuery.easing.easeInBounce(a, b * 2, 0, d, e) * .5 + c; 3271 return jQuery.easing.easeOutBounce(a, b * 2 - e, 0, d, e) * .5 + d * .5 + c 3272 } 3273}); 3274// ================================================== 3275// fancyBox v3.5.7 3276// 3277// Licensed GPLv3 for open source use 3278// or fancyBox Commercial License for commercial use 3279// 3280// http://fancyapps.com/fancybox/ 3281// Copyright 2019 fancyApps 3282// 3283// ================================================== 3284(function (window, document, $, undefined) { 3285 "use strict"; 3286 3287 window.console = window.console || { 3288 info: function (stuff) {} 3289 }; 3290 3291 // If there's no jQuery, fancyBox can't work 3292 // ========================================= 3293 3294 if (!$) { 3295 return; 3296 } 3297 3298 // Check if fancyBox is already initialized 3299 // ======================================== 3300 3301 if ($.fn.fancybox) { 3302 console.info("fancyBox already initialized"); 3303 3304 return; 3305 } 3306 3307 // Private default settings 3308 // ======================== 3309 3310 var defaults = { 3311 // Close existing modals
vendor: 141,869 bytes, lines 3312-8848
3312 // Set this to false if you do not need to stack multiple instances 3313 closeExisting: false, 3314 3315 // Enable infinite gallery navigation 3316 loop: false, 3317 3318 // Horizontal space between slides 3319 gutter: 50, 3320 3321 // Enable keyboard navigation 3322 keyboard: true, 3323 3324 // Should allow caption to overlap the content 3325 preventCaptionOverlap: true, 3326 3327 // Should display navigation arrows at the screen edges 3328 arrows: true, 3329 3330 // Should display counter at the top left corner 3331 infobar: true, 3332 3333 // Should display close button (using `btnTpl.smallBtn` template) over the content 3334 // Can be true, false, "auto" 3335 // If "auto" - will be automatically enabled for "html", "inline" or "ajax" items 3336 smallBtn: "auto", 3337 3338 // Should display toolbar (buttons at the top) 3339 // Can be true, false, "auto" 3340 // If "auto" - will be automatically hidden if "smallBtn" is enabled 3341 toolbar: "auto", 3342 3343 // What buttons should appear in the top right corner. 3344 // Buttons will be created using templates from `btnTpl` option 3345 // and they will be placed into toolbar (class="fancybox-toolbar"` element) 3346 buttons: [ 3347 "zoom", 3348 //"share", 3349 "slideShow", 3350 //"fullScreen", 3351 //"download", 3352 "thumbs", 3353 "close" 3354 ], 3355 3356 // Detect "idle" time in seconds 3357 idleTime: 3, 3358 3359 // Disable right-click and use simple image protection for images 3360 protect: false, 3361 3362 // Shortcut to make content "modal" - disable keyboard navigtion, hide buttons, etc 3363 modal: false, 3364 3365 image: { 3366 // Wait for images to load before displaying 3367 // true - wait for image to load and then display; 3368 // false - display thumbnail and load the full-sized image over top, 3369 // requires predefined image dimensions (`data-width` and `data-height` attributes) 3370 preload: false 3371 }, 3372 3373 ajax: { 3374 // Object containing settings for ajax request 3375 settings: { 3376 // This helps to indicate that request comes from the modal 3377 // Feel free to change naming 3378 data: { 3379 fancybox: true 3380 } 3381 } 3382 }, 3383 3384 iframe: { 3385 // Iframe template 3386 tpl: '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" allowfullscreen="allowfullscreen" allow="autoplay; fullscreen" src=""></iframe>', 3387 3388 // Preload iframe before displaying it 3389 // This allows to calculate iframe content width and height 3390 // (note: Due to "Same Origin Policy", you can't get cross domain data). 3391 preload: true, 3392 3393 // Custom CSS styling for iframe wrapping element 3394 // You can use this to set custom iframe dimensions 3395 css: {}, 3396 3397 // Iframe tag attributes 3398 attr: { 3399 scrolling: "auto" 3400 } 3401 }, 3402 3403 // For HTML5 video only 3404 video: { 3405 tpl: '<video class="fancybox-video" controls controlsList="nodownload" poster="{{poster}}">' + 3406 '<source src="{{src}}" type="{{format}}" />' + 3407 'Sorry, your browser doesn\'t support embedded videos, <a href="{{src}}">download</a> and watch with your favorite video player!' + 3408 "</video>", 3409 format: "", // custom video format 3410 autoStart: true 3411 }, 3412 3413 // Default content type if cannot be detected automatically 3414 defaultType: "image", 3415 3416 // Open/close animation type 3417 // Possible values: 3418 // false - disable 3419 // "zoom" - zoom images from/to thumbnail 3420 // "fade" 3421 // "zoom-in-out" 3422 // 3423 animationEffect: "zoom", 3424 3425 // Duration in ms for open/close animation 3426 animationDuration: 366, 3427 3428 // Should image change opacity while zooming 3429 // If opacity is "auto", then opacity will be changed if image and thumbnail have different aspect ratios 3430 zoomOpacity: "auto", 3431 3432 // Transition effect between slides 3433 // 3434 // Possible values: 3435 // false - disable 3436 // "fade' 3437 // "slide' 3438 // "circular' 3439 // "tube' 3440 // "zoom-in-out' 3441 // "rotate' 3442 // 3443 transitionEffect: "fade", 3444 3445 // Duration in ms for transition animation 3446 transitionDuration: 366, 3447 3448 // Custom CSS class for slide element 3449 slideClass: "", 3450 3451 // Custom CSS class for layout 3452 baseClass: "", 3453 3454 // Base template for layout 3455 baseTpl: '<div class="fancybox-container" role="dialog" tabindex="-1">' + 3456 '<div class="fancybox-bg"></div>' + 3457 '<div class="fancybox-inner">' + 3458 '<div class="fancybox-infobar"><span data-fancybox-index></span> / <span data-fancybox-count></span></div>' + 3459 '<div class="fancybox-toolbar">{{buttons}}</div>' + 3460 '<div class="fancybox-navigation">{{arrows}}</div>' + 3461 '<div class="fancybox-stage"></div>' + 3462 '<div class="fancybox-caption"><div class="fancybox-caption__body"></div></div>' + 3463 "</div>" + 3464 "</div>", 3465 3466 // Loading indicator template 3467 spinnerTpl: '<div class="fancybox-loading"></div>', 3468 3469 // Error message template 3470 errorTpl: '<div class="fancybox-error"><p>{{ERROR}}</p></div>', 3471 3472 btnTpl: { 3473 download: '<a download data-fancybox-download class="fancybox-button fancybox-button--download" title="{{DOWNLOAD}}" href="javascript:;">' + 3474 '<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>' + 3475 "</a>", 3476 3477 zoom: '<button data-fancybox-zoom class="fancybox-button fancybox-button--zoom" title="{{ZOOM}}">' + 3478 '<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>' + 3479 "</button>", 3480 3481 close: '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="{{CLOSE}}">' + 3482 '<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>' + 3483 "</button>", 3484 3485 // Arrows 3486 arrowLeft: '<button data-fancybox-prev class="fancybox-button fancybox-button--arrow_left" title="{{PREV}}">' + 3487 '<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>' + 3488 "</button>", 3489 3490 arrowRight: '<button data-fancybox-next class="fancybox-button fancybox-button--arrow_right" title="{{NEXT}}">' + 3491 '<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>' + 3492 "</button>", 3493 3494 // This small close button will be appended to your html/inline/ajax content by default, 3495 // if "smallBtn" option is not set to false 3496 smallBtn: '<button type="button" data-fancybox-close class="fancybox-button fancybox-close-small" title="{{CLOSE}}">' + 3497 '<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>' + 3498 "</button>" 3499 }, 3500 3501 // Container is injected into this element 3502 parentEl: "body", 3503 3504 // Hide browser vertical scrollbars; use at your own risk 3505 hideScrollbar: true, 3506 3507 // Focus handling 3508 // ============== 3509 3510 // Try to focus on the first focusable element after opening 3511 autoFocus: true, 3512 3513 // Put focus back to active element after closing 3514 backFocus: true, 3515 3516 // Do not let user to focus on element outside modal content 3517 trapFocus: true, 3518 3519 // Module specific options 3520 // ======================= 3521 3522 fullScreen: { 3523 autoStart: false 3524 }, 3525 3526 // Set `touch: false` to disable panning/swiping 3527 touch: { 3528 vertical: true, // Allow to drag content vertically 3529 momentum: true // Continue movement after releasing mouse/touch when panning 3530 }, 3531 3532 // Hash value when initializing manually, 3533 // set `false` to disable hash change 3534 hash: null, 3535 3536 // Customize or add new media types 3537 // Example: 3538 /* 3539 media : { 3540 youtube : { 3541 params : { 3542 autoplay : 0 3543 } 3544 } 3545 } 3546 */ 3547 media: {}, 3548 3549 slideShow: { 3550 autoStart: false, 3551 speed: 3000 3552 }, 3553 3554 thumbs: { 3555 autoStart: false, // Display thumbnails on opening 3556 hideOnClose: true, // Hide thumbnail grid when closing animation starts 3557 parentEl: ".fancybox-container", // Container is injected into this element 3558 axis: "y" // Vertical (y) or horizontal (x) scrolling 3559 }, 3560 3561 // Use mousewheel to navigate gallery 3562 // If 'auto' - enabled for images only 3563 wheel: "auto", 3564 3565 // Callbacks 3566 //========== 3567 3568 // See Documentation/API/Events for more information 3569 // Example: 3570 /* 3571 afterShow: function( instance, current ) { 3572 console.info( 'Clicked element:' ); 3573 console.info( current.opts.$orig ); 3574 } 3575 */ 3576 3577 onInit: $.noop, // When instance has been initialized 3578 3579 beforeLoad: $.noop, // Before the content of a slide is being loaded 3580 afterLoad: $.noop, // When the content of a slide is done loading 3581 3582 beforeShow: $.noop, // Before open animation starts 3583 afterShow: $.noop, // When content is done loading and animating 3584 3585 beforeClose: $.noop, // Before the instance attempts to close. Return false to cancel the close. 3586 afterClose: $.noop, // After instance has been closed 3587 3588 onActivate: $.noop, // When instance is brought to front 3589 onDeactivate: $.noop, // When other instance has been activated 3590 3591 // Interaction 3592 // =========== 3593 3594 // Use options below to customize taken action when user clicks or double clicks on the fancyBox area, 3595 // each option can be string or method that returns value. 3596 // 3597 // Possible values: 3598 // "close" - close instance 3599 // "next" - move to next gallery item 3600 // "nextOrClose" - move to next gallery item or close if gallery has only one item 3601 // "toggleControls" - show/hide controls 3602 // "zoom" - zoom image (if loaded) 3603 // false - do nothing 3604 3605 // Clicked on the content 3606 clickContent: function (current, event) { 3607 return current.type === "image" ? "zoom" : false; 3608 }, 3609 3610 // Clicked on the slide 3611 clickSlide: "close", 3612 3613 // Clicked on the background (backdrop) element; 3614 // if you have not changed the layout, then most likely you need to use `clickSlide` option 3615 clickOutside: "close", 3616 3617 // Same as previous two, but for double click 3618 dblclickContent: false, 3619 dblclickSlide: false, 3620 dblclickOutside: false, 3621 3622 // Custom options when mobile device is detected 3623 // ============================================= 3624 3625 mobile: { 3626 preventCaptionOverlap: false, 3627 idleTime: false, 3628 clickContent: function (current, event) { 3629 return current.type === "image" ? "toggleControls" : false; 3630 }, 3631 clickSlide: function (current, event) { 3632 return current.type === "image" ? "toggleControls" : "close"; 3633 }, 3634 dblclickContent: function (current, event) { 3635 return current.type === "image" ? "zoom" : false; 3636 }, 3637 dblclickSlide: function (current, event) { 3638 return current.type === "image" ? "zoom" : false; 3639 } 3640 }, 3641 3642 // Internationalization 3643 // ==================== 3644 3645 lang: "en", 3646 i18n: { 3647 en: { 3648 CLOSE: "Close", 3649 NEXT: "Next", 3650 PREV: "Previous", 3651 ERROR: "The requested content cannot be loaded. <br/> Please try again later.", 3652 PLAY_START: "Start slideshow", 3653 PLAY_STOP: "Pause slideshow", 3654 FULL_SCREEN: "Full screen", 3655 THUMBS: "Thumbnails", 3656 DOWNLOAD: "Download", 3657 SHARE: "Share", 3658 ZOOM: "Zoom" 3659 }, 3660 de: { 3661 CLOSE: "Schließen", 3662 NEXT: "Weiter", 3663 PREV: "Zurück", 3664 ERROR: "Die angeforderten Daten konnten nicht geladen werden. <br/> Bitte versuchen Sie es später nochmal.", 3665 PLAY_START: "Diaschau starten", 3666 PLAY_STOP: "Diaschau beenden", 3667 FULL_SCREEN: "Vollbild", 3668 THUMBS: "Vorschaubilder", 3669 DOWNLOAD: "Herunterladen", 3670 SHARE: "Teilen", 3671 ZOOM: "Vergrößern" 3672 } 3673 } 3674 }; 3675 3676 // Few useful variables and methods 3677 // ================================ 3678 3679 var $W = $(window); 3680 var $D = $(document); 3681 3682 var called = 0; 3683 3684 // Check if an object is a jQuery object and not a native JavaScript object 3685 // ======================================================================== 3686 var isQuery = function (obj) { 3687 return obj && obj.hasOwnProperty && obj instanceof $; 3688 }; 3689 3690 // Handle multiple browsers for "requestAnimationFrame" and "cancelAnimationFrame" 3691 // =============================================================================== 3692 var requestAFrame = (function () { 3693 return ( 3694 window.requestAnimationFrame || 3695 window.webkitRequestAnimationFrame || 3696 window.mozRequestAnimationFrame || 3697 window.oRequestAnimationFrame || 3698 // if all else fails, use setTimeout 3699 function (callback) { 3700 return window.setTimeout(callback, 1000 / 60); 3701 } 3702 ); 3703 })(); 3704 3705 var cancelAFrame = (function () { 3706 return ( 3707 window.cancelAnimationFrame || 3708 window.webkitCancelAnimationFrame || 3709 window.mozCancelAnimationFrame || 3710 window.oCancelAnimationFrame || 3711 function (id) { 3712 window.clearTimeout(id); 3713 } 3714 ); 3715 })(); 3716 3717 // Detect the supported transition-end event property name 3718 // ======================================================= 3719 var transitionEnd = (function () { 3720 var el = document.createElement("fakeelement"), 3721 t; 3722 3723 var transitions = { 3724 transition: "transitionend", 3725 OTransition: "oTransitionEnd", 3726 MozTransition: "transitionend", 3727 WebkitTransition: "webkitTransitionEnd" 3728 }; 3729 3730 for (t in transitions) { 3731 if (el.style[t] !== undefined) { 3732 return transitions[t]; 3733 } 3734 } 3735 3736 return "transitionend"; 3737 })(); 3738 3739 // Force redraw on an element. 3740 // This helps in cases where the browser doesn't redraw an updated element properly 3741 // ================================================================================ 3742 var forceRedraw = function ($el) { 3743 return $el && $el.length && $el[0].offsetHeight; 3744 }; 3745 3746 // Exclude array (`buttons`) options from deep merging 3747 // =================================================== 3748 var mergeOpts = function (opts1, opts2) { 3749 var rez = $.extend(true, {}, opts1, opts2); 3750 3751 $.each(opts2, function (key, value) { 3752 if ($.isArray(value)) { 3753 rez[key] = value; 3754 } 3755 }); 3756 3757 return rez; 3758 }; 3759 3760 // How much of an element is visible in viewport 3761 // ============================================= 3762 3763 var inViewport = function (elem) { 3764 var elemCenter, rez; 3765 3766 if (!elem || elem.ownerDocument !== document) { 3767 return false; 3768 } 3769 3770 $(".fancybox-container").css("pointer-events", "none"); 3771 3772 elemCenter = { 3773 x: elem.getBoundingClientRect().left + elem.offsetWidth / 2, 3774 y: elem.getBoundingClientRect().top + elem.offsetHeight / 2 3775 }; 3776 3777 rez = document.elementFromPoint(elemCenter.x, elemCenter.y) === elem; 3778 3779 $(".fancybox-container").css("pointer-events", ""); 3780 3781 return rez; 3782 }; 3783 3784 // Class definition 3785 // ================ 3786 3787 var FancyBox = function (content, opts, index) { 3788 var self = this; 3789 3790 self.opts = mergeOpts({ 3791 index: index 3792 }, $.fancybox.defaults); 3793 3794 if ($.isPlainObject(opts)) { 3795 self.opts = mergeOpts(self.opts, opts); 3796 } 3797 3798 if ($.fancybox.isMobile) { 3799 self.opts = mergeOpts(self.opts, self.opts.mobile); 3800 } 3801 3802 self.id = self.opts.id || ++called; 3803 3804 self.currIndex = parseInt(self.opts.index, 10) || 0; 3805 self.prevIndex = null; 3806 3807 self.prevPos = null; 3808 self.currPos = 0; 3809 3810 self.firstRun = true; 3811 3812 // All group items 3813 self.group = []; 3814 3815 // Existing slides (for current, next and previous gallery items) 3816 self.slides = {}; 3817 3818 // Create group elements 3819 self.addContent(content); 3820 3821 if (!self.group.length) { 3822 return; 3823 } 3824 3825 self.init(); 3826 }; 3827 3828 $.extend(FancyBox.prototype, { 3829 // Create DOM structure 3830 // ==================== 3831 3832 init: function () { 3833 var self = this, 3834 firstItem = self.group[self.currIndex], 3835 firstItemOpts = firstItem.opts, 3836 $container, 3837 buttonStr; 3838 3839 if (firstItemOpts.closeExisting) { 3840 $.fancybox.close(true); 3841 } 3842 3843 // Hide scrollbars 3844 // =============== 3845 3846 $("body").addClass("fancybox-active"); 3847 3848 if ( 3849 !$.fancybox.getInstance() && 3850 firstItemOpts.hideScrollbar !== false && 3851 !$.fancybox.isMobile && 3852 document.body.scrollHeight > window.innerHeight 3853 ) { 3854 $("head").append( 3855 '<style id="fancybox-style-noscroll" type="text/css">.compensate-for-scrollbar{margin-right:' + 3856 (window.innerWidth - document.documentElement.clientWidth) + 3857 "px;}</style>" 3858 ); 3859 3860 $("body").addClass("compensate-for-scrollbar"); 3861 } 3862 3863 // Build html markup and set references 3864 // ==================================== 3865 3866 // Build html code for buttons and insert into main template 3867 buttonStr = ""; 3868 3869 $.each(firstItemOpts.buttons, function (index, value) { 3870 buttonStr += firstItemOpts.btnTpl[value] || ""; 3871 }); 3872 3873 // Create markup from base template, it will be initially hidden to 3874 // avoid unnecessary work like painting while initializing is not complete 3875 $container = $( 3876 self.translate( 3877 self, 3878 firstItemOpts.baseTpl 3879 .replace("{{buttons}}", buttonStr) 3880 .replace("{{arrows}}", firstItemOpts.btnTpl.arrowLeft + firstItemOpts.btnTpl.arrowRight) 3881 ) 3882 ) 3883 .attr("id", "fancybox-container-" + self.id) 3884 .addClass(firstItemOpts.baseClass) 3885 .data("FancyBox", self) 3886 .appendTo(firstItemOpts.parentEl); 3887 3888 // Create object holding references to jQuery wrapped nodes 3889 self.$refs = { 3890 container: $container 3891 }; 3892 3893 ["bg", "inner", "infobar", "toolbar", "stage", "caption", "navigation"].forEach(function (item) { 3894 self.$refs[item] = $container.find(".fancybox-" + item); 3895 }); 3896 3897 self.trigger("onInit"); 3898 3899 // Enable events, deactive previous instances 3900 self.activate(); 3901 3902 // Build slides, load and reveal content 3903 self.jumpTo(self.currIndex); 3904 }, 3905 3906 // Simple i18n support - replaces object keys found in template 3907 // with corresponding values 3908 // ============================================================ 3909 3910 translate: function (obj, str) { 3911 var arr = obj.opts.i18n[obj.opts.lang] || obj.opts.i18n.en; 3912 3913 return str.replace(/\{\{(\w+)\}\}/g, function (match, n) { 3914 return arr[n] === undefined ? match : arr[n]; 3915 }); 3916 }, 3917 3918 // Populate current group with fresh content 3919 // Check if each object has valid type and content 3920 // =============================================== 3921 3922 addContent: function (content) { 3923 var self = this, 3924 items = $.makeArray(content), 3925 thumbs; 3926 3927 $.each(items, function (i, item) { 3928 var obj = {}, 3929 opts = {}, 3930 $item, 3931 type, 3932 found, 3933 src, 3934 srcParts; 3935 3936 // Step 1 - Make sure we have an object 3937 // ==================================== 3938 3939 if ($.isPlainObject(item)) { 3940 // We probably have manual usage here, something like 3941 // $.fancybox.open( [ { src : "image.jpg", type : "image" } ] ) 3942 3943 obj = item; 3944 opts = item.opts || item; 3945 } else if ($.type(item) === "object" && $(item).length) { 3946 // Here we probably have jQuery collection returned by some selector 3947 $item = $(item); 3948 3949 // Support attributes like `data-options='{"touch" : false}'` and `data-touch='false'` 3950 opts = $item.data() || {}; 3951 opts = $.extend(true, {}, opts, opts.options); 3952 3953 // Here we store clicked element 3954 opts.$orig = $item; 3955 3956 obj.src = self.opts.src || opts.src || $item.attr("href"); 3957 3958 // Assume that simple syntax is used, for example: 3959 // `$.fancybox.open( $("#test"), {} );` 3960 if (!obj.type && !obj.src) { 3961 obj.type = "inline"; 3962 obj.src = item; 3963 } 3964 } else { 3965 // Assume we have a simple html code, for example: 3966 // $.fancybox.open( '<div><h1>Hi!</h1></div>' ); 3967 obj = { 3968 type: "html", 3969 src: item + "" 3970 }; 3971 } 3972 3973 // Each gallery object has full collection of options 3974 obj.opts = $.extend(true, {}, self.opts, opts); 3975 3976 // Do not merge buttons array 3977 if ($.isArray(opts.buttons)) { 3978 obj.opts.buttons = opts.buttons; 3979 } 3980 3981 if ($.fancybox.isMobile && obj.opts.mobile) { 3982 obj.opts = mergeOpts(obj.opts, obj.opts.mobile); 3983 } 3984 3985 // Step 2 - Make sure we have content type, if not - try to guess 3986 // ============================================================== 3987 3988 type = obj.type || obj.opts.type; 3989 src = obj.src || ""; 3990 3991 if (!type && src) { 3992 if ((found = src.match(/\.(mp4|mov|ogv|webm)((\?|#).*)?$/i))) { 3993 type = "video"; 3994 3995 if (!obj.opts.video.format) { 3996 obj.opts.video.format = "video/" + (found[1] === "ogv" ? "ogg" : found[1]); 3997 } 3998 } else if (src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i)) { 3999 type = "image"; 4000 } else if (src.match(/\.(pdf)((\?|#).*)?$/i)) { 4001 type = "iframe"; 4002 obj = $.extend(true, obj, { 4003 contentType: "pdf", 4004 opts: { 4005 iframe: { 4006 preload: false 4007 } 4008 } 4009 }); 4010 } else if (src.charAt(0) === "#") { 4011 type = "inline"; 4012 } 4013 } 4014 4015 if (type) { 4016 obj.type = type; 4017 } else { 4018 self.trigger("objectNeedsType", obj); 4019 } 4020 4021 if (!obj.contentType) { 4022 obj.contentType = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1 ? "html" : obj.type; 4023 } 4024 4025 // Step 3 - Some adjustments 4026 // ========================= 4027 4028 obj.index = self.group.length; 4029 4030 if (obj.opts.smallBtn == "auto") { 4031 obj.opts.smallBtn = $.inArray(obj.type, ["html", "inline", "ajax"]) > -1; 4032 } 4033 4034 if (obj.opts.toolbar === "auto") { 4035 obj.opts.toolbar = !obj.opts.smallBtn; 4036 } 4037 4038 // Find thumbnail image, check if exists and if is in the viewport 4039 obj.$thumb = obj.opts.$thumb || null; 4040 4041 if (obj.opts.$trigger && obj.index === self.opts.index) { 4042 obj.$thumb = obj.opts.$trigger.find("img:first"); 4043 4044 if (obj.$thumb.length) { 4045 obj.opts.$orig = obj.opts.$trigger; 4046 } 4047 } 4048 4049 if (!(obj.$thumb && obj.$thumb.length) && obj.opts.$orig) { 4050 obj.$thumb = obj.opts.$orig.find("img:first"); 4051 } 4052 4053 if (obj.$thumb && !obj.$thumb.length) { 4054 obj.$thumb = null; 4055 } 4056 4057 obj.thumb = obj.opts.thumb || (obj.$thumb ? obj.$thumb[0].src : null); 4058 4059 // "caption" is a "special" option, it can be used to customize caption per gallery item 4060 if ($.type(obj.opts.caption) === "function") { 4061 obj.opts.caption = obj.opts.caption.apply(item, [self, obj]); 4062 } 4063 4064 if ($.type(self.opts.caption) === "function") { 4065 obj.opts.caption = self.opts.caption.apply(item, [self, obj]); 4066 } 4067 4068 // Make sure we have caption as a string or jQuery object 4069 if (!(obj.opts.caption instanceof $)) { 4070 obj.opts.caption = obj.opts.caption === undefined ? "" : obj.opts.caption + ""; 4071 } 4072 4073 // Check if url contains "filter" used to filter the content 4074 // Example: "ajax.html #something" 4075 if (obj.type === "ajax") { 4076 srcParts = src.split(/\s+/, 2); 4077 4078 if (srcParts.length > 1) { 4079 obj.src = srcParts.shift(); 4080 4081 obj.opts.filter = srcParts.shift(); 4082 } 4083 } 4084 4085 // Hide all buttons and disable interactivity for modal items 4086 if (obj.opts.modal) { 4087 obj.opts = $.extend(true, obj.opts, { 4088 trapFocus: true, 4089 // Remove buttons 4090 infobar: 0, 4091 toolbar: 0, 4092 4093 smallBtn: 0, 4094 4095 // Disable keyboard navigation 4096 keyboard: 0, 4097 4098 // Disable some modules 4099 slideShow: 0, 4100 fullScreen: 0, 4101 thumbs: 0, 4102 touch: 0, 4103 4104 // Disable click event handlers 4105 clickContent: false, 4106 clickSlide: false, 4107 clickOutside: false, 4108 dblclickContent: false, 4109 dblclickSlide: false, 4110 dblclickOutside: false 4111 }); 4112 } 4113 4114 // Step 4 - Add processed object to group 4115 // ====================================== 4116 4117 self.group.push(obj); 4118 }); 4119 4120 // Update controls if gallery is already opened 4121 if (Object.keys(self.slides).length) { 4122 self.updateControls(); 4123 4124 // Update thumbnails, if needed 4125 thumbs = self.Thumbs; 4126 4127 if (thumbs && thumbs.isActive) { 4128 thumbs.create(); 4129 4130 thumbs.focus(); 4131 } 4132 } 4133 }, 4134 4135 // Attach an event handler functions for: 4136 // - navigation buttons 4137 // - browser scrolling, resizing; 4138 // - focusing 4139 // - keyboard 4140 // - detecting inactivity 4141 // ====================================== 4142 4143 addEvents: function () { 4144 var self = this; 4145 4146 self.removeEvents(); 4147 4148 // Make navigation elements clickable 4149 // ================================== 4150 4151 self.$refs.container 4152 .on("click.fb-close", "[data-fancybox-close]", function (e) { 4153 e.stopPropagation(); 4154 e.preventDefault(); 4155 4156 self.close(e); 4157 }) 4158 .on("touchstart.fb-prev click.fb-prev", "[data-fancybox-prev]", function (e) { 4159 e.stopPropagation(); 4160 e.preventDefault(); 4161 4162 self.previous(); 4163 }) 4164 .on("touchstart.fb-next click.fb-next", "[data-fancybox-next]", function (e) { 4165 e.stopPropagation(); 4166 e.preventDefault(); 4167 4168 self.next(); 4169 }) 4170 .on("click.fb", "[data-fancybox-zoom]", function (e) { 4171 // Click handler for zoom button 4172 self[self.isScaledDown() ? "scaleToActual" : "scaleToFit"](); 4173 }); 4174 4175 // Handle page scrolling and browser resizing 4176 // ========================================== 4177 4178 $W.on("orientationchange.fb resize.fb", function (e) { 4179 if (e && e.originalEvent && e.originalEvent.type === "resize") { 4180 if (self.requestId) { 4181 cancelAFrame(self.requestId); 4182 } 4183 4184 self.requestId = requestAFrame(function () { 4185 self.update(e); 4186 }); 4187 } else { 4188 if (self.current && self.current.type === "iframe") { 4189 self.$refs.stage.hide(); 4190 } 4191 4192 setTimeout( 4193 function () { 4194 self.$refs.stage.show(); 4195 4196 self.update(e); 4197 }, 4198 $.fancybox.isMobile ? 600 : 250 4199 ); 4200 } 4201 }); 4202 4203 $D.on("keydown.fb", function (e) { 4204 var instance = $.fancybox ? $.fancybox.getInstance() : null, 4205 current = instance.current, 4206 keycode = e.keyCode || e.which; 4207 4208 // Trap keyboard focus inside of the modal 4209 // ======================================= 4210 4211 if (keycode == 9) { 4212 if (current.opts.trapFocus) { 4213 self.focus(e); 4214 } 4215 4216 return; 4217 } 4218 4219 // Enable keyboard navigation 4220 // ========================== 4221 4222 if (!current.opts.keyboard || e.ctrlKey || e.altKey || e.shiftKey || $(e.target).is("input,textarea,video,audio,select")) { 4223 return; 4224 } 4225 4226 // Backspace and Esc keys 4227 if (keycode === 8 || keycode === 27) { 4228 e.preventDefault(); 4229 4230 self.close(e); 4231 4232 return; 4233 } 4234 4235 // Left arrow and Up arrow 4236 if (keycode === 37 || keycode === 38) { 4237 e.preventDefault(); 4238 4239 self.previous(); 4240 4241 return; 4242 } 4243 4244 // Righ arrow and Down arrow 4245 if (keycode === 39 || keycode === 40) { 4246 e.preventDefault(); 4247 4248 self.next(); 4249 4250 return; 4251 } 4252 4253 self.trigger("afterKeydown", e, keycode); 4254 }); 4255 4256 // Hide controls after some inactivity period 4257 if (self.group[self.currIndex].opts.idleTime) { 4258 self.idleSecondsCounter = 0; 4259 4260 $D.on( 4261 "mousemove.fb-idle mouseleave.fb-idle mousedown.fb-idle touchstart.fb-idle touchmove.fb-idle scroll.fb-idle keydown.fb-idle", 4262 function (e) { 4263 self.idleSecondsCounter = 0; 4264 4265 if (self.isIdle) { 4266 self.showControls(); 4267 } 4268 4269 self.isIdle = false; 4270 } 4271 ); 4272 4273 self.idleInterval = window.setInterval(function () { 4274 self.idleSecondsCounter++; 4275 4276 if (self.idleSecondsCounter >= self.group[self.currIndex].opts.idleTime && !self.isDragging) { 4277 self.isIdle = true; 4278 self.idleSecondsCounter = 0; 4279 4280 self.hideControls(); 4281 } 4282 }, 1000); 4283 } 4284 }, 4285 4286 // Remove events added by the core 4287 // =============================== 4288 4289 removeEvents: function () { 4290 var self = this; 4291 4292 $W.off("orientationchange.fb resize.fb"); 4293 $D.off("keydown.fb .fb-idle"); 4294 4295 this.$refs.container.off(".fb-close .fb-prev .fb-next"); 4296 4297 if (self.idleInterval) { 4298 window.clearInterval(self.idleInterval); 4299 4300 self.idleInterval = null; 4301 } 4302 }, 4303 4304 // Change to previous gallery item 4305 // =============================== 4306 4307 previous: function (duration) { 4308 return this.jumpTo(this.currPos - 1, duration); 4309 }, 4310 4311 // Change to next gallery item 4312 // =========================== 4313 4314 next: function (duration) { 4315 return this.jumpTo(this.currPos + 1, duration); 4316 }, 4317 4318 // Switch to selected gallery item 4319 // =============================== 4320 4321 jumpTo: function (pos, duration) { 4322 var self = this, 4323 groupLen = self.group.length, 4324 firstRun, 4325 isMoved, 4326 loop, 4327 current, 4328 previous, 4329 slidePos, 4330 stagePos, 4331 prop, 4332 diff; 4333 4334 if (self.isDragging || self.isClosing || (self.isAnimating && self.firstRun)) { 4335 return; 4336 } 4337 4338 // Should loop? 4339 pos = parseInt(pos, 10); 4340 loop = self.current ? self.current.opts.loop : self.opts.loop; 4341 4342 if (!loop && (pos < 0 || pos >= groupLen)) { 4343 return false; 4344 } 4345 4346 // Check if opening for the first time; this helps to speed things up 4347 firstRun = self.firstRun = !Object.keys(self.slides).length; 4348 4349 // Create slides 4350 previous = self.current; 4351 4352 self.prevIndex = self.currIndex; 4353 self.prevPos = self.currPos; 4354 4355 current = self.createSlide(pos); 4356 4357 if (groupLen > 1) { 4358 if (loop || current.index < groupLen - 1) { 4359 self.createSlide(pos + 1); 4360 } 4361 4362 if (loop || current.index > 0) { 4363 self.createSlide(pos - 1); 4364 } 4365 } 4366 4367 self.current = current; 4368 self.currIndex = current.index; 4369 self.currPos = current.pos; 4370 4371 self.trigger("beforeShow", firstRun); 4372 4373 self.updateControls(); 4374 4375 // Validate duration length 4376 current.forcedDuration = undefined; 4377 4378 if ($.isNumeric(duration)) { 4379 current.forcedDuration = duration; 4380 } else { 4381 duration = current.opts[firstRun ? "animationDuration" : "transitionDuration"]; 4382 } 4383 4384 duration = parseInt(duration, 10); 4385 4386 // Check if user has swiped the slides or if still animating 4387 isMoved = self.isMoved(current); 4388 4389 // Make sure current slide is visible 4390 current.$slide.addClass("fancybox-slide--current"); 4391 4392 // Fresh start - reveal container, current slide and start loading content 4393 if (firstRun) { 4394 if (current.opts.animationEffect && duration) { 4395 self.$refs.container.css("transition-duration", duration + "ms"); 4396 } 4397 4398 self.$refs.container.addClass("fancybox-is-open").trigger("focus"); 4399 4400 // Attempt to load content into slide 4401 // This will later call `afterLoad` -> `revealContent` 4402 self.loadSlide(current); 4403 4404 self.preload("image"); 4405 4406 return; 4407 } 4408 4409 // Get actual slide/stage positions (before cleaning up) 4410 slidePos = $.fancybox.getTranslate(previous.$slide); 4411 stagePos = $.fancybox.getTranslate(self.$refs.stage); 4412 4413 // Clean up all slides 4414 $.each(self.slides, function (index, slide) { 4415 $.fancybox.stop(slide.$slide, true); 4416 }); 4417 4418 if (previous.pos !== current.pos) { 4419 previous.isComplete = false; 4420 } 4421 4422 previous.$slide.removeClass("fancybox-slide--complete fancybox-slide--current"); 4423 4424 // If slides are out of place, then animate them to correct position 4425 if (isMoved) { 4426 // Calculate horizontal swipe distance 4427 diff = slidePos.left - (previous.pos * slidePos.width + previous.pos * previous.opts.gutter); 4428 4429 $.each(self.slides, function (index, slide) { 4430 slide.$slide.removeClass("fancybox-animated").removeClass(function (index, className) { 4431 return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" "); 4432 }); 4433 4434 // Make sure that each slide is in equal distance 4435 // This is mostly needed for freshly added slides, because they are not yet positioned 4436 var leftPos = slide.pos * slidePos.width + slide.pos * slide.opts.gutter; 4437 4438 $.fancybox.setTranslate(slide.$slide, { 4439 top: 0, 4440 left: leftPos - stagePos.left + diff 4441 }); 4442 4443 if (slide.pos !== current.pos) { 4444 slide.$slide.addClass("fancybox-slide--" + (slide.pos > current.pos ? "next" : "previous")); 4445 } 4446 4447 // Redraw to make sure that transition will start 4448 forceRedraw(slide.$slide); 4449 4450 // Animate the slide 4451 $.fancybox.animate( 4452 slide.$slide, { 4453 top: 0, 4454 left: (slide.pos - current.pos) * slidePos.width + (slide.pos - current.pos) * slide.opts.gutter 4455 }, 4456 duration, 4457 function () { 4458 slide.$slide 4459 .css({ 4460 transform: "", 4461 opacity: "" 4462 }) 4463 .removeClass("fancybox-slide--next fancybox-slide--previous"); 4464 4465 if (slide.pos === self.currPos) { 4466 self.complete(); 4467 } 4468 } 4469 ); 4470 }); 4471 } else if (duration && current.opts.transitionEffect) { 4472 // Set transition effect for previously active slide 4473 prop = "fancybox-animated fancybox-fx-" + current.opts.transitionEffect; 4474 4475 previous.$slide.addClass("fancybox-slide--" + (previous.pos > current.pos ? "next" : "previous")); 4476 4477 $.fancybox.animate( 4478 previous.$slide, 4479 prop, 4480 duration, 4481 function () { 4482 previous.$slide.removeClass(prop).removeClass("fancybox-slide--next fancybox-slide--previous"); 4483 }, 4484 false 4485 ); 4486 } 4487 4488 if (current.isLoaded) { 4489 self.revealContent(current); 4490 } else { 4491 self.loadSlide(current); 4492 } 4493 4494 self.preload("image"); 4495 }, 4496 4497 // Create new "slide" element 4498 // These are gallery items that are actually added to DOM 4499 // ======================================================= 4500 4501 createSlide: function (pos) { 4502 var self = this, 4503 $slide, 4504 index; 4505 4506 index = pos % self.group.length; 4507 index = index < 0 ? self.group.length + index : index; 4508 4509 if (!self.slides[pos] && self.group[index]) { 4510 $slide = $('<div class="fancybox-slide"></div>').appendTo(self.$refs.stage); 4511 4512 self.slides[pos] = $.extend(true, {}, self.group[index], { 4513 pos: pos, 4514 $slide: $slide, 4515 isLoaded: false 4516 }); 4517 4518 self.updateSlide(self.slides[pos]); 4519 } 4520 4521 return self.slides[pos]; 4522 }, 4523 4524 // Scale image to the actual size of the image; 4525 // x and y values should be relative to the slide 4526 // ============================================== 4527 4528 scaleToActual: function (x, y, duration) { 4529 var self = this, 4530 current = self.current, 4531 $content = current.$content, 4532 canvasWidth = $.fancybox.getTranslate(current.$slide).width, 4533 canvasHeight = $.fancybox.getTranslate(current.$slide).height, 4534 newImgWidth = current.width, 4535 newImgHeight = current.height, 4536 imgPos, 4537 posX, 4538 posY, 4539 scaleX, 4540 scaleY; 4541 4542 if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) { 4543 return; 4544 } 4545 4546 self.isAnimating = true; 4547 4548 $.fancybox.stop($content); 4549 4550 x = x === undefined ? canvasWidth * 0.5 : x; 4551 y = y === undefined ? canvasHeight * 0.5 : y; 4552 4553 imgPos = $.fancybox.getTranslate($content); 4554 4555 imgPos.top -= $.fancybox.getTranslate(current.$slide).top; 4556 imgPos.left -= $.fancybox.getTranslate(current.$slide).left; 4557 4558 scaleX = newImgWidth / imgPos.width; 4559 scaleY = newImgHeight / imgPos.height; 4560 4561 // Get center position for original image 4562 posX = canvasWidth * 0.5 - newImgWidth * 0.5; 4563 posY = canvasHeight * 0.5 - newImgHeight * 0.5; 4564 4565 // Make sure image does not move away from edges 4566 if (newImgWidth > canvasWidth) { 4567 posX = imgPos.left * scaleX - (x * scaleX - x); 4568 4569 if (posX > 0) { 4570 posX = 0; 4571 } 4572 4573 if (posX < canvasWidth - newImgWidth) { 4574 posX = canvasWidth - newImgWidth; 4575 } 4576 } 4577 4578 if (newImgHeight > canvasHeight) { 4579 posY = imgPos.top * scaleY - (y * scaleY - y); 4580 4581 if (posY > 0) { 4582 posY = 0; 4583 } 4584 4585 if (posY < canvasHeight - newImgHeight) { 4586 posY = canvasHeight - newImgHeight; 4587 } 4588 } 4589 4590 self.updateCursor(newImgWidth, newImgHeight); 4591 4592 $.fancybox.animate( 4593 $content, { 4594 top: posY, 4595 left: posX, 4596 scaleX: scaleX, 4597 scaleY: scaleY 4598 }, 4599 duration || 366, 4600 function () { 4601 self.isAnimating = false; 4602 } 4603 ); 4604 4605 // Stop slideshow 4606 if (self.SlideShow && self.SlideShow.isActive) { 4607 self.SlideShow.stop(); 4608 } 4609 }, 4610 4611 // Scale image to fit inside parent element 4612 // ======================================== 4613 4614 scaleToFit: function (duration) { 4615 var self = this, 4616 current = self.current, 4617 $content = current.$content, 4618 end; 4619 4620 if (self.isAnimating || self.isMoved() || !$content || !(current.type == "image" && current.isLoaded && !current.hasError)) { 4621 return; 4622 } 4623 4624 self.isAnimating = true; 4625 4626 $.fancybox.stop($content); 4627 4628 end = self.getFitPos(current); 4629 4630 self.updateCursor(end.width, end.height); 4631 4632 $.fancybox.animate( 4633 $content, { 4634 top: end.top, 4635 left: end.left, 4636 scaleX: end.width / $content.width(), 4637 scaleY: end.height / $content.height() 4638 }, 4639 duration || 366, 4640 function () { 4641 self.isAnimating = false; 4642 } 4643 ); 4644 }, 4645 4646 // Calculate image size to fit inside viewport 4647 // =========================================== 4648 4649 getFitPos: function (slide) { 4650 var self = this, 4651 $content = slide.$content, 4652 $slide = slide.$slide, 4653 width = slide.width || slide.opts.width, 4654 height = slide.height || slide.opts.height, 4655 maxWidth, 4656 maxHeight, 4657 minRatio, 4658 aspectRatio, 4659 rez = {}; 4660 4661 if (!slide.isLoaded || !$content || !$content.length) { 4662 return false; 4663 } 4664 4665 maxWidth = $.fancybox.getTranslate(self.$refs.stage).width; 4666 maxHeight = $.fancybox.getTranslate(self.$refs.stage).height; 4667 4668 maxWidth -= 4669 parseFloat($slide.css("paddingLeft")) + 4670 parseFloat($slide.css("paddingRight")) + 4671 parseFloat($content.css("marginLeft")) + 4672 parseFloat($content.css("marginRight")); 4673 4674 maxHeight -= 4675 parseFloat($slide.css("paddingTop")) + 4676 parseFloat($slide.css("paddingBottom")) + 4677 parseFloat($content.css("marginTop")) + 4678 parseFloat($content.css("marginBottom")); 4679 4680 if (!width || !height) { 4681 width = maxWidth; 4682 height = maxHeight; 4683 } 4684 4685 minRatio = Math.min(1, maxWidth / width, maxHeight / height); 4686 4687 width = minRatio * width; 4688 height = minRatio * height; 4689 4690 // Adjust width/height to precisely fit into container 4691 if (width > maxWidth - 0.5) { 4692 width = maxWidth; 4693 } 4694 4695 if (height > maxHeight - 0.5) { 4696 height = maxHeight; 4697 } 4698 4699 if (slide.type === "image") { 4700 rez.top = Math.floor((maxHeight - height) * 0.5) + parseFloat($slide.css("paddingTop")); 4701 rez.left = Math.floor((maxWidth - width) * 0.5) + parseFloat($slide.css("paddingLeft")); 4702 } else if (slide.contentType === "video") { 4703 // Force aspect ratio for the video 4704 // "I say the whole world must learn of our peaceful ways… by force!" 4705 aspectRatio = slide.opts.width && slide.opts.height ? width / height : slide.opts.ratio || 16 / 9; 4706 4707 if (height > width / aspectRatio) { 4708 height = width / aspectRatio; 4709 } else if (width > height * aspectRatio) { 4710 width = height * aspectRatio; 4711 } 4712 } 4713 4714 rez.width = width; 4715 rez.height = height; 4716 4717 return rez; 4718 }, 4719 4720 // Update content size and position for all slides 4721 // ============================================== 4722 4723 update: function (e) { 4724 var self = this; 4725 4726 $.each(self.slides, function (key, slide) { 4727 self.updateSlide(slide, e); 4728 }); 4729 }, 4730 4731 // Update slide content position and size 4732 // ====================================== 4733 4734 updateSlide: function (slide, e) { 4735 var self = this, 4736 $content = slide && slide.$content, 4737 width = slide.width || slide.opts.width, 4738 height = slide.height || slide.opts.height, 4739 $slide = slide.$slide; 4740 4741 // First, prevent caption overlap, if needed 4742 self.adjustCaption(slide); 4743 4744 // Then resize content to fit inside the slide 4745 if ($content && (width || height || slide.contentType === "video") && !slide.hasError) { 4746 $.fancybox.stop($content); 4747 4748 $.fancybox.setTranslate($content, self.getFitPos(slide)); 4749 4750 if (slide.pos === self.currPos) { 4751 self.isAnimating = false; 4752 4753 self.updateCursor(); 4754 } 4755 } 4756 4757 // Then some adjustments 4758 self.adjustLayout(slide); 4759 4760 if ($slide.length) { 4761 $slide.trigger("refresh"); 4762 4763 if (slide.pos === self.currPos) { 4764 self.$refs.toolbar 4765 .add(self.$refs.navigation.find(".fancybox-button--arrow_right")) 4766 .toggleClass("compensate-for-scrollbar", $slide.get(0).scrollHeight > $slide.get(0).clientHeight); 4767 } 4768 } 4769 4770 self.trigger("onUpdate", slide, e); 4771 }, 4772 4773 // Horizontally center slide 4774 // ========================= 4775 4776 centerSlide: function (duration) { 4777 var self = this, 4778 current = self.current, 4779 $slide = current.$slide; 4780 4781 if (self.isClosing || !current) { 4782 return; 4783 } 4784 4785 $slide.siblings().css({ 4786 transform: "", 4787 opacity: "" 4788 }); 4789 4790 $slide 4791 .parent() 4792 .children() 4793 .removeClass("fancybox-slide--previous fancybox-slide--next"); 4794 4795 $.fancybox.animate( 4796 $slide, { 4797 top: 0, 4798 left: 0, 4799 opacity: 1 4800 }, 4801 duration === undefined ? 0 : duration, 4802 function () { 4803 // Clean up 4804 $slide.css({ 4805 transform: "", 4806 opacity: "" 4807 }); 4808 4809 if (!current.isComplete) { 4810 self.complete(); 4811 } 4812 }, 4813 false 4814 ); 4815 }, 4816 4817 // Check if current slide is moved (swiped) 4818 // ======================================== 4819 4820 isMoved: function (slide) { 4821 var current = slide || this.current, 4822 slidePos, 4823 stagePos; 4824 4825 if (!current) { 4826 return false; 4827 } 4828 4829 stagePos = $.fancybox.getTranslate(this.$refs.stage); 4830 slidePos = $.fancybox.getTranslate(current.$slide); 4831 4832 return ( 4833 !current.$slide.hasClass("fancybox-animated") && 4834 (Math.abs(slidePos.top - stagePos.top) > 0.5 || Math.abs(slidePos.left - stagePos.left) > 0.5) 4835 ); 4836 }, 4837 4838 // Update cursor style depending if content can be zoomed 4839 // ====================================================== 4840 4841 updateCursor: function (nextWidth, nextHeight) { 4842 var self = this, 4843 current = self.current, 4844 $container = self.$refs.container, 4845 canPan, 4846 isZoomable; 4847 4848 if (!current || self.isClosing || !self.Guestures) { 4849 return; 4850 } 4851 4852 $container.removeClass("fancybox-is-zoomable fancybox-can-zoomIn fancybox-can-zoomOut fancybox-can-swipe fancybox-can-pan"); 4853 4854 canPan = self.canPan(nextWidth, nextHeight); 4855 4856 isZoomable = canPan ? true : self.isZoomable(); 4857 4858 $container.toggleClass("fancybox-is-zoomable", isZoomable); 4859 4860 $("[data-fancybox-zoom]").prop("disabled", !isZoomable); 4861 4862 if (canPan) { 4863 $container.addClass("fancybox-can-pan"); 4864 } else if ( 4865 isZoomable && 4866 (current.opts.clickContent === "zoom" || ($.isFunction(current.opts.clickContent) && current.opts.clickContent(current) == "zoom")) 4867 ) { 4868 $container.addClass("fancybox-can-zoomIn"); 4869 } else if (current.opts.touch && (current.opts.touch.vertical || self.group.length > 1) && current.contentType !== "video") { 4870 $container.addClass("fancybox-can-swipe"); 4871 } 4872 }, 4873 4874 // Check if current slide is zoomable 4875 // ================================== 4876 4877 isZoomable: function () { 4878 var self = this, 4879 current = self.current, 4880 fitPos; 4881 4882 // Assume that slide is zoomable if: 4883 // - image is still loading 4884 // - actual size of the image is smaller than available area 4885 if (current && !self.isClosing && current.type === "image" && !current.hasError) { 4886 if (!current.isLoaded) { 4887 return true; 4888 } 4889 4890 fitPos = self.getFitPos(current); 4891 4892 if (fitPos && (current.width > fitPos.width || current.height > fitPos.height)) { 4893 return true; 4894 } 4895 } 4896 4897 return false; 4898 }, 4899 4900 // Check if current image dimensions are smaller than actual 4901 // ========================================================= 4902 4903 isScaledDown: function (nextWidth, nextHeight) { 4904 var self = this, 4905 rez = false, 4906 current = self.current, 4907 $content = current.$content; 4908 4909 if (nextWidth !== undefined && nextHeight !== undefined) { 4910 rez = nextWidth < current.width && nextHeight < current.height; 4911 } else if ($content) { 4912 rez = $.fancybox.getTranslate($content); 4913 rez = rez.width < current.width && rez.height < current.height; 4914 } 4915 4916 return rez; 4917 }, 4918 4919 // Check if image dimensions exceed parent element 4920 // =============================================== 4921 4922 canPan: function (nextWidth, nextHeight) { 4923 var self = this, 4924 current = self.current, 4925 pos = null, 4926 rez = false; 4927 4928 if (current.type === "image" && (current.isComplete || (nextWidth && nextHeight)) && !current.hasError) { 4929 rez = self.getFitPos(current); 4930 4931 if (nextWidth !== undefined && nextHeight !== undefined) { 4932 pos = { 4933 width: nextWidth, 4934 height: nextHeight 4935 }; 4936 } else if (current.isComplete) { 4937 pos = $.fancybox.getTranslate(current.$content); 4938 } 4939 4940 if (pos && rez) { 4941 rez = Math.abs(pos.width - rez.width) > 1.5 || Math.abs(pos.height - rez.height) > 1.5; 4942 } 4943 } 4944 4945 return rez; 4946 }, 4947 4948 // Load content into the slide 4949 // =========================== 4950 4951 loadSlide: function (slide) { 4952 var self = this, 4953 type, 4954 $slide, 4955 ajaxLoad; 4956 4957 if (slide.isLoading || slide.isLoaded) { 4958 return; 4959 } 4960 4961 slide.isLoading = true; 4962 4963 if (self.trigger("beforeLoad", slide) === false) { 4964 slide.isLoading = false; 4965 4966 return false; 4967 } 4968 4969 type = slide.type; 4970 $slide = slide.$slide; 4971 4972 $slide 4973 .off("refresh") 4974 .trigger("onReset") 4975 .addClass(slide.opts.slideClass); 4976 4977 // Create content depending on the type 4978 switch (type) { 4979 case "image": 4980 self.setImage(slide); 4981 4982 break; 4983 4984 case "iframe": 4985 self.setIframe(slide); 4986 4987 break; 4988 4989 case "html": 4990 self.setContent(slide, slide.src || slide.content); 4991 4992 break; 4993 4994 case "video": 4995 self.setContent( 4996 slide, 4997 slide.opts.video.tpl 4998 .replace(/\{\{src\}\}/gi, slide.src) 4999 .replace("{{format}}", slide.opts.videoFormat || slide.opts.video.format || "") 5000 .replace("{{poster}}", slide.thumb || "") 5001 ); 5002 5003 break; 5004 5005 case "inline": 5006 if ($(slide.src).length) { 5007 self.setContent(slide, $(slide.src)); 5008 } else { 5009 self.setError(slide); 5010 } 5011 5012 break; 5013 5014 case "ajax": 5015 self.showLoading(slide); 5016 5017 ajaxLoad = $.ajax( 5018 $.extend({}, slide.opts.ajax.settings, { 5019 url: slide.src, 5020 success: function (data, textStatus) { 5021 if (textStatus === "success") { 5022 self.setContent(slide, data); 5023 } 5024 }, 5025 error: function (jqXHR, textStatus) { 5026 if (jqXHR && textStatus !== "abort") { 5027 self.setError(slide); 5028 } 5029 } 5030 }) 5031 ); 5032 5033 $slide.one("onReset", function () { 5034 ajaxLoad.abort(); 5035 }); 5036 5037 break; 5038 5039 default: 5040 self.setError(slide); 5041 5042 break; 5043 } 5044 5045 return true; 5046 }, 5047 5048 // Use thumbnail image, if possible 5049 // ================================ 5050 5051 setImage: function (slide) { 5052 var self = this, 5053 ghost; 5054 5055 // Check if need to show loading icon 5056 setTimeout(function () { 5057 var $img = slide.$image; 5058 5059 if (!self.isClosing && slide.isLoading && (!$img || !$img.length || !$img[0].complete) && !slide.hasError) { 5060 self.showLoading(slide); 5061 } 5062 }, 50); 5063 5064 //Check if image has srcset 5065 self.checkSrcset(slide); 5066 5067 // This will be wrapper containing both ghost and actual image 5068 slide.$content = $('<div class="fancybox-content"></div>') 5069 .addClass("fancybox-is-hidden") 5070 .appendTo(slide.$slide.addClass("fancybox-slide--image")); 5071 5072 // If we have a thumbnail, we can display it while actual image is loading 5073 // Users will not stare at black screen and actual image will appear gradually 5074 if (slide.opts.preload !== false && slide.opts.width && slide.opts.height && slide.thumb) { 5075 slide.width = slide.opts.width; 5076 slide.height = slide.opts.height; 5077 5078 ghost = document.createElement("img"); 5079 5080 ghost.onerror = function () { 5081 $(this).remove(); 5082 5083 slide.$ghost = null; 5084 }; 5085 5086 ghost.onload = function () { 5087 self.afterLoad(slide); 5088 }; 5089 5090 slide.$ghost = $(ghost) 5091 .addClass("fancybox-image") 5092 .appendTo(slide.$content) 5093 .attr("src", slide.thumb); 5094 } 5095 5096 // Start loading actual image 5097 self.setBigImage(slide); 5098 }, 5099 5100 // Check if image has srcset and get the source 5101 // ============================================ 5102 checkSrcset: function (slide) { 5103 var srcset = slide.opts.srcset || slide.opts.image.srcset, 5104 found, 5105 temp, 5106 pxRatio, 5107 windowWidth; 5108 5109 // If we have "srcset", then we need to find first matching "src" value. 5110 // This is necessary, because when you set an src attribute, the browser will preload the image 5111 // before any javascript or even CSS is applied. 5112 if (srcset) { 5113 pxRatio = window.devicePixelRatio || 1; 5114 windowWidth = window.innerWidth * pxRatio; 5115 5116 temp = srcset.split(",").map(function (el) { 5117 var ret = {}; 5118 5119 el.trim() 5120 .split(/\s+/) 5121 .forEach(function (el, i) { 5122 var value = parseInt(el.substring(0, el.length - 1), 10); 5123 5124 if (i === 0) { 5125 return (ret.url = el); 5126 } 5127 5128 if (value) { 5129 ret.value = value; 5130 ret.postfix = el[el.length - 1]; 5131 } 5132 }); 5133 5134 return ret; 5135 }); 5136 5137 // Sort by value 5138 temp.sort(function (a, b) { 5139 return a.value - b.value; 5140 }); 5141 5142 // Ok, now we have an array of all srcset values 5143 for (var j = 0; j < temp.length; j++) { 5144 var el = temp[j]; 5145 5146 if ((el.postfix === "w" && el.value >= windowWidth) || (el.postfix === "x" && el.value >= pxRatio)) { 5147 found = el; 5148 break; 5149 } 5150 } 5151 5152 // If not found, take the last one 5153 if (!found && temp.length) { 5154 found = temp[temp.length - 1]; 5155 } 5156 5157 if (found) { 5158 slide.src = found.url; 5159 5160 // If we have default width/height values, we can calculate height for matching source 5161 if (slide.width && slide.height && found.postfix == "w") { 5162 slide.height = (slide.width / slide.height) * found.value; 5163 slide.width = found.value; 5164 } 5165 5166 slide.opts.srcset = srcset; 5167 } 5168 } 5169 }, 5170 5171 // Create full-size image 5172 // ====================== 5173 5174 setBigImage: function (slide) { 5175 var self = this, 5176 img = document.createElement("img"), 5177 $img = $(img); 5178 5179 slide.$image = $img 5180 .one("error", function () { 5181 self.setError(slide); 5182 }) 5183 .one("load", function () { 5184 var sizes; 5185 5186 if (!slide.$ghost) { 5187 self.resolveImageSlideSize(slide, this.naturalWidth, this.naturalHeight); 5188 5189 self.afterLoad(slide); 5190 } 5191 5192 if (self.isClosing) { 5193 return; 5194 } 5195 5196 if (slide.opts.srcset) { 5197 sizes = slide.opts.sizes; 5198 5199 if (!sizes || sizes === "auto") { 5200 sizes = 5201 (slide.width / slide.height > 1 && $W.width() / $W.height() > 1 ? "100" : Math.round((slide.width / slide.height) * 100)) + 5202 "vw"; 5203 } 5204 5205 $img.attr("sizes", sizes).attr("srcset", slide.opts.srcset); 5206 } 5207 5208 // Hide temporary image after some delay 5209 if (slide.$ghost) { 5210 setTimeout(function () { 5211 if (slide.$ghost && !self.isClosing) { 5212 slide.$ghost.hide(); 5213 } 5214 }, Math.min(300, Math.max(1000, slide.height / 1600))); 5215 } 5216 5217 self.hideLoading(slide); 5218 }) 5219 .addClass("fancybox-image") 5220 .attr("src", slide.src) 5221 .appendTo(slide.$content); 5222 5223 if ((img.complete || img.readyState == "complete") && $img.naturalWidth && $img.naturalHeight) { 5224 $img.trigger("load"); 5225 } else if (img.error) { 5226 $img.trigger("error"); 5227 } 5228 }, 5229 5230 // Computes the slide size from image size and maxWidth/maxHeight 5231 // ============================================================== 5232 5233 resolveImageSlideSize: function (slide, imgWidth, imgHeight) { 5234 var maxWidth = parseInt(slide.opts.width, 10), 5235 maxHeight = parseInt(slide.opts.height, 10); 5236 5237 // Sets the default values from the image 5238 slide.width = imgWidth; 5239 slide.height = imgHeight; 5240 5241 if (maxWidth > 0) { 5242 slide.width = maxWidth; 5243 slide.height = Math.floor((maxWidth * imgHeight) / imgWidth); 5244 } 5245 5246 if (maxHeight > 0) { 5247 slide.width = Math.floor((maxHeight * imgWidth) / imgHeight); 5248 slide.height = maxHeight; 5249 } 5250 }, 5251 5252 // Create iframe wrapper, iframe and bindings 5253 // ========================================== 5254 5255 setIframe: function (slide) { 5256 var self = this, 5257 opts = slide.opts.iframe, 5258 $slide = slide.$slide, 5259 $iframe; 5260 5261 slide.$content = $('<div class="fancybox-content' + (opts.preload ? " fancybox-is-hidden" : "") + '"></div>') 5262 .css(opts.css) 5263 .appendTo($slide); 5264 5265 $slide.addClass("fancybox-slide--" + slide.contentType); 5266 5267 slide.$iframe = $iframe = $(opts.tpl.replace(/\{rnd\}/g, new Date().getTime())) 5268 .attr(opts.attr) 5269 .appendTo(slide.$content); 5270 5271 if (opts.preload) { 5272 self.showLoading(slide); 5273 5274 // Unfortunately, it is not always possible to determine if iframe is successfully loaded 5275 // (due to browser security policy) 5276 5277 $iframe.on("load.fb error.fb", function (e) { 5278 this.isReady = 1; 5279 5280 slide.$slide.trigger("refresh"); 5281 5282 self.afterLoad(slide); 5283 }); 5284 5285 // Recalculate iframe content size 5286 // =============================== 5287 5288 $slide.on("refresh.fb", function () { 5289 var $content = slide.$content, 5290 frameWidth = opts.css.width, 5291 frameHeight = opts.css.height, 5292 $contents, 5293 $body; 5294 5295 if ($iframe[0].isReady !== 1) { 5296 return; 5297 } 5298 5299 try { 5300 $contents = $iframe.contents(); 5301 $body = $contents.find("body"); 5302 } catch (ignore) {} 5303 5304 // Calculate content dimensions, if it is accessible 5305 if ($body && $body.length && $body.children().length) { 5306 // Avoid scrolling to top (if multiple instances) 5307 $slide.css("overflow", "visible"); 5308 5309 $content.css({ 5310 width: "100%", 5311 "max-width": "100%", 5312 height: "9999px" 5313 }); 5314 5315 if (frameWidth === undefined) { 5316 frameWidth = Math.ceil(Math.max($body[0].clientWidth, $body.outerWidth(true))); 5317 } 5318 5319 $content.css("width", frameWidth ? frameWidth : "").css("max-width", ""); 5320 5321 if (frameHeight === undefined) { 5322 frameHeight = Math.ceil(Math.max($body[0].clientHeight, $body.outerHeight(true))); 5323 } 5324 5325 $content.css("height", frameHeight ? frameHeight : ""); 5326 5327 $slide.css("overflow", "auto"); 5328 } 5329 5330 $content.removeClass("fancybox-is-hidden"); 5331 }); 5332 } else { 5333 self.afterLoad(slide); 5334 } 5335 5336 $iframe.attr("src", slide.src); 5337 5338 // Remove iframe if closing or changing gallery item 5339 $slide.one("onReset", function () { 5340 // This helps IE not to throw errors when closing 5341 try { 5342 $(this) 5343 .find("iframe") 5344 .hide() 5345 .unbind() 5346 .attr("src", "//about:blank"); 5347 } catch (ignore) {} 5348 5349 $(this) 5350 .off("refresh.fb") 5351 .empty(); 5352 5353 slide.isLoaded = false; 5354 slide.isRevealed = false; 5355 }); 5356 }, 5357 5358 // Wrap and append content to the slide 5359 // ====================================== 5360 5361 setContent: function (slide, content) { 5362 var self = this; 5363 5364 if (self.isClosing) { 5365 return; 5366 } 5367 5368 self.hideLoading(slide); 5369 5370 if (slide.$content) { 5371 $.fancybox.stop(slide.$content); 5372 } 5373 5374 slide.$slide.empty(); 5375 5376 // If content is a jQuery object, then it will be moved to the slide. 5377 // The placeholder is created so we will know where to put it back. 5378 if (isQuery(content) && content.parent().length) { 5379 // Make sure content is not already moved to fancyBox 5380 if (content.hasClass("fancybox-content") || content.parent().hasClass("fancybox-content")) { 5381 content.parents(".fancybox-slide").trigger("onReset"); 5382 } 5383 5384 // Create temporary element marking original place of the content 5385 slide.$placeholder = $("<div>") 5386 .hide() 5387 .insertAfter(content); 5388 5389 // Make sure content is visible 5390 content.css("display", "inline-block"); 5391 } else if (!slide.hasError) { 5392 // If content is just a plain text, try to convert it to html 5393 if ($.type(content) === "string") { 5394 content = $("<div>") 5395 .append($.trim(content)) 5396 .contents(); 5397 } 5398 5399 // If "filter" option is provided, then filter content 5400 if (slide.opts.filter) { 5401 content = $("<div>") 5402 .html(content) 5403 .find(slide.opts.filter); 5404 } 5405 } 5406 5407 slide.$slide.one("onReset", function () { 5408 // Pause all html5 video/audio 5409 $(this) 5410 .find("video,audio") 5411 .trigger("pause"); 5412 5413 // Put content back 5414 if (slide.$placeholder) { 5415 slide.$placeholder.after(content.removeClass("fancybox-content").hide()).remove(); 5416 5417 slide.$placeholder = null; 5418 } 5419 5420 // Remove custom close button 5421 if (slide.$smallBtn) { 5422 slide.$smallBtn.remove(); 5423 5424 slide.$smallBtn = null; 5425 } 5426 5427 // Remove content and mark slide as not loaded 5428 if (!slide.hasError) { 5429 $(this).empty(); 5430 5431 slide.isLoaded = false; 5432 slide.isRevealed = false; 5433 } 5434 }); 5435 5436 $(content).appendTo(slide.$slide); 5437 5438 if ($(content).is("video,audio")) { 5439 $(content).addClass("fancybox-video"); 5440 5441 $(content).wrap("<div></div>"); 5442 5443 slide.contentType = "video"; 5444 5445 slide.opts.width = slide.opts.width || $(content).attr("width"); 5446 slide.opts.height = slide.opts.height || $(content).attr("height"); 5447 } 5448 5449 slide.$content = slide.$slide 5450 .children() 5451 .filter("div,form,main,video,audio,article,.fancybox-content") 5452 .first(); 5453 5454 slide.$content.siblings().hide(); 5455 5456 // Re-check if there is a valid content 5457 // (in some cases, ajax response can contain various elements or plain text) 5458 if (!slide.$content.length) { 5459 slide.$content = slide.$slide 5460 .wrapInner("<div></div>") 5461 .children() 5462 .first(); 5463 } 5464 5465 slide.$content.addClass("fancybox-content"); 5466 5467 slide.$slide.addClass("fancybox-slide--" + slide.contentType); 5468 5469 self.afterLoad(slide); 5470 }, 5471 5472 // Display error message 5473 // ===================== 5474 5475 setError: function (slide) { 5476 slide.hasError = true; 5477 5478 slide.$slide 5479 .trigger("onReset") 5480 .removeClass("fancybox-slide--" + slide.contentType) 5481 .addClass("fancybox-slide--error"); 5482 5483 slide.contentType = "html"; 5484 5485 this.setContent(slide, this.translate(slide, slide.opts.errorTpl)); 5486 5487 if (slide.pos === this.currPos) { 5488 this.isAnimating = false; 5489 } 5490 }, 5491 5492 // Show loading icon inside the slide 5493 // ================================== 5494 5495 showLoading: function (slide) { 5496 var self = this; 5497 5498 slide = slide || self.current; 5499 5500 if (slide && !slide.$spinner) { 5501 slide.$spinner = $(self.translate(self, self.opts.spinnerTpl)) 5502 .appendTo(slide.$slide) 5503 .hide() 5504 .fadeIn("fast"); 5505 } 5506 }, 5507 5508 // Remove loading icon from the slide 5509 // ================================== 5510 5511 hideLoading: function (slide) { 5512 var self = this; 5513 5514 slide = slide || self.current; 5515 5516 if (slide && slide.$spinner) { 5517 slide.$spinner.stop().remove(); 5518 5519 delete slide.$spinner; 5520 } 5521 }, 5522 5523 // Adjustments after slide content has been loaded 5524 // =============================================== 5525 5526 afterLoad: function (slide) { 5527 var self = this; 5528 5529 if (self.isClosing) { 5530 return; 5531 } 5532 5533 slide.isLoading = false; 5534 slide.isLoaded = true; 5535 5536 self.trigger("afterLoad", slide); 5537 5538 self.hideLoading(slide); 5539 5540 // Add small close button 5541 if (slide.opts.smallBtn && (!slide.$smallBtn || !slide.$smallBtn.length)) { 5542 slide.$smallBtn = $(self.translate(slide, slide.opts.btnTpl.smallBtn)).appendTo(slide.$content); 5543 } 5544 5545 // Disable right click 5546 if (slide.opts.protect && slide.$content && !slide.hasError) { 5547 slide.$content.on("contextmenu.fb", function (e) { 5548 if (e.button == 2) { 5549 e.preventDefault(); 5550 } 5551 5552 return true; 5553 }); 5554 5555 // Add fake element on top of the image 5556 // This makes a bit harder for user to select image 5557 if (slide.type === "image") { 5558 $('<div class="fancybox-spaceball"></div>').appendTo(slide.$content); 5559 } 5560 } 5561 5562 self.adjustCaption(slide); 5563 5564 self.adjustLayout(slide); 5565 5566 if (slide.pos === self.currPos) { 5567 self.updateCursor(); 5568 } 5569 5570 self.revealContent(slide); 5571 }, 5572 5573 // Prevent caption overlap, 5574 // fix css inconsistency across browsers 5575 // ===================================== 5576 5577 adjustCaption: function (slide) { 5578 var self = this, 5579 current = slide || self.current, 5580 caption = current.opts.caption, 5581 preventOverlap = current.opts.preventCaptionOverlap, 5582 $caption = self.$refs.caption, 5583 $clone, 5584 captionH = false; 5585 5586 $caption.toggleClass("fancybox-caption--separate", preventOverlap); 5587 5588 if (preventOverlap && caption && caption.length) { 5589 if (current.pos !== self.currPos) { 5590 $clone = $caption.clone().appendTo($caption.parent()); 5591 5592 $clone 5593 .children() 5594 .eq(0) 5595 .empty() 5596 .html(caption); 5597 5598 captionH = $clone.outerHeight(true); 5599 5600 $clone.empty().remove(); 5601 } else if (self.$caption) { 5602 captionH = self.$caption.outerHeight(true); 5603 } 5604 5605 current.$slide.css("padding-bottom", captionH || ""); 5606 } 5607 }, 5608 5609 // Simple hack to fix inconsistency across browsers, described here (affects Edge, too): 5610 // https://bugzilla.mozilla.org/show_bug.cgi?id=748518 5611 // ==================================================================================== 5612 5613 adjustLayout: function (slide) { 5614 var self = this, 5615 current = slide || self.current, 5616 scrollHeight, 5617 marginBottom, 5618 inlinePadding, 5619 actualPadding; 5620 5621 if (current.isLoaded && current.opts.disableLayoutFix !== true) { 5622 current.$content.css("margin-bottom", ""); 5623 5624 // If we would always set margin-bottom for the content, 5625 // then it would potentially break vertical align 5626 if (current.$content.outerHeight() > current.$slide.height() + 0.5) { 5627 inlinePadding = current.$slide[0].style["padding-bottom"]; 5628 actualPadding = current.$slide.css("padding-bottom"); 5629 5630 if (parseFloat(actualPadding) > 0) { 5631 scrollHeight = current.$slide[0].scrollHeight; 5632 5633 current.$slide.css("padding-bottom", 0); 5634 5635 if (Math.abs(scrollHeight - current.$slide[0].scrollHeight) < 1) { 5636 marginBottom = actualPadding; 5637 } 5638 5639 current.$slide.css("padding-bottom", inlinePadding); 5640 } 5641 } 5642 5643 current.$content.css("margin-bottom", marginBottom); 5644 } 5645 }, 5646 5647 // Make content visible 5648 // This method is called right after content has been loaded or 5649 // user navigates gallery and transition should start 5650 // ============================================================ 5651 5652 revealContent: function (slide) { 5653 var self = this, 5654 $slide = slide.$slide, 5655 end = false, 5656 start = false, 5657 isMoved = self.isMoved(slide), 5658 isRevealed = slide.isRevealed, 5659 effect, 5660 effectClassName, 5661 duration, 5662 opacity; 5663 5664 slide.isRevealed = true; 5665 5666 effect = slide.opts[self.firstRun ? "animationEffect" : "transitionEffect"]; 5667 duration = slide.opts[self.firstRun ? "animationDuration" : "transitionDuration"]; 5668 5669 duration = parseInt(slide.forcedDuration === undefined ? duration : slide.forcedDuration, 10); 5670 5671 if (isMoved || slide.pos !== self.currPos || !duration) { 5672 effect = false; 5673 } 5674 5675 // Check if can zoom 5676 if (effect === "zoom") { 5677 if (slide.pos === self.currPos && duration && slide.type === "image" && !slide.hasError && (start = self.getThumbPos(slide))) { 5678 end = self.getFitPos(slide); 5679 } else { 5680 effect = "fade"; 5681 } 5682 } 5683 5684 // Zoom animation 5685 // ============== 5686 if (effect === "zoom") { 5687 self.isAnimating = true; 5688 5689 end.scaleX = end.width / start.width; 5690 end.scaleY = end.height / start.height; 5691 5692 // Check if we need to animate opacity 5693 opacity = slide.opts.zoomOpacity; 5694 5695 if (opacity == "auto") { 5696 opacity = Math.abs(slide.width / slide.height - start.width / start.height) > 0.1; 5697 } 5698 5699 if (opacity) { 5700 start.opacity = 0.1; 5701 end.opacity = 1; 5702 } 5703 5704 // Draw image at start position 5705 $.fancybox.setTranslate(slide.$content.removeClass("fancybox-is-hidden"), start); 5706 5707 forceRedraw(slide.$content); 5708 5709 // Start animation 5710 $.fancybox.animate(slide.$content, end, duration, function () { 5711 self.isAnimating = false; 5712 5713 self.complete(); 5714 }); 5715 5716 return; 5717 } 5718 5719 self.updateSlide(slide); 5720 5721 // Simply show content if no effect 5722 // ================================ 5723 if (!effect) { 5724 slide.$content.removeClass("fancybox-is-hidden"); 5725 5726 if (!isRevealed && isMoved && slide.type === "image" && !slide.hasError) { 5727 slide.$content.hide().fadeIn("fast"); 5728 } 5729 5730 if (slide.pos === self.currPos) { 5731 self.complete(); 5732 } 5733 5734 return; 5735 } 5736 5737 // Prepare for CSS transiton 5738 // ========================= 5739 $.fancybox.stop($slide); 5740 5741 //effectClassName = "fancybox-animated fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-fx-" + effect; 5742 effectClassName = "fancybox-slide--" + (slide.pos >= self.prevPos ? "next" : "previous") + " fancybox-animated fancybox-fx-" + effect; 5743 5744 $slide.addClass(effectClassName).removeClass("fancybox-slide--current"); //.addClass(effectClassName); 5745 5746 slide.$content.removeClass("fancybox-is-hidden"); 5747 5748 // Force reflow 5749 forceRedraw($slide); 5750 5751 if (slide.type !== "image") { 5752 slide.$content.hide().show(0); 5753 } 5754 5755 $.fancybox.animate( 5756 $slide, 5757 "fancybox-slide--current", 5758 duration, 5759 function () { 5760 $slide.removeClass(effectClassName).css({ 5761 transform: "", 5762 opacity: "" 5763 }); 5764 5765 if (slide.pos === self.currPos) { 5766 self.complete(); 5767 } 5768 }, 5769 true 5770 ); 5771 }, 5772 5773 // Check if we can and have to zoom from thumbnail 5774 //================================================ 5775 5776 getThumbPos: function (slide) { 5777 var rez = false, 5778 $thumb = slide.$thumb, 5779 thumbPos, 5780 btw, 5781 brw, 5782 bbw, 5783 blw; 5784 5785 if (!$thumb || !inViewport($thumb[0])) { 5786 return false; 5787 } 5788 5789 thumbPos = $.fancybox.getTranslate($thumb); 5790 5791 btw = parseFloat($thumb.css("border-top-width") || 0); 5792 brw = parseFloat($thumb.css("border-right-width") || 0); 5793 bbw = parseFloat($thumb.css("border-bottom-width") || 0); 5794 blw = parseFloat($thumb.css("border-left-width") || 0); 5795 5796 rez = { 5797 top: thumbPos.top + btw, 5798 left: thumbPos.left + blw, 5799 width: thumbPos.width - brw - blw, 5800 height: thumbPos.height - btw - bbw, 5801 scaleX: 1, 5802 scaleY: 1 5803 }; 5804 5805 return thumbPos.width > 0 && thumbPos.height > 0 ? rez : false; 5806 }, 5807 5808 // Final adjustments after current gallery item is moved to position 5809 // and it`s content is loaded 5810 // ================================================================== 5811 5812 complete: function () { 5813 var self = this, 5814 current = self.current, 5815 slides = {}, 5816 $el; 5817 5818 if (self.isMoved() || !current.isLoaded) { 5819 return; 5820 } 5821 5822 if (!current.isComplete) { 5823 current.isComplete = true; 5824 5825 current.$slide.siblings().trigger("onReset"); 5826 5827 self.preload("inline"); 5828 5829 // Trigger any CSS transiton inside the slide 5830 forceRedraw(current.$slide); 5831 5832 current.$slide.addClass("fancybox-slide--complete"); 5833 5834 // Remove unnecessary slides 5835 $.each(self.slides, function (key, slide) { 5836 if (slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1) { 5837 slides[slide.pos] = slide; 5838 } else if (slide) { 5839 $.fancybox.stop(slide.$slide); 5840 5841 slide.$slide.off().remove(); 5842 } 5843 }); 5844 5845 self.slides = slides; 5846 } 5847 5848 self.isAnimating = false; 5849 5850 self.updateCursor(); 5851 5852 self.trigger("afterShow"); 5853 5854 // Autoplay first html5 video/audio 5855 if (!!current.opts.video.autoStart) { 5856 current.$slide 5857 .find("video,audio") 5858 .filter(":visible:first") 5859 .trigger("play") 5860 .one("ended", function () { 5861 if (Document.exitFullscreen) { 5862 Document.exitFullscreen(); 5863 } else if (this.webkitExitFullscreen) { 5864 this.webkitExitFullscreen(); 5865 } 5866 5867 self.next(); 5868 }); 5869 } 5870 5871 // Try to focus on the first focusable element 5872 if (current.opts.autoFocus && current.contentType === "html") { 5873 // Look for the first input with autofocus attribute 5874 $el = current.$content.find("input[autofocus]:enabled:visible:first"); 5875 5876 if ($el.length) { 5877 $el.trigger("focus"); 5878 } else { 5879 self.focus(null, true); 5880 } 5881 } 5882 5883 // Avoid jumping 5884 current.$slide.scrollTop(0).scrollLeft(0); 5885 }, 5886 5887 // Preload next and previous slides 5888 // ================================ 5889 5890 preload: function (type) { 5891 var self = this, 5892 prev, 5893 next; 5894 5895 if (self.group.length < 2) { 5896 return; 5897 } 5898 5899 next = self.slides[self.currPos + 1]; 5900 prev = self.slides[self.currPos - 1]; 5901 5902 if (prev && prev.type === type) { 5903 self.loadSlide(prev); 5904 } 5905 5906 if (next && next.type === type) { 5907 self.loadSlide(next); 5908 } 5909 }, 5910 5911 // Try to find and focus on the first focusable element 5912 // ==================================================== 5913 5914 focus: function (e, firstRun) { 5915 var self = this, 5916 focusableStr = [ 5917 "a[href]", 5918 "area[href]", 5919 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])', 5920 "select:not([disabled]):not([aria-hidden])", 5921 "textarea:not([disabled]):not([aria-hidden])", 5922 "button:not([disabled]):not([aria-hidden])", 5923 "iframe", 5924 "object", 5925 "embed", 5926 "video", 5927 "audio", 5928 "[contenteditable]", 5929 '[tabindex]:not([tabindex^="-"])' 5930 ].join(","), 5931 focusableItems, 5932 focusedItemIndex; 5933 5934 if (self.isClosing) { 5935 return; 5936 } 5937 5938 if (e || !self.current || !self.current.isComplete) { 5939 // Focus on any element inside fancybox 5940 focusableItems = self.$refs.container.find("*:visible"); 5941 } else { 5942 // Focus inside current slide 5943 focusableItems = self.current.$slide.find("*:visible" + (firstRun ? ":not(.fancybox-close-small)" : "")); 5944 } 5945 5946 focusableItems = focusableItems.filter(focusableStr).filter(function () { 5947 return $(this).css("visibility") !== "hidden" && !$(this).hasClass("disabled"); 5948 }); 5949 5950 if (focusableItems.length) { 5951 focusedItemIndex = focusableItems.index(document.activeElement); 5952 5953 if (e && e.shiftKey) { 5954 // Back tab 5955 if (focusedItemIndex < 0 || focusedItemIndex == 0) { 5956 e.preventDefault(); 5957 5958 focusableItems.eq(focusableItems.length - 1).trigger("focus"); 5959 } 5960 } else { 5961 // Outside or Forward tab 5962 if (focusedItemIndex < 0 || focusedItemIndex == focusableItems.length - 1) { 5963 if (e) { 5964 e.preventDefault(); 5965 } 5966 5967 focusableItems.eq(0).trigger("focus"); 5968 } 5969 } 5970 } else { 5971 self.$refs.container.trigger("focus"); 5972 } 5973 }, 5974 5975 // Activates current instance - brings container to the front and enables keyboard, 5976 // notifies other instances about deactivating 5977 // ================================================================================= 5978 5979 activate: function () { 5980 var self = this; 5981 5982 // Deactivate all instances 5983 $(".fancybox-container").each(function () { 5984 var instance = $(this).data("FancyBox"); 5985 5986 // Skip self and closing instances 5987 if (instance && instance.id !== self.id && !instance.isClosing) { 5988 instance.trigger("onDeactivate"); 5989 5990 instance.removeEvents(); 5991 5992 instance.isVisible = false; 5993 } 5994 }); 5995 5996 self.isVisible = true; 5997 5998 if (self.current || self.isIdle) { 5999 self.update(); 6000 6001 self.updateControls(); 6002 } 6003 6004 self.trigger("onActivate"); 6005 6006 self.addEvents(); 6007 }, 6008 6009 // Start closing procedure 6010 // This will start "zoom-out" animation if needed and clean everything up afterwards 6011 // ================================================================================= 6012 6013 close: function (e, d) { 6014 var self = this, 6015 current = self.current, 6016 effect, 6017 duration, 6018 $content, 6019 domRect, 6020 opacity, 6021 start, 6022 end; 6023 6024 var done = function () { 6025 self.cleanUp(e); 6026 }; 6027 6028 if (self.isClosing) { 6029 return false; 6030 } 6031 6032 self.isClosing = true; 6033 6034 // If beforeClose callback prevents closing, make sure content is centered 6035 if (self.trigger("beforeClose", e) === false) { 6036 self.isClosing = false; 6037 6038 requestAFrame(function () { 6039 self.update(); 6040 }); 6041 6042 return false; 6043 } 6044 6045 // Remove all events 6046 // If there are multiple instances, they will be set again by "activate" method 6047 self.removeEvents(); 6048 6049 $content = current.$content; 6050 effect = current.opts.animationEffect; 6051 duration = $.isNumeric(d) ? d : effect ? current.opts.animationDuration : 0; 6052 6053 current.$slide.removeClass("fancybox-slide--complete fancybox-slide--next fancybox-slide--previous fancybox-animated"); 6054 6055 if (e !== true) { 6056 $.fancybox.stop(current.$slide); 6057 } else { 6058 effect = false; 6059 } 6060 6061 // Remove other slides 6062 current.$slide 6063 .siblings() 6064 .trigger("onReset") 6065 .remove(); 6066 6067 // Trigger animations 6068 if (duration) { 6069 self.$refs.container 6070 .removeClass("fancybox-is-open") 6071 .addClass("fancybox-is-closing") 6072 .css("transition-duration", duration + "ms"); 6073 } 6074 6075 // Clean up 6076 self.hideLoading(current); 6077 6078 self.hideControls(true); 6079 6080 self.updateCursor(); 6081 6082 // Check if possible to zoom-out 6083 if ( 6084 effect === "zoom" && 6085 !($content && duration && current.type === "image" && !self.isMoved() && !current.hasError && (end = self.getThumbPos(current))) 6086 ) { 6087 effect = "fade"; 6088 } 6089 6090 if (effect === "zoom") { 6091 $.fancybox.stop($content); 6092 6093 domRect = $.fancybox.getTranslate($content); 6094 6095 start = { 6096 top: domRect.top, 6097 left: domRect.left, 6098 scaleX: domRect.width / end.width, 6099 scaleY: domRect.height / end.height, 6100 width: end.width, 6101 height: end.height 6102 }; 6103 6104 // Check if we need to animate opacity 6105 opacity = current.opts.zoomOpacity; 6106 6107 if (opacity == "auto") { 6108 opacity = Math.abs(current.width / current.height - end.width / end.height) > 0.1; 6109 } 6110 6111 if (opacity) { 6112 end.opacity = 0; 6113 } 6114 6115 $.fancybox.setTranslate($content, start); 6116 6117 forceRedraw($content); 6118 6119 $.fancybox.animate($content, end, duration, done); 6120 6121 return true; 6122 } 6123 6124 if (effect && duration) { 6125 $.fancybox.animate( 6126 current.$slide.addClass("fancybox-slide--previous").removeClass("fancybox-slide--current"), 6127 "fancybox-animated fancybox-fx-" + effect, 6128 duration, 6129 done 6130 ); 6131 } else { 6132 // If skip animation 6133 if (e === true) { 6134 setTimeout(done, duration); 6135 } else { 6136 done(); 6137 } 6138 } 6139 6140 return true; 6141 }, 6142 6143 // Final adjustments after removing the instance 6144 // ============================================= 6145 6146 cleanUp: function (e) { 6147 var self = this, 6148 instance, 6149 $focus = self.current.opts.$orig, 6150 x, 6151 y; 6152 6153 self.current.$slide.trigger("onReset"); 6154 6155 self.$refs.container.empty().remove(); 6156 6157 self.trigger("afterClose", e); 6158 6159 // Place back focus 6160 if (!!self.current.opts.backFocus) { 6161 if (!$focus || !$focus.length || !$focus.is(":visible")) { 6162 $focus = self.$trigger; 6163 } 6164 6165 if ($focus && $focus.length) { 6166 x = window.scrollX; 6167 y = window.scrollY; 6168 6169 $focus.trigger("focus"); 6170 6171 $("html, body") 6172 .scrollTop(y) 6173 .scrollLeft(x); 6174 } 6175 } 6176 6177 self.current = null; 6178 6179 // Check if there are other instances 6180 instance = $.fancybox.getInstance(); 6181 6182 if (instance) { 6183 instance.activate(); 6184 } else { 6185 $("body").removeClass("fancybox-active compensate-for-scrollbar"); 6186 6187 $("#fancybox-style-noscroll").remove(); 6188 } 6189 }, 6190 6191 // Call callback and trigger an event 6192 // ================================== 6193 6194 trigger: function (name, slide) { 6195 var args = Array.prototype.slice.call(arguments, 1), 6196 self = this, 6197 obj = slide && slide.opts ? slide : self.current, 6198 rez; 6199 6200 if (obj) { 6201 args.unshift(obj); 6202 } else { 6203 obj = self; 6204 } 6205 6206 args.unshift(self); 6207 6208 if ($.isFunction(obj.opts[name])) { 6209 rez = obj.opts[name].apply(obj, args); 6210 } 6211 6212 if (rez === false) { 6213 return rez; 6214 } 6215 6216 if (name === "afterClose" || !self.$refs) { 6217 $D.trigger(name + ".fb", args); 6218 } else { 6219 self.$refs.container.trigger(name + ".fb", args); 6220 } 6221 }, 6222 6223 // Update infobar values, navigation button states and reveal caption 6224 // ================================================================== 6225 6226 updateControls: function () { 6227 var self = this, 6228 current = self.current, 6229 index = current.index, 6230 $container = self.$refs.container, 6231 $caption = self.$refs.caption, 6232 caption = current.opts.caption; 6233 6234 // Recalculate content dimensions 6235 current.$slide.trigger("refresh"); 6236 6237 // Set caption 6238 if (caption && caption.length) { 6239 self.$caption = $caption; 6240 6241 $caption 6242 .children() 6243 .eq(0) 6244 .html(caption); 6245 } else { 6246 self.$caption = null; 6247 } 6248 6249 if (!self.hasHiddenControls && !self.isIdle) { 6250 self.showControls(); 6251 } 6252 6253 // Update info and navigation elements 6254 $container.find("[data-fancybox-count]").html(self.group.length); 6255 $container.find("[data-fancybox-index]").html(index + 1); 6256 6257 $container.find("[data-fancybox-prev]").prop("disabled", !current.opts.loop && index <= 0); 6258 $container.find("[data-fancybox-next]").prop("disabled", !current.opts.loop && index >= self.group.length - 1); 6259 6260 if (current.type === "image") { 6261 // Re-enable buttons; update download button source 6262 $container 6263 .find("[data-fancybox-zoom]") 6264 .show() 6265 .end() 6266 .find("[data-fancybox-download]") 6267 .attr("href", current.opts.image.src || current.src) 6268 .show(); 6269 } else if (current.opts.toolbar) { 6270 $container.find("[data-fancybox-download],[data-fancybox-zoom]").hide(); 6271 } 6272 6273 // Make sure focus is not on disabled button/element 6274 if ($(document.activeElement).is(":hidden,[disabled]")) { 6275 self.$refs.container.trigger("focus"); 6276 } 6277 }, 6278 6279 // Hide toolbar and caption 6280 // ======================== 6281 6282 hideControls: function (andCaption) { 6283 var self = this, 6284 arr = ["infobar", "toolbar", "nav"]; 6285 6286 if (andCaption || !self.current.opts.preventCaptionOverlap) { 6287 arr.push("caption"); 6288 } 6289 6290 this.$refs.container.removeClass( 6291 arr 6292 .map(function (i) { 6293 return "fancybox-show-" + i; 6294 }) 6295 .join(" ") 6296 ); 6297 6298 this.hasHiddenControls = true; 6299 }, 6300 6301 showControls: function () { 6302 var self = this, 6303 opts = self.current ? self.current.opts : self.opts, 6304 $container = self.$refs.container; 6305 6306 self.hasHiddenControls = false; 6307 self.idleSecondsCounter = 0; 6308 6309 $container 6310 .toggleClass("fancybox-show-toolbar", !!(opts.toolbar && opts.buttons)) 6311 .toggleClass("fancybox-show-infobar", !!(opts.infobar && self.group.length > 1)) 6312 .toggleClass("fancybox-show-caption", !!self.$caption) 6313 .toggleClass("fancybox-show-nav", !!(opts.arrows && self.group.length > 1)) 6314 .toggleClass("fancybox-is-modal", !!opts.modal); 6315 }, 6316 6317 // Toggle toolbar and caption 6318 // ========================== 6319 6320 toggleControls: function () { 6321 if (this.hasHiddenControls) { 6322 this.showControls(); 6323 } else { 6324 this.hideControls(); 6325 } 6326 } 6327 }); 6328 6329 $.fancybox = { 6330 version: "3.5.7", 6331 defaults: defaults, 6332 6333 // Get current instance and execute a command. 6334 // 6335 // Examples of usage: 6336 // 6337 // $instance = $.fancybox.getInstance(); 6338 // $.fancybox.getInstance().jumpTo( 1 ); 6339 // $.fancybox.getInstance( 'jumpTo', 1 ); 6340 // $.fancybox.getInstance( function() { 6341 // console.info( this.currIndex ); 6342 // }); 6343 // ====================================================== 6344 6345 getInstance: function (command) { 6346 var instance = $('.fancybox-container:not(".fancybox-is-closing"):last').data("FancyBox"), 6347 args = Array.prototype.slice.call(arguments, 1); 6348 6349 if (instance instanceof FancyBox) { 6350 if ($.type(command) === "string") { 6351 instance[command].apply(instance, args); 6352 } else if ($.type(command) === "function") { 6353 command.apply(instance, args); 6354 } 6355 6356 return instance; 6357 } 6358 6359 return false; 6360 }, 6361 6362 // Create new instance 6363 // =================== 6364 6365 open: function (items, opts, index) { 6366 return new FancyBox(items, opts, index); 6367 }, 6368 6369 // Close current or all instances 6370 // ============================== 6371 6372 close: function (all) { 6373 var instance = this.getInstance(); 6374 6375 if (instance) { 6376 instance.close(); 6377 6378 // Try to find and close next instance 6379 if (all === true) { 6380 this.close(all); 6381 } 6382 } 6383 }, 6384 6385 // Close all instances and unbind all events 6386 // ========================================= 6387 6388 destroy: function () { 6389 this.close(true); 6390 6391 $D.add("body").off("click.fb-start", "**"); 6392 }, 6393 6394 // Try to detect mobile devices 6395 // ============================ 6396 6397 isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent), 6398 6399 // Detect if 'translate3d' support is available 6400 // ============================================ 6401 6402 use3d: (function () { 6403 var div = document.createElement("div"); 6404 6405 return ( 6406 window.getComputedStyle && 6407 window.getComputedStyle(div) && 6408 window.getComputedStyle(div).getPropertyValue("transform") && 6409 !(document.documentMode && document.documentMode < 11) 6410 ); 6411 })(), 6412 6413 // Helper function to get current visual state of an element 6414 // returns array[ top, left, horizontal-scale, vertical-scale, opacity ] 6415 // ===================================================================== 6416 6417 getTranslate: function ($el) { 6418 var domRect; 6419 6420 if (!$el || !$el.length) { 6421 return false; 6422 } 6423 6424 domRect = $el[0].getBoundingClientRect(); 6425 6426 return { 6427 top: domRect.top || 0, 6428 left: domRect.left || 0, 6429 width: domRect.width, 6430 height: domRect.height, 6431 opacity: parseFloat($el.css("opacity")) 6432 }; 6433 }, 6434 6435 // Shortcut for setting "translate3d" properties for element 6436 // Can set be used to set opacity, too 6437 // ======================================================== 6438 6439 setTranslate: function ($el, props) { 6440 var str = "", 6441 css = {}; 6442 6443 if (!$el || !props) { 6444 return; 6445 } 6446 6447 if (props.left !== undefined || props.top !== undefined) { 6448 str = 6449 (props.left === undefined ? $el.position().left : props.left) + 6450 "px, " + 6451 (props.top === undefined ? $el.position().top : props.top) + 6452 "px"; 6453 6454 if (this.use3d) { 6455 str = "translate3d(" + str + ", 0px)"; 6456 } else { 6457 str = "translate(" + str + ")"; 6458 } 6459 } 6460 6461 if (props.scaleX !== undefined && props.scaleY !== undefined) { 6462 str += " scale(" + props.scaleX + ", " + props.scaleY + ")"; 6463 } else if (props.scaleX !== undefined) { 6464 str += " scaleX(" + props.scaleX + ")"; 6465 } 6466 6467 if (str.length) { 6468 css.transform = str; 6469 } 6470 6471 if (props.opacity !== undefined) { 6472 css.opacity = props.opacity; 6473 } 6474 6475 if (props.width !== undefined) { 6476 css.width = props.width; 6477 } 6478 6479 if (props.height !== undefined) { 6480 css.height = props.height; 6481 } 6482 6483 return $el.css(css); 6484 }, 6485 6486 // Simple CSS transition handler 6487 // ============================= 6488 6489 animate: function ($el, to, duration, callback, leaveAnimationName) { 6490 var self = this, 6491 from; 6492 6493 if ($.isFunction(duration)) { 6494 callback = duration; 6495 duration = null; 6496 } 6497 6498 self.stop($el); 6499 6500 from = self.getTranslate($el); 6501 6502 $el.on(transitionEnd, function (e) { 6503 // Skip events from child elements and z-index change 6504 if (e && e.originalEvent && (!$el.is(e.originalEvent.target) || e.originalEvent.propertyName == "z-index")) { 6505 return; 6506 } 6507 6508 self.stop($el); 6509 6510 if ($.isNumeric(duration)) { 6511 $el.css("transition-duration", ""); 6512 } 6513 6514 if ($.isPlainObject(to)) { 6515 if (to.scaleX !== undefined && to.scaleY !== undefined) { 6516 self.setTranslate($el, { 6517 top: to.top, 6518 left: to.left, 6519 width: from.width * to.scaleX, 6520 height: from.height * to.scaleY, 6521 scaleX: 1, 6522 scaleY: 1 6523 }); 6524 } 6525 } else if (leaveAnimationName !== true) { 6526 $el.removeClass(to); 6527 } 6528 6529 if ($.isFunction(callback)) { 6530 callback(e); 6531 } 6532 }); 6533 6534 if ($.isNumeric(duration)) { 6535 $el.css("transition-duration", duration + "ms"); 6536 } 6537 6538 // Start animation by changing CSS properties or class name 6539 if ($.isPlainObject(to)) { 6540 if (to.scaleX !== undefined && to.scaleY !== undefined) { 6541 delete to.width; 6542 delete to.height; 6543 6544 if ($el.parent().hasClass("fancybox-slide--image")) { 6545 $el.parent().addClass("fancybox-is-scaling"); 6546 } 6547 } 6548 6549 $.fancybox.setTranslate($el, to); 6550 } else { 6551 $el.addClass(to); 6552 } 6553 6554 // Make sure that `transitionend` callback gets fired 6555 $el.data( 6556 "timer", 6557 setTimeout(function () { 6558 $el.trigger(transitionEnd); 6559 }, duration + 33) 6560 ); 6561 }, 6562 6563 stop: function ($el, callCallback) { 6564 if ($el && $el.length) { 6565 clearTimeout($el.data("timer")); 6566 6567 if (callCallback) { 6568 $el.trigger(transitionEnd); 6569 } 6570 6571 $el.off(transitionEnd).css("transition-duration", ""); 6572 6573 $el.parent().removeClass("fancybox-is-scaling"); 6574 } 6575 } 6576 }; 6577 6578 // Default click handler for "fancyboxed" links 6579 // ============================================ 6580 6581 function _run(e, opts) { 6582 var items = [], 6583 index = 0, 6584 $target, 6585 value, 6586 instance; 6587 6588 // Avoid opening multiple times 6589 if (e && e.isDefaultPrevented()) { 6590 return; 6591 } 6592 6593 e.preventDefault(); 6594 6595 opts = opts || {}; 6596 6597 if (e && e.data) { 6598 opts = mergeOpts(e.data.options, opts); 6599 } 6600 6601 $target = opts.$target || $(e.currentTarget).trigger("blur"); 6602 instance = $.fancybox.getInstance(); 6603 6604 if (instance && instance.$trigger && instance.$trigger.is($target)) { 6605 return; 6606 } 6607 6608 if (opts.selector) { 6609 items = $(opts.selector); 6610 } else { 6611 // Get all related items and find index for clicked one 6612 value = $target.attr("data-fancybox") || ""; 6613 6614 if (value) { 6615 items = e.data ? e.data.items : []; 6616 items = items.length ? items.filter('[data-fancybox="' + value + '"]') : $('[data-fancybox="' + value + '"]'); 6617 } else { 6618 items = [$target]; 6619 } 6620 } 6621 6622 index = $(items).index($target); 6623 6624 // Sometimes current item can not be found 6625 if (index < 0) { 6626 index = 0; 6627 } 6628 6629 instance = $.fancybox.open(items, opts, index); 6630 6631 // Save last active element 6632 instance.$trigger = $target; 6633 } 6634 6635 // Create a jQuery plugin 6636 // ====================== 6637 6638 $.fn.fancybox = function (options) { 6639 var selector; 6640 6641 options = options || {}; 6642 selector = options.selector || false; 6643 6644 if (selector) { 6645 // Use body element instead of document so it executes first 6646 $("body") 6647 .off("click.fb-start", selector) 6648 .on("click.fb-start", selector, { 6649 options: options 6650 }, _run); 6651 } else { 6652 this.off("click.fb-start").on( 6653 "click.fb-start", { 6654 items: this, 6655 options: options 6656 }, 6657 _run 6658 ); 6659 } 6660 6661 return this; 6662 }; 6663 6664 // Self initializing plugin for all elements having `data-fancybox` attribute 6665 // ========================================================================== 6666 6667 $D.on("click.fb-start", "[data-fancybox]", _run); 6668 6669 // Enable "trigger elements" 6670 // ========================= 6671 6672 $D.on("click.fb-start", "[data-fancybox-trigger]", function (e) { 6673 $('[data-fancybox="' + $(this).attr("data-fancybox-trigger") + '"]') 6674 .eq($(this).attr("data-fancybox-index") || 0) 6675 .trigger("click.fb-start", { 6676 $trigger: $(this) 6677 }); 6678 }); 6679 6680 // Track focus event for better accessibility styling 6681 // ================================================== 6682 (function () { 6683 var buttonStr = ".fancybox-button", 6684 focusStr = "fancybox-focus", 6685 $pressed = null; 6686 6687 $D.on("mousedown mouseup focus blur", buttonStr, function (e) { 6688 switch (e.type) { 6689 case "mousedown": 6690 $pressed = $(this); 6691 break; 6692 case "mouseup": 6693 $pressed = null; 6694 break; 6695 case "focusin": 6696 $(buttonStr).removeClass(focusStr); 6697 6698 if (!$(this).is($pressed) && !$(this).is("[disabled]")) { 6699 $(this).addClass(focusStr); 6700 } 6701 break; 6702 case "focusout": 6703 $(buttonStr).removeClass(focusStr); 6704 break; 6705 } 6706 }); 6707 })(); 6708 })(window, document, jQuery); 6709 // ========================================================================== 6710 // 6711 // Media 6712 // Adds additional media type support 6713 // 6714 // ========================================================================== 6715 (function ($) { 6716 "use strict"; 6717 6718 // Object containing properties for each media type 6719 var defaults = { 6720 youtube: { 6721 matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i, 6722 params: { 6723 autoplay: 1, 6724 autohide: 1, 6725 fs: 1, 6726 rel: 0, 6727 hd: 1, 6728 wmode: "transparent", 6729 enablejsapi: 1, 6730 html5: 1 6731 }, 6732 paramPlace: 8, 6733 type: "iframe", 6734 url: "https://www.youtube-nocookie.com/embed/$4", 6735 thumb: "https://img.youtube.com/vi/$4/hqdefault.jpg" 6736 }, 6737 6738 vimeo: { 6739 matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/, 6740 params: { 6741 autoplay: 1, 6742 hd: 1, 6743 show_title: 1, 6744 show_byline: 1, 6745 show_portrait: 0, 6746 fullscreen: 1 6747 }, 6748 paramPlace: 3, 6749 type: "iframe", 6750 url: "//player.vimeo.com/video/$2" 6751 }, 6752 6753 instagram: { 6754 matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i, 6755 type: "image", 6756 url: "//$1/p/$2/media/?size=l" 6757 }, 6758 6759 // Examples: 6760 // http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16 6761 // https://www.google.com/maps/@37.7852006,-122.4146355,14.65z 6762 // https://www.google.com/maps/@52.2111123,2.9237542,6.61z?hl=en 6763 // https://www.google.com/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572 6764 gmap_place: { 6765 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i, 6766 type: "iframe", 6767 url: function (rez) { 6768 return ( 6769 "//maps.google." + 6770 rez[2] + 6771 "/?ll=" + 6772 (rez[9] ? rez[9] + "&z=" + Math.floor(rez[10]) + (rez[12] ? rez[12].replace(/^\//, "&") : "") : rez[12] + "").replace(/\?/, "&") + 6773 "&output=" + 6774 (rez[12] && rez[12].indexOf("layer=c") > 0 ? "svembed" : "embed") 6775 ); 6776 } 6777 }, 6778 6779 // Examples: 6780 // https://www.google.com/maps/search/Empire+State+Building/ 6781 // https://www.google.com/maps/search/?api=1&query=centurylink+field 6782 // https://www.google.com/maps/search/?api=1&query=47.5951518,-122.3316393 6783 gmap_search: { 6784 matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(maps\/search\/)(.*)/i, 6785 type: "iframe", 6786 url: function (rez) { 6787 return "//maps.google." + rez[2] + "/maps?q=" + rez[5].replace("query=", "q=").replace("api=1", "") + "&output=embed"; 6788 } 6789 } 6790 }; 6791 6792 // Formats matching url to final form 6793 var format = function (url, rez, params) { 6794 if (!url) { 6795 return; 6796 } 6797 6798 params = params || ""; 6799 6800 if ($.type(params) === "object") { 6801 params = $.param(params, true); 6802 } 6803 6804 $.each(rez, function (key, value) { 6805 url = url.replace("$" + key, value || ""); 6806 }); 6807 6808 if (params.length) { 6809 url += (url.indexOf("?") > 0 ? "&" : "?") + params; 6810 } 6811 6812 return url; 6813 }; 6814 6815 $(document).on("objectNeedsType.fb", function (e, instance, item) { 6816 var url = item.src || "", 6817 type = false, 6818 media, 6819 thumb, 6820 rez, 6821 params, 6822 urlParams, 6823 paramObj, 6824 provider; 6825 6826 media = $.extend(true, {}, defaults, item.opts.media); 6827 6828 // Look for any matching media type 6829 $.each(media, function (providerName, providerOpts) { 6830 rez = url.match(providerOpts.matcher); 6831 6832 if (!rez) { 6833 return; 6834 } 6835 6836 type = providerOpts.type; 6837 provider = providerName; 6838 paramObj = {}; 6839 6840 if (providerOpts.paramPlace && rez[providerOpts.paramPlace]) { 6841 urlParams = rez[providerOpts.paramPlace]; 6842 6843 if (urlParams[0] == "?") { 6844 urlParams = urlParams.substring(1); 6845 } 6846 6847 urlParams = urlParams.split("&"); 6848 6849 for (var m = 0; m < urlParams.length; ++m) { 6850 var p = urlParams[m].split("=", 2); 6851 6852 if (p.length == 2) { 6853 paramObj[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " ")); 6854 } 6855 } 6856 } 6857 6858 params = $.extend(true, {}, providerOpts.params, item.opts[providerName], paramObj); 6859 6860 url = 6861 $.type(providerOpts.url) === "function" ? providerOpts.url.call(this, rez, params, item) : format(providerOpts.url, rez, params); 6862 6863 thumb = 6864 $.type(providerOpts.thumb) === "function" ? providerOpts.thumb.call(this, rez, params, item) : format(providerOpts.thumb, rez); 6865 6866 if (providerName === "youtube") { 6867 url = url.replace(/&t=((\d+)m)?(\d+)s/, function (match, p1, m, s) { 6868 return "&start=" + ((m ? parseInt(m, 10) * 60 : 0) + parseInt(s, 10)); 6869 }); 6870 } else if (providerName === "vimeo") { 6871 url = url.replace("&%23", "#"); 6872 } 6873 6874 return false; 6875 }); 6876 6877 // If it is found, then change content type and update the url 6878 6879 if (type) { 6880 if (!item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length)) { 6881 item.opts.thumb = thumb; 6882 } 6883 6884 if (type === "iframe") { 6885 item.opts = $.extend(true, item.opts, { 6886 iframe: { 6887 preload: false, 6888 attr: { 6889 scrolling: "no" 6890 } 6891 } 6892 }); 6893 } 6894 6895 $.extend(item, { 6896 type: type, 6897 src: url, 6898 origSrc: item.src, 6899 contentSource: provider, 6900 contentType: type === "image" ? "image" : provider == "gmap_place" || provider == "gmap_search" ? "map" : "video" 6901 }); 6902 } else if (url) { 6903 item.type = item.opts.defaultType; 6904 } 6905 }); 6906 6907 // Load YouTube/Video API on request to detect when video finished playing 6908 var VideoAPILoader = { 6909 youtube: { 6910 src: "https://www.youtube.com/iframe_api", 6911 class: "YT", 6912 loading: false, 6913 loaded: false 6914 }, 6915 6916 vimeo: { 6917 src: "https://player.vimeo.com/api/player.js", 6918 class: "Vimeo", 6919 loading: false, 6920 loaded: false 6921 }, 6922 6923 load: function (vendor) { 6924 var _this = this, 6925 script; 6926 6927 if (this[vendor].loaded) { 6928 setTimeout(function () { 6929 _this.done(vendor); 6930 }); 6931 return; 6932 } 6933 6934 if (this[vendor].loading) { 6935 return; 6936 } 6937 6938 this[vendor].loading = true; 6939 6940 script = document.createElement("script"); 6941 script.type = "text/javascript"; 6942 script.src = this[vendor].src; 6943 6944 if (vendor === "youtube") { 6945 window.onYouTubeIframeAPIReady = function () { 6946 _this[vendor].loaded = true; 6947 _this.done(vendor); 6948 }; 6949 } else { 6950 script.onload = function () { 6951 _this[vendor].loaded = true; 6952 _this.done(vendor); 6953 }; 6954 } 6955 6956 document.body.appendChild(script); 6957 }, 6958 done: function (vendor) { 6959 var instance, $el, player; 6960 6961 if (vendor === "youtube") { 6962 delete window.onYouTubeIframeAPIReady; 6963 } 6964 6965 instance = $.fancybox.getInstance(); 6966 6967 if (instance) { 6968 $el = instance.current.$content.find("iframe"); 6969 6970 if (vendor === "youtube" && YT !== undefined && YT) { 6971 player = new YT.Player($el.attr("id"), { 6972 events: { 6973 onStateChange: function (e) { 6974 if (e.data == 0) { 6975 instance.next(); 6976 } 6977 } 6978 } 6979 }); 6980 } else if (vendor === "vimeo" && Vimeo !== undefined && Vimeo) { 6981 player = new Vimeo.Player($el); 6982 6983 player.on("ended", function () { 6984 instance.next(); 6985 }); 6986 } 6987 } 6988 } 6989 }; 6990 6991 $(document).on({ 6992 "afterShow.fb": function (e, instance, current) { 6993 if (instance.group.length > 1 && (current.contentSource === "youtube" || current.contentSource === "vimeo")) { 6994 VideoAPILoader.load(current.contentSource); 6995 } 6996 } 6997 }); 6998 })(jQuery); 6999 // ========================================================================== 7000 // 7001 // Guestures 7002 // Adds touch guestures, handles click and tap events 7003 // 7004 // ========================================================================== 7005 (function (window, document, $) { 7006 "use strict"; 7007 7008 var requestAFrame = (function () { 7009 return ( 7010 window.requestAnimationFrame || 7011 window.webkitRequestAnimationFrame || 7012 window.mozRequestAnimationFrame || 7013 window.oRequestAnimationFrame || 7014 // if all else fails, use setTimeout 7015 function (callback) { 7016 return window.setTimeout(callback, 1000 / 60); 7017 } 7018 ); 7019 })(); 7020 7021 var cancelAFrame = (function () { 7022 return ( 7023 window.cancelAnimationFrame || 7024 window.webkitCancelAnimationFrame || 7025 window.mozCancelAnimationFrame || 7026 window.oCancelAnimationFrame || 7027 function (id) { 7028 window.clearTimeout(id); 7029 } 7030 ); 7031 })(); 7032 7033 var getPointerXY = function (e) { 7034 var result = []; 7035 7036 e = e.originalEvent || e || window.e; 7037 e = e.touches && e.touches.length ? e.touches : e.changedTouches && e.changedTouches.length ? e.changedTouches : [e]; 7038 7039 for (var key in e) { 7040 if (e[key].pageX) { 7041 result.push({ 7042 x: e[key].pageX, 7043 y: e[key].pageY 7044 }); 7045 } else if (e[key].clientX) { 7046 result.push({ 7047 x: e[key].clientX, 7048 y: e[key].clientY 7049 }); 7050 } 7051 } 7052 7053 return result; 7054 }; 7055 7056 var distance = function (point2, point1, what) { 7057 if (!point1 || !point2) { 7058 return 0; 7059 } 7060 7061 if (what === "x") { 7062 return point2.x - point1.x; 7063 } else if (what === "y") { 7064 return point2.y - point1.y; 7065 } 7066 7067 return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2)); 7068 }; 7069 7070 var isClickable = function ($el) { 7071 if ( 7072 $el.is('a,area,button,[role="button"],input,label,select,summary,textarea,video,audio,iframe') || 7073 $.isFunction($el.get(0).onclick) || 7074 $el.data("selectable") 7075 ) { 7076 return true; 7077 } 7078 7079 // Check for attributes like data-fancybox-next or data-fancybox-close 7080 for (var i = 0, atts = $el[0].attributes, n = atts.length; i < n; i++) { 7081 if (atts[i].nodeName.substr(0, 14) === "data-fancybox-") { 7082 return true; 7083 } 7084 } 7085 7086 return false; 7087 }; 7088 7089 var hasScrollbars = function (el) { 7090 var overflowY = window.getComputedStyle(el)["overflow-y"], 7091 overflowX = window.getComputedStyle(el)["overflow-x"], 7092 vertical = (overflowY === "scroll" || overflowY === "auto") && el.scrollHeight > el.clientHeight, 7093 horizontal = (overflowX === "scroll" || overflowX === "auto") && el.scrollWidth > el.clientWidth; 7094 7095 return vertical || horizontal; 7096 }; 7097 7098 var isScrollable = function ($el) { 7099 var rez = false; 7100 7101 while (true) { 7102 rez = hasScrollbars($el.get(0)); 7103 7104 if (rez) { 7105 break; 7106 } 7107 7108 $el = $el.parent(); 7109 7110 if (!$el.length || $el.hasClass("fancybox-stage") || $el.is("body")) { 7111 break; 7112 } 7113 } 7114 7115 return rez; 7116 }; 7117 7118 var Guestures = function (instance) { 7119 var self = this; 7120 7121 self.instance = instance; 7122 7123 self.$bg = instance.$refs.bg; 7124 self.$stage = instance.$refs.stage; 7125 self.$container = instance.$refs.container; 7126 7127 self.destroy(); 7128 7129 self.$container.on("touchstart.fb.touch mousedown.fb.touch", $.proxy(self, "ontouchstart")); 7130 }; 7131 7132 Guestures.prototype.destroy = function () { 7133 var self = this; 7134 7135 self.$container.off(".fb.touch"); 7136 7137 $(document).off(".fb.touch"); 7138 7139 if (self.requestId) { 7140 cancelAFrame(self.requestId); 7141 self.requestId = null; 7142 } 7143 7144 if (self.tapped) { 7145 clearTimeout(self.tapped); 7146 self.tapped = null; 7147 } 7148 }; 7149 7150 Guestures.prototype.ontouchstart = function (e) { 7151 var self = this, 7152 $target = $(e.target), 7153 instance = self.instance, 7154 current = instance.current, 7155 $slide = current.$slide, 7156 $content = current.$content, 7157 isTouchDevice = e.type == "touchstart"; 7158 7159 // Do not respond to both (touch and mouse) events 7160 if (isTouchDevice) { 7161 self.$container.off("mousedown.fb.touch"); 7162 } 7163 7164 // Ignore right click 7165 if (e.originalEvent && e.originalEvent.button == 2) { 7166 return; 7167 } 7168 7169 // Ignore taping on links, buttons, input elements 7170 if (!$slide.length || !$target.length || isClickable($target) || isClickable($target.parent())) { 7171 return; 7172 } 7173 // Ignore clicks on the scrollbar 7174 if (!$target.is("img") && e.originalEvent.clientX > $target[0].clientWidth + $target.offset().left) { 7175 return; 7176 } 7177 7178 // Ignore clicks while zooming or closing 7179 if (!current || instance.isAnimating || current.$slide.hasClass("fancybox-animated")) { 7180 e.stopPropagation(); 7181 e.preventDefault(); 7182 7183 return; 7184 } 7185 7186 self.realPoints = self.startPoints = getPointerXY(e); 7187 7188 if (!self.startPoints.length) { 7189 return; 7190 } 7191 7192 // Allow other scripts to catch touch event if "touch" is set to false 7193 if (current.touch) { 7194 e.stopPropagation(); 7195 } 7196 7197 self.startEvent = e; 7198 7199 self.canTap = true; 7200 self.$target = $target; 7201 self.$content = $content; 7202 self.opts = current.opts.touch; 7203 7204 self.isPanning = false; 7205 self.isSwiping = false; 7206 self.isZooming = false; 7207 self.isScrolling = false; 7208 self.canPan = instance.canPan(); 7209 7210 self.startTime = new Date().getTime(); 7211 self.distanceX = self.distanceY = self.distance = 0; 7212 7213 self.canvasWidth = Math.round($slide[0].clientWidth); 7214 self.canvasHeight = Math.round($slide[0].clientHeight); 7215 7216 self.contentLastPos = null; 7217 self.contentStartPos = $.fancybox.getTranslate(self.$content) || { 7218 top: 0, 7219 left: 0 7220 }; 7221 self.sliderStartPos = $.fancybox.getTranslate($slide); 7222 7223 // Since position will be absolute, but we need to make it relative to the stage 7224 self.stagePos = $.fancybox.getTranslate(instance.$refs.stage); 7225 7226 self.sliderStartPos.top -= self.stagePos.top; 7227 self.sliderStartPos.left -= self.stagePos.left; 7228 7229 self.contentStartPos.top -= self.stagePos.top; 7230 self.contentStartPos.left -= self.stagePos.left; 7231 7232 $(document) 7233 .off(".fb.touch") 7234 .on(isTouchDevice ? "touchend.fb.touch touchcancel.fb.touch" : "mouseup.fb.touch mouseleave.fb.touch", $.proxy(self, "ontouchend")) 7235 .on(isTouchDevice ? "touchmove.fb.touch" : "mousemove.fb.touch", $.proxy(self, "ontouchmove")); 7236 7237 if ($.fancybox.isMobile) { 7238 document.addEventListener("scroll", self.onscroll, true); 7239 } 7240 7241 // Skip if clicked outside the sliding area 7242 if (!(self.opts || self.canPan) || !($target.is(self.$stage) || self.$stage.find($target).length)) { 7243 if ($target.is(".fancybox-image")) { 7244 e.preventDefault(); 7245 } 7246 7247 if (!($.fancybox.isMobile && $target.parents(".fancybox-caption").length)) { 7248 return; 7249 } 7250 } 7251 7252 self.isScrollable = isScrollable($target) || isScrollable($target.parent()); 7253 7254 // Check if element is scrollable and try to prevent default behavior (scrolling) 7255 if (!($.fancybox.isMobile && self.isScrollable)) { 7256 e.preventDefault(); 7257 } 7258 7259 // One finger or mouse click - swipe or pan an image 7260 if (self.startPoints.length === 1 || current.hasError) { 7261 if (self.canPan) { 7262 $.fancybox.stop(self.$content); 7263 7264 self.isPanning = true; 7265 } else { 7266 self.isSwiping = true; 7267 } 7268 7269 self.$container.addClass("fancybox-is-grabbing"); 7270 } 7271 7272 // Two fingers - zoom image 7273 if (self.startPoints.length === 2 && current.type === "image" && (current.isLoaded || current.$ghost)) { 7274 self.canTap = false; 7275 self.isSwiping = false; 7276 self.isPanning = false; 7277 7278 self.isZooming = true; 7279 7280 $.fancybox.stop(self.$content); 7281 7282 self.centerPointStartX = (self.startPoints[0].x + self.startPoints[1].x) * 0.5 - $(window).scrollLeft(); 7283 self.centerPointStartY = (self.startPoints[0].y + self.startPoints[1].y) * 0.5 - $(window).scrollTop(); 7284 7285 self.percentageOfImageAtPinchPointX = (self.centerPointStartX - self.contentStartPos.left) / self.contentStartPos.width; 7286 self.percentageOfImageAtPinchPointY = (self.centerPointStartY - self.contentStartPos.top) / self.contentStartPos.height; 7287 7288 self.startDistanceBetweenFingers = distance(self.startPoints[0], self.startPoints[1]); 7289 } 7290 }; 7291 7292 Guestures.prototype.onscroll = function (e) { 7293 var self = this; 7294 7295 self.isScrolling = true; 7296 7297 document.removeEventListener("scroll", self.onscroll, true); 7298 }; 7299 7300 Guestures.prototype.ontouchmove = function (e) { 7301 var self = this; 7302 7303 // Make sure user has not released over iframe or disabled element 7304 if (e.originalEvent.buttons !== undefined && e.originalEvent.buttons === 0) { 7305 self.ontouchend(e); 7306 return; 7307 } 7308 7309 if (self.isScrolling) { 7310 self.canTap = false; 7311 return; 7312 } 7313 7314 self.newPoints = getPointerXY(e); 7315 7316 if (!(self.opts || self.canPan) || !self.newPoints.length || !self.newPoints.length) { 7317 return; 7318 } 7319 7320 if (!(self.isSwiping && self.isSwiping === true)) { 7321 e.preventDefault(); 7322 } 7323 7324 self.distanceX = distance(self.newPoints[0], self.startPoints[0], "x"); 7325 self.distanceY = distance(self.newPoints[0], self.startPoints[0], "y"); 7326 7327 self.distance = distance(self.newPoints[0], self.startPoints[0]); 7328 7329 // Skip false ontouchmove events (Chrome) 7330 if (self.distance > 0) { 7331 if (self.isSwiping) { 7332 self.onSwipe(e); 7333 } else if (self.isPanning) { 7334 self.onPan(); 7335 } else if (self.isZooming) { 7336 self.onZoom(); 7337 } 7338 } 7339 }; 7340 7341 Guestures.prototype.onSwipe = function (e) { 7342 var self = this, 7343 instance = self.instance, 7344 swiping = self.isSwiping, 7345 left = self.sliderStartPos.left || 0, 7346 angle; 7347 7348 // If direction is not yet determined 7349 if (swiping === true) { 7350 // We need at least 10px distance to correctly calculate an angle 7351 if (Math.abs(self.distance) > 10) { 7352 self.canTap = false; 7353 7354 if (instance.group.length < 2 && self.opts.vertical) { 7355 self.isSwiping = "y"; 7356 } else if (instance.isDragging || self.opts.vertical === false || (self.opts.vertical === "auto" && $(window).width() > 800)) { 7357 self.isSwiping = "x"; 7358 } else { 7359 angle = Math.abs((Math.atan2(self.distanceY, self.distanceX) * 180) / Math.PI); 7360 7361 self.isSwiping = angle > 45 && angle < 135 ? "y" : "x"; 7362 } 7363 7364 if (self.isSwiping === "y" && $.fancybox.isMobile && self.isScrollable) { 7365 self.isScrolling = true; 7366 7367 return; 7368 } 7369 7370 instance.isDragging = self.isSwiping; 7371 7372 // Reset points to avoid jumping, because we dropped first swipes to calculate the angle 7373 self.startPoints = self.newPoints; 7374 7375 $.each(instance.slides, function (index, slide) { 7376 var slidePos, stagePos; 7377 7378 $.fancybox.stop(slide.$slide); 7379 7380 slidePos = $.fancybox.getTranslate(slide.$slide); 7381 stagePos = $.fancybox.getTranslate(instance.$refs.stage); 7382 7383 slide.$slide 7384 .css({ 7385 transform: "", 7386 opacity: "", 7387 "transition-duration": "" 7388 }) 7389 .removeClass("fancybox-animated") 7390 .removeClass(function (index, className) { 7391 return (className.match(/(^|\s)fancybox-fx-\S+/g) || []).join(" "); 7392 }); 7393 7394 if (slide.pos === instance.current.pos) { 7395 self.sliderStartPos.top = slidePos.top - stagePos.top; 7396 self.sliderStartPos.left = slidePos.left - stagePos.left; 7397 } 7398 7399 $.fancybox.setTranslate(slide.$slide, { 7400 top: slidePos.top - stagePos.top, 7401 left: slidePos.left - stagePos.left 7402 }); 7403 }); 7404 7405 // Stop slideshow 7406 if (instance.SlideShow && instance.SlideShow.isActive) { 7407 instance.SlideShow.stop(); 7408 } 7409 } 7410 7411 return; 7412 } 7413 7414 // Sticky edges 7415 if (swiping == "x") { 7416 if ( 7417 self.distanceX > 0 && 7418 (self.instance.group.length < 2 || (self.instance.current.index === 0 && !self.instance.current.opts.loop)) 7419 ) { 7420 left = left + Math.pow(self.distanceX, 0.8); 7421 } else if ( 7422 self.distanceX < 0 && 7423 (self.instance.group.length < 2 || 7424 (self.instance.current.index === self.instance.group.length - 1 && !self.instance.current.opts.loop)) 7425 ) { 7426 left = left - Math.pow(-self.distanceX, 0.8); 7427 } else { 7428 left = left + self.distanceX; 7429 } 7430 } 7431 7432 self.sliderLastPos = { 7433 top: swiping == "x" ? 0 : self.sliderStartPos.top + self.distanceY, 7434 left: left 7435 }; 7436 7437 if (self.requestId) { 7438 cancelAFrame(self.requestId); 7439 7440 self.requestId = null; 7441 } 7442 7443 self.requestId = requestAFrame(function () { 7444 if (self.sliderLastPos) { 7445 $.each(self.instance.slides, function (index, slide) { 7446 var pos = slide.pos - self.instance.currPos; 7447 7448 $.fancybox.setTranslate(slide.$slide, { 7449 top: self.sliderLastPos.top, 7450 left: self.sliderLastPos.left + pos * self.canvasWidth + pos * slide.opts.gutter 7451 }); 7452 }); 7453 7454 self.$container.addClass("fancybox-is-sliding"); 7455 } 7456 }); 7457 }; 7458 7459 Guestures.prototype.onPan = function () { 7460 var self = this; 7461 7462 // Prevent accidental movement (sometimes, when tapping casually, finger can move a bit) 7463 if (distance(self.newPoints[0], self.realPoints[0]) < ($.fancybox.isMobile ? 10 : 5)) { 7464 self.startPoints = self.newPoints; 7465 return; 7466 } 7467 7468 self.canTap = false; 7469 7470 self.contentLastPos = self.limitMovement(); 7471 7472 if (self.requestId) { 7473 cancelAFrame(self.requestId); 7474 } 7475 7476 self.requestId = requestAFrame(function () { 7477 $.fancybox.setTranslate(self.$content, self.contentLastPos); 7478 }); 7479 }; 7480 7481 // Make panning sticky to the edges 7482 Guestures.prototype.limitMovement = function () { 7483 var self = this; 7484 7485 var canvasWidth = self.canvasWidth; 7486 var canvasHeight = self.canvasHeight; 7487 7488 var distanceX = self.distanceX; 7489 var distanceY = self.distanceY; 7490 7491 var contentStartPos = self.contentStartPos; 7492 7493 var currentOffsetX = contentStartPos.left; 7494 var currentOffsetY = contentStartPos.top; 7495 7496 var currentWidth = contentStartPos.width; 7497 var currentHeight = contentStartPos.height; 7498 7499 var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY, newOffsetX, newOffsetY; 7500 7501 if (currentWidth > canvasWidth) { 7502 newOffsetX = currentOffsetX + distanceX; 7503 } else { 7504 newOffsetX = currentOffsetX; 7505 } 7506 7507 newOffsetY = currentOffsetY + distanceY; 7508 7509 // Slow down proportionally to traveled distance 7510 minTranslateX = Math.max(0, canvasWidth * 0.5 - currentWidth * 0.5); 7511 minTranslateY = Math.max(0, canvasHeight * 0.5 - currentHeight * 0.5); 7512 7513 maxTranslateX = Math.min(canvasWidth - currentWidth, canvasWidth * 0.5 - currentWidth * 0.5); 7514 maxTranslateY = Math.min(canvasHeight - currentHeight, canvasHeight * 0.5 - currentHeight * 0.5); 7515 7516 // -> 7517 if (distanceX > 0 && newOffsetX > minTranslateX) { 7518 newOffsetX = minTranslateX - 1 + Math.pow(-minTranslateX + currentOffsetX + distanceX, 0.8) || 0; 7519 } 7520 7521 // <- 7522 if (distanceX < 0 && newOffsetX < maxTranslateX) { 7523 newOffsetX = maxTranslateX + 1 - Math.pow(maxTranslateX - currentOffsetX - distanceX, 0.8) || 0; 7524 } 7525 7526 // \/ 7527 if (distanceY > 0 && newOffsetY > minTranslateY) { 7528 newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8) || 0; 7529 } 7530 7531 // /\ 7532 if (distanceY < 0 && newOffsetY < maxTranslateY) { 7533 newOffsetY = maxTranslateY + 1 - Math.pow(maxTranslateY - currentOffsetY - distanceY, 0.8) || 0; 7534 } 7535 7536 return { 7537 top: newOffsetY, 7538 left: newOffsetX 7539 }; 7540 }; 7541 7542 Guestures.prototype.limitPosition = function (newOffsetX, newOffsetY, newWidth, newHeight) { 7543 var self = this; 7544 7545 var canvasWidth = self.canvasWidth; 7546 var canvasHeight = self.canvasHeight; 7547 7548 if (newWidth > canvasWidth) { 7549 newOffsetX = newOffsetX > 0 ? 0 : newOffsetX; 7550 newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX; 7551 } else { 7552 // Center horizontally 7553 newOffsetX = Math.max(0, canvasWidth / 2 - newWidth / 2); 7554 } 7555 7556 if (newHeight > canvasHeight) { 7557 newOffsetY = newOffsetY > 0 ? 0 : newOffsetY; 7558 newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY; 7559 } else { 7560 // Center vertically 7561 newOffsetY = Math.max(0, canvasHeight / 2 - newHeight / 2); 7562 } 7563 7564 return { 7565 top: newOffsetY, 7566 left: newOffsetX 7567 }; 7568 }; 7569 7570 Guestures.prototype.onZoom = function () { 7571 var self = this; 7572 7573 // Calculate current distance between points to get pinch ratio and new width and height 7574 var contentStartPos = self.contentStartPos; 7575 7576 var currentWidth = contentStartPos.width; 7577 var currentHeight = contentStartPos.height; 7578 7579 var currentOffsetX = contentStartPos.left; 7580 var currentOffsetY = contentStartPos.top; 7581 7582 var endDistanceBetweenFingers = distance(self.newPoints[0], self.newPoints[1]); 7583 7584 var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers; 7585 7586 var newWidth = Math.floor(currentWidth * pinchRatio); 7587 var newHeight = Math.floor(currentHeight * pinchRatio); 7588 7589 // This is the translation due to pinch-zooming 7590 var translateFromZoomingX = (currentWidth - newWidth) * self.percentageOfImageAtPinchPointX; 7591 var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY; 7592 7593 // Point between the two touches 7594 var centerPointEndX = (self.newPoints[0].x + self.newPoints[1].x) / 2 - $(window).scrollLeft(); 7595 var centerPointEndY = (self.newPoints[0].y + self.newPoints[1].y) / 2 - $(window).scrollTop(); 7596 7597 // And this is the translation due to translation of the centerpoint 7598 // between the two fingers 7599 var translateFromTranslatingX = centerPointEndX - self.centerPointStartX; 7600 var translateFromTranslatingY = centerPointEndY - self.centerPointStartY; 7601 7602 // The new offset is the old/current one plus the total translation 7603 var newOffsetX = currentOffsetX + (translateFromZoomingX + translateFromTranslatingX); 7604 var newOffsetY = currentOffsetY + (translateFromZoomingY + translateFromTranslatingY); 7605 7606 var newPos = { 7607 top: newOffsetY, 7608 left: newOffsetX, 7609 scaleX: pinchRatio, 7610 scaleY: pinchRatio 7611 }; 7612 7613 self.canTap = false; 7614 7615 self.newWidth = newWidth; 7616 self.newHeight = newHeight; 7617 7618 self.contentLastPos = newPos; 7619 7620 if (self.requestId) { 7621 cancelAFrame(self.requestId); 7622 } 7623 7624 self.requestId = requestAFrame(function () { 7625 $.fancybox.setTranslate(self.$content, self.contentLastPos); 7626 }); 7627 }; 7628 7629 Guestures.prototype.ontouchend = function (e) { 7630 var self = this; 7631 7632 var swiping = self.isSwiping; 7633 var panning = self.isPanning; 7634 var zooming = self.isZooming; 7635 var scrolling = self.isScrolling; 7636 7637 self.endPoints = getPointerXY(e); 7638 self.dMs = Math.max(new Date().getTime() - self.startTime, 1); 7639 7640 self.$container.removeClass("fancybox-is-grabbing"); 7641 7642 $(document).off(".fb.touch"); 7643 7644 document.removeEventListener("scroll", self.onscroll, true); 7645 7646 if (self.requestId) { 7647 cancelAFrame(self.requestId); 7648 7649 self.requestId = null; 7650 } 7651 7652 self.isSwiping = false; 7653 self.isPanning = false; 7654 self.isZooming = false; 7655 self.isScrolling = false; 7656 7657 self.instance.isDragging = false; 7658 7659 if (self.canTap) { 7660 return self.onTap(e); 7661 } 7662 7663 self.speed = 100; 7664 7665 // Speed in px/ms 7666 self.velocityX = (self.distanceX / self.dMs) * 0.5; 7667 self.velocityY = (self.distanceY / self.dMs) * 0.5; 7668 7669 if (panning) { 7670 self.endPanning(); 7671 } else if (zooming) { 7672 self.endZooming(); 7673 } else { 7674 self.endSwiping(swiping, scrolling); 7675 } 7676 7677 return; 7678 }; 7679 7680 Guestures.prototype.endSwiping = function (swiping, scrolling) { 7681 var self = this, 7682 ret = false, 7683 len = self.instance.group.length, 7684 distanceX = Math.abs(self.distanceX), 7685 canAdvance = swiping == "x" && len > 1 && ((self.dMs > 130 && distanceX > 10) || distanceX > 50), 7686 speedX = 300; 7687 7688 self.sliderLastPos = null; 7689 7690 // Close if swiped vertically / navigate if horizontally 7691 if (swiping == "y" && !scrolling && Math.abs(self.distanceY) > 50) { 7692 // Continue vertical movement 7693 $.fancybox.animate( 7694 self.instance.current.$slide, { 7695 top: self.sliderStartPos.top + self.distanceY + self.velocityY * 150, 7696 opacity: 0 7697 }, 7698 200 7699 ); 7700 ret = self.instance.close(true, 250); 7701 } else if (canAdvance && self.distanceX > 0) { 7702 ret = self.instance.previous(speedX); 7703 } else if (canAdvance && self.distanceX < 0) { 7704 ret = self.instance.next(speedX); 7705 } 7706 7707 if (ret === false && (swiping == "x" || swiping == "y")) { 7708 self.instance.centerSlide(200); 7709 } 7710 7711 self.$container.removeClass("fancybox-is-sliding"); 7712 }; 7713 7714 // Limit panning from edges 7715 // ======================== 7716 Guestures.prototype.endPanning = function () { 7717 var self = this, 7718 newOffsetX, 7719 newOffsetY, 7720 newPos; 7721 7722 if (!self.contentLastPos) { 7723 return; 7724 } 7725 7726 if (self.opts.momentum === false || self.dMs > 350) { 7727 newOffsetX = self.contentLastPos.left; 7728 newOffsetY = self.contentLastPos.top; 7729 } else { 7730 // Continue movement 7731 newOffsetX = self.contentLastPos.left + self.velocityX * 500; 7732 newOffsetY = self.contentLastPos.top + self.velocityY * 500; 7733 } 7734 7735 newPos = self.limitPosition(newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height); 7736 7737 newPos.width = self.contentStartPos.width; 7738 newPos.height = self.contentStartPos.height; 7739 7740 $.fancybox.animate(self.$content, newPos, 366); 7741 }; 7742 7743 Guestures.prototype.endZooming = function () { 7744 var self = this; 7745 7746 var current = self.instance.current; 7747 7748 var newOffsetX, newOffsetY, newPos, reset; 7749 7750 var newWidth = self.newWidth; 7751 var newHeight = self.newHeight; 7752 7753 if (!self.contentLastPos) { 7754 return; 7755 } 7756 7757 newOffsetX = self.contentLastPos.left; 7758 newOffsetY = self.contentLastPos.top; 7759 7760 reset = { 7761 top: newOffsetY, 7762 left: newOffsetX, 7763 width: newWidth, 7764 height: newHeight, 7765 scaleX: 1, 7766 scaleY: 1 7767 }; 7768 7769 // Reset scalex/scaleY values; this helps for perfomance and does not break animation 7770 $.fancybox.setTranslate(self.$content, reset); 7771 7772 if (newWidth < self.canvasWidth && newHeight < self.canvasHeight) { 7773 self.instance.scaleToFit(150); 7774 } else if (newWidth > current.width || newHeight > current.height) { 7775 self.instance.scaleToActual(self.centerPointStartX, self.centerPointStartY, 150); 7776 } else { 7777 newPos = self.limitPosition(newOffsetX, newOffsetY, newWidth, newHeight); 7778 7779 $.fancybox.animate(self.$content, newPos, 150); 7780 } 7781 }; 7782 7783 Guestures.prototype.onTap = function (e) { 7784 var self = this; 7785 var $target = $(e.target); 7786 7787 var instance = self.instance; 7788 var current = instance.current; 7789 7790 var endPoints = (e && getPointerXY(e)) || self.startPoints; 7791 7792 var tapX = endPoints[0] ? endPoints[0].x - $(window).scrollLeft() - self.stagePos.left : 0; 7793 var tapY = endPoints[0] ? endPoints[0].y - $(window).scrollTop() - self.stagePos.top : 0; 7794 7795 var where; 7796 7797 var process = function (prefix) { 7798 var action = current.opts[prefix]; 7799 7800 if ($.isFunction(action)) { 7801 action = action.apply(instance, [current, e]); 7802 } 7803 7804 if (!action) { 7805 return; 7806 } 7807 7808 switch (action) { 7809 case "close": 7810 instance.close(self.startEvent); 7811 7812 break; 7813 7814 case "toggleControls": 7815 instance.toggleControls(); 7816 7817 break; 7818 7819 case "next": 7820 instance.next(); 7821 7822 break; 7823 7824 case "nextOrClose": 7825 if (instance.group.length > 1) { 7826 instance.next(); 7827 } else { 7828 instance.close(self.startEvent); 7829 } 7830 7831 break; 7832 7833 case "zoom": 7834 if (current.type == "image" && (current.isLoaded || current.$ghost)) { 7835 if (instance.canPan()) { 7836 instance.scaleToFit(); 7837 } else if (instance.isScaledDown()) { 7838 instance.scaleToActual(tapX, tapY); 7839 } else if (instance.group.length < 2) { 7840 instance.close(self.startEvent); 7841 } 7842 } 7843 7844 break; 7845 } 7846 }; 7847 7848 // Ignore right click 7849 if (e.originalEvent && e.originalEvent.button == 2) { 7850 return; 7851 } 7852 7853 // Skip if clicked on the scrollbar 7854 if (!$target.is("img") && tapX > $target[0].clientWidth + $target.offset().left) { 7855 return; 7856 } 7857 7858 // Check where is clicked 7859 if ($target.is(".fancybox-bg,.fancybox-inner,.fancybox-outer,.fancybox-container")) { 7860 where = "Outside"; 7861 } else if ($target.is(".fancybox-slide")) { 7862 where = "Slide"; 7863 } else if ( 7864 instance.current.$content && 7865 instance.current.$content 7866 .find($target) 7867 .addBack() 7868 .filter($target).length 7869 ) { 7870 where = "Content"; 7871 } else { 7872 return; 7873 } 7874 7875 // Check if this is a double tap 7876 if (self.tapped) { 7877 // Stop previously created single tap 7878 clearTimeout(self.tapped); 7879 self.tapped = null; 7880 7881 // Skip if distance between taps is too big 7882 if (Math.abs(tapX - self.tapX) > 50 || Math.abs(tapY - self.tapY) > 50) { 7883 return this; 7884 } 7885 7886 // OK, now we assume that this is a double-tap 7887 process("dblclick" + where); 7888 } else { 7889 // Single tap will be processed if user has not clicked second time within 300ms 7890 // or there is no need to wait for double-tap 7891 self.tapX = tapX; 7892 self.tapY = tapY; 7893 7894 if (current.opts["dblclick" + where] && current.opts["dblclick" + where] !== current.opts["click" + where]) { 7895 self.tapped = setTimeout(function () { 7896 self.tapped = null; 7897 7898 if (!instance.isAnimating) { 7899 process("click" + where); 7900 } 7901 }, 500); 7902 } else { 7903 process("click" + where); 7904 } 7905 } 7906 7907 return this; 7908 }; 7909 7910 $(document) 7911 .on("onActivate.fb", function (e, instance) { 7912 if (instance && !instance.Guestures) { 7913 instance.Guestures = new Guestures(instance); 7914 } 7915 }) 7916 .on("beforeClose.fb", function (e, instance) { 7917 if (instance && instance.Guestures) { 7918 instance.Guestures.destroy(); 7919 } 7920 }); 7921 })(window, document, jQuery); 7922 // ========================================================================== 7923 // 7924 // SlideShow 7925 // Enables slideshow functionality 7926 // 7927 // Example of usage: 7928 // $.fancybox.getInstance().SlideShow.start() 7929 // 7930 // ========================================================================== 7931 (function (document, $) { 7932 "use strict"; 7933 7934 $.extend(true, $.fancybox.defaults, { 7935 btnTpl: { 7936 slideShow: '<button data-fancybox-play class="fancybox-button fancybox-button--play" title="{{PLAY_START}}">' + 7937 '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.5 5.4v13.2l11-6.6z"/></svg>' + 7938 '<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>' + 7939 "</button>" 7940 }, 7941 slideShow: { 7942 autoStart: false, 7943 speed: 3000, 7944 progress: true 7945 } 7946 }); 7947 7948 var SlideShow = function (instance) { 7949 this.instance = instance; 7950 this.init(); 7951 }; 7952 7953 $.extend(SlideShow.prototype, { 7954 timer: null, 7955 isActive: false, 7956 $button: null, 7957 7958 init: function () { 7959 var self = this, 7960 instance = self.instance, 7961 opts = instance.group[instance.currIndex].opts.slideShow; 7962 7963 self.$button = instance.$refs.toolbar.find("[data-fancybox-play]").on("click", function () { 7964 self.toggle(); 7965 }); 7966 7967 if (instance.group.length < 2 || !opts) { 7968 self.$button.hide(); 7969 } else if (opts.progress) { 7970 self.$progress = $('<div class="fancybox-progress"></div>').appendTo(instance.$refs.inner); 7971 } 7972 }, 7973 7974 set: function (force) { 7975 var self = this, 7976 instance = self.instance, 7977 current = instance.current; 7978 7979 // Check if reached last element 7980 if (current && (force === true || current.opts.loop || instance.currIndex < instance.group.length - 1)) { 7981 if (self.isActive && current.contentType !== "video") { 7982 if (self.$progress) { 7983 $.fancybox.animate(self.$progress.show(), { 7984 scaleX: 1 7985 }, current.opts.slideShow.speed); 7986 } 7987 7988 self.timer = setTimeout(function () { 7989 if (!instance.current.opts.loop && instance.current.index == instance.group.length - 1) { 7990 instance.jumpTo(0); 7991 } else { 7992 instance.next(); 7993 } 7994 }, current.opts.slideShow.speed); 7995 } 7996 } else { 7997 self.stop(); 7998 instance.idleSecondsCounter = 0; 7999 instance.showControls(); 8000 } 8001 }, 8002 8003 clear: function () { 8004 var self = this; 8005 8006 clearTimeout(self.timer); 8007 8008 self.timer = null; 8009 8010 if (self.$progress) { 8011 self.$progress.removeAttr("style").hide(); 8012 } 8013 }, 8014 8015 start: function () { 8016 var self = this, 8017 current = self.instance.current; 8018 8019 if (current) { 8020 self.$button 8021 .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_STOP) 8022 .removeClass("fancybox-button--play") 8023 .addClass("fancybox-button--pause"); 8024 8025 self.isActive = true; 8026 8027 if (current.isComplete) { 8028 self.set(true); 8029 } 8030 8031 self.instance.trigger("onSlideShowChange", true); 8032 } 8033 }, 8034 8035 stop: function () { 8036 var self = this, 8037 current = self.instance.current; 8038 8039 self.clear(); 8040 8041 self.$button 8042 .attr("title", (current.opts.i18n[current.opts.lang] || current.opts.i18n.en).PLAY_START) 8043 .removeClass("fancybox-button--pause") 8044 .addClass("fancybox-button--play"); 8045 8046 self.isActive = false; 8047 8048 self.instance.trigger("onSlideShowChange", false); 8049 8050 if (self.$progress) { 8051 self.$progress.removeAttr("style").hide(); 8052 } 8053 }, 8054 8055 toggle: function () { 8056 var self = this; 8057 8058 if (self.isActive) { 8059 self.stop(); 8060 } else { 8061 self.start(); 8062 } 8063 } 8064 }); 8065 8066 $(document).on({ 8067 "onInit.fb": function (e, instance) { 8068 if (instance && !instance.SlideShow) { 8069 instance.SlideShow = new SlideShow(instance); 8070 } 8071 }, 8072 8073 "beforeShow.fb": function (e, instance, current, firstRun) { 8074 var SlideShow = instance && instance.SlideShow; 8075 8076 if (firstRun) { 8077 if (SlideShow && current.opts.slideShow.autoStart) { 8078 SlideShow.start(); 8079 } 8080 } else if (SlideShow && SlideShow.isActive) { 8081 SlideShow.clear(); 8082 } 8083 }, 8084 8085 "afterShow.fb": function (e, instance, current) { 8086 var SlideShow = instance && instance.SlideShow; 8087 8088 if (SlideShow && SlideShow.isActive) { 8089 SlideShow.set(); 8090 } 8091 }, 8092 8093 "afterKeydown.fb": function (e, instance, current, keypress, keycode) { 8094 var SlideShow = instance && instance.SlideShow; 8095 8096 // "P" or Spacebar 8097 if (SlideShow && current.opts.slideShow && (keycode === 80 || keycode === 32) && !$(document.activeElement).is("button,a,input")) { 8098 keypress.preventDefault(); 8099 8100 SlideShow.toggle(); 8101 } 8102 }, 8103 8104 "beforeClose.fb onDeactivate.fb": function (e, instance) { 8105 var SlideShow = instance && instance.SlideShow; 8106 8107 if (SlideShow) { 8108 SlideShow.stop(); 8109 } 8110 } 8111 }); 8112 8113 // Page Visibility API to pause slideshow when window is not active 8114 $(document).on("visibilitychange", function () { 8115 if ( $.fancybox.version != '3.5.7' ) { 8116 return; 8117 } 8118 8119 var instance = $.fancybox.getInstance(), 8120 SlideShow = instance && instance.SlideShow; 8121 8122 if (SlideShow && SlideShow.isActive) { 8123 if (document.hidden) { 8124 SlideShow.clear(); 8125 } else { 8126 SlideShow.set(); 8127 } 8128 } 8129 }); 8130 })(document, jQuery); 8131 // ========================================================================== 8132 // 8133 // FullScreen 8134 // Adds fullscreen functionality 8135 // 8136 // ========================================================================== 8137 (function (document, $) { 8138 "use strict"; 8139 8140 // Collection of methods supported by user browser 8141 var fn = (function () { 8142 var fnMap = [ 8143 ["requestFullscreen", "exitFullscreen", "fullscreenElement", "fullscreenEnabled", "fullscreenchange", "fullscreenerror"], 8144 // new WebKit 8145 [ 8146 "webkitRequestFullscreen", 8147 "webkitExitFullscreen", 8148 "webkitFullscreenElement", 8149 "webkitFullscreenEnabled", 8150 "webkitfullscreenchange", 8151 "webkitfullscreenerror" 8152 ], 8153 // old WebKit (Safari 5.1) 8154 [ 8155 "webkitRequestFullScreen", 8156 "webkitCancelFullScreen", 8157 "webkitCurrentFullScreenElement", 8158 "webkitCancelFullScreen", 8159 "webkitfullscreenchange", 8160 "webkitfullscreenerror" 8161 ], 8162 [ 8163 "mozRequestFullScreen", 8164 "mozCancelFullScreen", 8165 "mozFullScreenElement", 8166 "mozFullScreenEnabled", 8167 "mozfullscreenchange", 8168 "mozfullscreenerror" 8169 ], 8170 ["msRequestFullscreen", "msExitFullscreen", "msFullscreenElement", "msFullscreenEnabled", "MSFullscreenChange", "MSFullscreenError"] 8171 ]; 8172 8173 var ret = {}; 8174 8175 for (var i = 0; i < fnMap.length; i++) { 8176 var val = fnMap[i]; 8177 8178 if (val && val[1] in document) { 8179 for (var j = 0; j < val.length; j++) { 8180 ret[fnMap[0][j]] = val[j]; 8181 } 8182 8183 return ret; 8184 } 8185 } 8186 8187 return false; 8188 })(); 8189 8190 if (fn) { 8191 var FullScreen = { 8192 request: function (elem) { 8193 elem = elem || document.documentElement; 8194 8195 elem[fn.requestFullscreen](elem.ALLOW_KEYBOARD_INPUT); 8196 }, 8197 exit: function () { 8198 document[fn.exitFullscreen](); 8199 }, 8200 toggle: function (elem) { 8201 elem = elem || document.documentElement; 8202 8203 if (this.isFullscreen()) { 8204 this.exit(); 8205 } else { 8206 this.request(elem); 8207 } 8208 }, 8209 isFullscreen: function () { 8210 return Boolean(document[fn.fullscreenElement]); 8211 }, 8212 enabled: function () { 8213 return Boolean(document[fn.fullscreenEnabled]); 8214 } 8215 }; 8216 8217 $.extend(true, $.fancybox.defaults, { 8218 btnTpl: { 8219 fullScreen: '<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fsenter" title="{{FULL_SCREEN}}">' + 8220 '<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>' + 8221 '<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>' + 8222 "</button>" 8223 }, 8224 fullScreen: { 8225 autoStart: false 8226 } 8227 }); 8228 8229 $(document).on(fn.fullscreenchange, function () { 8230 var isFullscreen = FullScreen.isFullscreen(), 8231 instance = $.fancybox.getInstance(); 8232 8233 if (instance) { 8234 // If image is zooming, then force to stop and reposition properly 8235 if (instance.current && instance.current.type === "image" && instance.isAnimating) { 8236 instance.isAnimating = false; 8237 8238 instance.update(true, true, 0); 8239 8240 if (!instance.isComplete) { 8241 instance.complete(); 8242 } 8243 } 8244 8245 instance.trigger("onFullscreenChange", isFullscreen); 8246 8247 instance.$refs.container.toggleClass("fancybox-is-fullscreen", isFullscreen); 8248 8249 instance.$refs.toolbar 8250 .find("[data-fancybox-fullscreen]") 8251 .toggleClass("fancybox-button--fsenter", !isFullscreen) 8252 .toggleClass("fancybox-button--fsexit", isFullscreen); 8253 } 8254 }); 8255 } 8256 8257 $(document).on({ 8258 "onInit.fb": function (e, instance) { 8259 var $container; 8260 8261 if (!fn) { 8262 instance.$refs.toolbar.find("[data-fancybox-fullscreen]").remove(); 8263 8264 return; 8265 } 8266 8267 if (instance && instance.group[instance.currIndex].opts.fullScreen) { 8268 $container = instance.$refs.container; 8269 8270 $container.on("click.fb-fullscreen", "[data-fancybox-fullscreen]", function (e) { 8271 e.stopPropagation(); 8272 e.preventDefault(); 8273 8274 FullScreen.toggle(); 8275 }); 8276 8277 if (instance.opts.fullScreen && instance.opts.fullScreen.autoStart === true) { 8278 FullScreen.request(); 8279 } 8280 8281 // Expose API 8282 instance.FullScreen = FullScreen; 8283 } else if (instance) { 8284 instance.$refs.toolbar.find("[data-fancybox-fullscreen]").hide(); 8285 } 8286 }, 8287 8288 "afterKeydown.fb": function (e, instance, current, keypress, keycode) { 8289 // "F" 8290 if (instance && instance.FullScreen && keycode === 70) { 8291 keypress.preventDefault(); 8292 8293 instance.FullScreen.toggle(); 8294 } 8295 }, 8296 8297 "beforeClose.fb": function (e, instance) { 8298 if (instance && instance.FullScreen && instance.$refs.container.hasClass("fancybox-is-fullscreen")) { 8299 FullScreen.exit(); 8300 } 8301 } 8302 }); 8303 })(document, jQuery); 8304 // ========================================================================== 8305 // 8306 // Thumbs 8307 // Displays thumbnails in a grid 8308 // 8309 // ========================================================================== 8310 (function (document, $) { 8311 "use strict"; 8312 8313 var CLASS = "fancybox-thumbs", 8314 CLASS_ACTIVE = CLASS + "-active"; 8315 8316 // Make sure there are default values 8317 $.fancybox.defaults = $.extend( 8318 true, { 8319 btnTpl: { 8320 thumbs: '<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="{{THUMBS}}">' + 8321 '<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>' + 8322 "</button>" 8323 }, 8324 thumbs: { 8325 autoStart: false, // Display thumbnails on opening 8326 hideOnClose: true, // Hide thumbnail grid when closing animation starts 8327 parentEl: ".fancybox-container", // Container is injected into this element 8328 axis: "y" // Vertical (y) or horizontal (x) scrolling 8329 } 8330 }, 8331 $.fancybox.defaults 8332 ); 8333 8334 var FancyThumbs = function (instance) { 8335 this.init(instance); 8336 }; 8337 8338 $.extend(FancyThumbs.prototype, { 8339 $button: null, 8340 $grid: null, 8341 $list: null, 8342 isVisible: false, 8343 isActive: false, 8344 8345 init: function (instance) { 8346 var self = this, 8347 group = instance.group, 8348 enabled = 0; 8349 8350 self.instance = instance; 8351 self.opts = group[instance.currIndex].opts.thumbs; 8352 8353 instance.Thumbs = self; 8354 8355 self.$button = instance.$refs.toolbar.find("[data-fancybox-thumbs]"); 8356 8357 // Enable thumbs if at least two group items have thumbnails 8358 for (var i = 0, len = group.length; i < len; i++) { 8359 if (group[i].thumb) { 8360 enabled++; 8361 } 8362 8363 if (enabled > 1) { 8364 break; 8365 } 8366 } 8367 8368 if (enabled > 1 && !!self.opts) { 8369 self.$button.removeAttr("style").on("click", function () { 8370 self.toggle(); 8371 }); 8372 8373 self.isActive = true; 8374 } else { 8375 self.$button.hide(); 8376 } 8377 }, 8378 8379 create: function () { 8380 var self = this, 8381 instance = self.instance, 8382 parentEl = self.opts.parentEl, 8383 list = [], 8384 src; 8385 8386 if (!self.$grid) { 8387 // Create main element 8388 self.$grid = $('<div class="' + CLASS + " " + CLASS + "-" + self.opts.axis + '"></div>').appendTo( 8389 instance.$refs.container 8390 .find(parentEl) 8391 .addBack() 8392 .filter(parentEl) 8393 ); 8394 8395 // Add "click" event that performs gallery navigation 8396 self.$grid.on("click", "a", function () { 8397 instance.jumpTo($(this).attr("data-index")); 8398 }); 8399 } 8400 8401 // Build the list 8402 if (!self.$list) { 8403 self.$list = $('<div class="' + CLASS + '__list">').appendTo(self.$grid); 8404 } 8405 8406 $.each(instance.group, function (i, item) { 8407 src = item.thumb; 8408 8409 if (!src && item.type === "image") { 8410 src = item.src; 8411 } 8412 8413 list.push( 8414 '<a href="javascript:;" tabindex="0" data-index="' + 8415 i + 8416 '"' + 8417 (src && src.length ? ' style="background-image:url(' + src + ')"' : 'class="fancybox-thumbs-missing"') + 8418 "></a>" 8419 ); 8420 }); 8421 8422 self.$list[0].innerHTML = list.join(""); 8423 8424 if (self.opts.axis === "x") { 8425 // Set fixed width for list element to enable horizontal scrolling 8426 self.$list.width( 8427 parseInt(self.$grid.css("padding-right"), 10) + 8428 instance.group.length * 8429 self.$list 8430 .children() 8431 .eq(0) 8432 .outerWidth(true) 8433 ); 8434 } 8435 }, 8436 8437 focus: function (duration) { 8438 var self = this, 8439 $list = self.$list, 8440 $grid = self.$grid, 8441 thumb, 8442 thumbPos; 8443 8444 if (!self.instance.current) { 8445 return; 8446 } 8447 8448 thumb = $list 8449 .children() 8450 .removeClass(CLASS_ACTIVE) 8451 .filter('[data-index="' + self.instance.current.index + '"]') 8452 .addClass(CLASS_ACTIVE); 8453 8454 thumbPos = thumb.position(); 8455 8456 // Check if need to scroll to make current thumb visible 8457 if (self.opts.axis === "y" && (thumbPos.top < 0 || thumbPos.top > $list.height() - thumb.outerHeight())) { 8458 $list.stop().animate({ 8459 scrollTop: $list.scrollTop() + thumbPos.top 8460 }, 8461 duration 8462 ); 8463 } else if ( 8464 self.opts.axis === "x" && 8465 (thumbPos.left < $grid.scrollLeft() || thumbPos.left > $grid.scrollLeft() + ($grid.width() - thumb.outerWidth())) 8466 ) { 8467 $list 8468 .parent() 8469 .stop() 8470 .animate({ 8471 scrollLeft: thumbPos.left 8472 }, 8473 duration 8474 ); 8475 } 8476 }, 8477 8478 update: function () { 8479 var that = this; 8480 that.instance.$refs.container.toggleClass("fancybox-show-thumbs", this.isVisible); 8481 8482 if (that.isVisible) { 8483 if (!that.$grid) { 8484 that.create(); 8485 } 8486 8487 that.instance.trigger("onThumbsShow"); 8488 8489 that.focus(0); 8490 } else if (that.$grid) { 8491 that.instance.trigger("onThumbsHide"); 8492 } 8493 8494 // Update content position 8495 that.instance.update(); 8496 }, 8497 8498 hide: function () { 8499 this.isVisible = false; 8500 this.update(); 8501 }, 8502 8503 show: function () { 8504 this.isVisible = true; 8505 this.update(); 8506 }, 8507 8508 toggle: function () { 8509 this.isVisible = !this.isVisible; 8510 this.update(); 8511 } 8512 }); 8513 8514 $(document).on({ 8515 "onInit.fb": function (e, instance) { 8516 var Thumbs; 8517 8518 if (instance && !instance.Thumbs) { 8519 Thumbs = new FancyThumbs(instance); 8520 8521 if (Thumbs.isActive && Thumbs.opts.autoStart === true) { 8522 Thumbs.show(); 8523 } 8524 } 8525 }, 8526 8527 "beforeShow.fb": function (e, instance, item, firstRun) { 8528 var Thumbs = instance && instance.Thumbs; 8529 8530 if (Thumbs && Thumbs.isVisible) { 8531 Thumbs.focus(firstRun ? 0 : 250); 8532 } 8533 }, 8534 8535 "afterKeydown.fb": function (e, instance, current, keypress, keycode) { 8536 var Thumbs = instance && instance.Thumbs; 8537 8538 // "G" 8539 if (Thumbs && Thumbs.isActive && keycode === 71) { 8540 keypress.preventDefault(); 8541 8542 Thumbs.toggle(); 8543 } 8544 }, 8545 8546 "beforeClose.fb": function (e, instance) { 8547 var Thumbs = instance && instance.Thumbs; 8548 8549 if (Thumbs && Thumbs.isVisible && Thumbs.opts.hideOnClose !== false) { 8550 Thumbs.$grid.hide(); 8551 } 8552 } 8553 }); 8554 })(document, jQuery); 8555 //// ========================================================================== 8556 // 8557 // Share 8558 // Displays simple form for sharing current url 8559 // 8560 // ========================================================================== 8561 (function (document, $) { 8562 "use strict"; 8563 8564 $.extend(true, $.fancybox.defaults, { 8565 btnTpl: { 8566 share: '<button data-fancybox-share class="fancybox-button fancybox-button--share" title="{{SHARE}}">' + 8567 '<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>' + 8568 "</button>" 8569 }, 8570 share: { 8571 url: function (instance, item) { 8572 return ( 8573 (!instance.currentHash && !(item.type === "inline" || item.type === "html") ? item.origSrc || item.src : false) || window.location 8574 ); 8575 }, 8576 tpl: '<div class="fancybox-share">' + 8577 "<h1>{{SHARE}}</h1>" + 8578 "<p>" + 8579 '<a class="fancybox-share__button fancybox-share__button--fb" href="https://www.facebook.com/sharer/sharer.php?u={{url}}">' + 8580 '<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>' + 8581 "<span>Facebook</span>" + 8582 "</a>" + 8583 '<a class="fancybox-share__button fancybox-share__button--tw" href="https://twitter.com/intent/tweet?url={{url}}&text={{descr}}">' + 8584 '<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>' + 8585 "<span>Twitter</span>" + 8586 "</a>" + 8587 '<a class="fancybox-share__button fancybox-share__button--pt" href="https://www.pinterest.com/pin/create/button/?url={{url}}&description={{descr}}&media={{media}}">' + 8588 '<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>' + 8589 "<span>Pinterest</span>" + 8590 "</a>" + 8591 "</p>" + 8592 '<p><input class="fancybox-share__input" type="text" value="{{url_raw}}" onclick="select()" /></p>' + 8593 "</div>" 8594 } 8595 }); 8596 8597 function escapeHtml(string) { 8598 var entityMap = { 8599 "&": "&", 8600 "<": "<", 8601 ">": ">", 8602 '"': """, 8603 "'": "'", 8604 "/": "/", 8605 "`": "`", 8606 "=": "=" 8607 }; 8608 8609 return String(string).replace(/[&<>"'`=\/]/g, function (s) { 8610 return entityMap[s]; 8611 }); 8612 } 8613 8614 $(document).on("click", "[data-fancybox-share]", function () { 8615 var instance = $.fancybox.getInstance(), 8616 current = instance.current || null, 8617 url, 8618 tpl; 8619 8620 if (!current) { 8621 return; 8622 } 8623 8624 if ($.type(current.opts.share.url) === "function") { 8625 url = current.opts.share.url.apply(current, [instance, current]); 8626 } 8627 8628 tpl = current.opts.share.tpl 8629 .replace(/\{\{media\}\}/g, current.type === "image" ? encodeURIComponent(current.src) : "") 8630 .replace(/\{\{url\}\}/g, encodeURIComponent(url)) 8631 .replace(/\{\{url_raw\}\}/g, escapeHtml(url)) 8632 .replace(/\{\{descr\}\}/g, instance.$caption ? encodeURIComponent(instance.$caption.text()) : ""); 8633 8634 $.fancybox.open({ 8635 src: instance.translate(instance, tpl), 8636 type: "html", 8637 opts: { 8638 touch: false, 8639 animationEffect: false, 8640 afterLoad: function (shareInstance, shareCurrent) { 8641 // Close self if parent instance is closing 8642 instance.$refs.container.one("beforeClose.fb", function () { 8643 shareInstance.close(null, 0); 8644 }); 8645 8646 // Opening links in a popup window 8647 shareCurrent.$content.find(".fancybox-share__button").on('click',function () { 8648 window.open(this.href, "Share", "width=550, height=450"); 8649 return false; 8650 }); 8651 }, 8652 mobile: { 8653 autoFocus: false 8654 } 8655 } 8656 }); 8657 }); 8658 })(document, jQuery); 8659 // ========================================================================== 8660 // 8661 // Hash 8662 // Enables linking to each modal 8663 // 8664 // ========================================================================== 8665 (function (window, document, $) { 8666 "use strict"; 8667 8668 // Simple $.escapeSelector polyfill (for jQuery prior v3) 8669 if (!$.escapeSelector) { 8670 $.escapeSelector = function (sel) { 8671 var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; 8672 var fcssescape = function (ch, asCodePoint) { 8673 if (asCodePoint) { 8674 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER 8675 if (ch === "\0") { 8676 return "\uFFFD"; 8677 } 8678 8679 // Control characters and (dependent upon position) numbers get escaped as code points 8680 return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " "; 8681 } 8682 8683 // Other potentially-special ASCII characters get backslash-escaped 8684 return "\\" + ch; 8685 }; 8686 8687 return (sel + "").replace(rcssescape, fcssescape); 8688 }; 8689 } 8690 8691 // Get info about gallery name and current index from url 8692 function parseUrl() { 8693 var hash = window.location.hash.substr(1), 8694 rez = hash.split("-"), 8695 index = rez.length > 1 && /^\+?\d+$/.test(rez[rez.length - 1]) ? parseInt(rez.pop(-1), 10) || 1 : 1, 8696 gallery = rez.join("-"); 8697 8698 return { 8699 hash: hash, 8700 /* Index is starting from 1 */ 8701 index: index < 1 ? 1 : index, 8702 gallery: gallery 8703 }; 8704 } 8705 8706 // Trigger click evnt on links to open new fancyBox instance 8707 function triggerFromUrl(url) { 8708 if (url.gallery !== "") { 8709 // If we can find element matching 'data-fancybox' atribute, 8710 // then triggering click event should start fancyBox 8711 $("[data-fancybox='" + $.escapeSelector(url.gallery) + "']") 8712 .eq(url.index - 1) 8713 .focus() 8714 .trigger("click.fb-start"); 8715 } 8716 } 8717 8718 // Get gallery name from current instance 8719 function getGalleryID(instance) { 8720 var opts, ret; 8721 8722 if (!instance) { 8723 return false; 8724 } 8725 8726 opts = instance.current ? instance.current.opts : instance.opts; 8727 ret = opts.hash || (opts.$orig ? opts.$orig.data("fancybox") || opts.$orig.data("fancybox-trigger") : ""); 8728 8729 return ret === "" ? false : ret; 8730 } 8731 8732 // Start when DOM becomes ready 8733 $(function () { 8734 if ( $.fancybox.version != '3.5.7' ) { 8735 return; 8736 } 8737 8738 // Check if user has disabled this module 8739 if ($.fancybox.defaults.hash === false) { 8740 return; 8741 } 8742 8743 // Update hash when opening/closing fancyBox 8744 $(document).on({ 8745 "onInit.fb": function (e, instance) { 8746 var url, gallery; 8747 8748 if (instance.group[instance.currIndex].opts.hash === false) { 8749 return; 8750 } 8751 8752 url = parseUrl(); 8753 gallery = getGalleryID(instance); 8754 8755 // Make sure gallery start index matches index from hash 8756 if (gallery && url.gallery && gallery == url.gallery) { 8757 instance.currIndex = url.index - 1; 8758 } 8759 }, 8760 8761 "beforeShow.fb": function (e, instance, current, firstRun) { 8762 var gallery; 8763 8764 if (!current || current.opts.hash === false) { 8765 return; 8766 } 8767 8768 // Check if need to update window hash 8769 gallery = getGalleryID(instance); 8770 8771 if (!gallery) { 8772 return; 8773 } 8774 8775 // Variable containing last hash value set by fancyBox 8776 // It will be used to determine if fancyBox needs to close after hash change is detected 8777 instance.currentHash = gallery + (instance.group.length > 1 ? "-" + (current.index + 1) : ""); 8778 8779 // If current hash is the same (this instance most likely is opened by hashchange), then do nothing 8780 if (window.location.hash === "#" + instance.currentHash) { 8781 return; 8782 } 8783 8784 if (firstRun && !instance.origHash) { 8785 instance.origHash = window.location.hash; 8786 } 8787 8788 if (instance.hashTimer) { 8789 clearTimeout(instance.hashTimer); 8790 } 8791 8792 // Update hash 8793 instance.hashTimer = setTimeout(function () { 8794 if ("replaceState" in window.history) { 8795 window.history[firstRun ? "pushState" : "replaceState"]({}, 8796 document.title, 8797 window.location.pathname + window.location.search + "#" + instance.currentHash 8798 ); 8799 8800 if (firstRun) { 8801 instance.hasCreatedHistory = true; 8802 } 8803 } else { 8804 window.location.hash = instance.currentHash; 8805 } 8806 8807 instance.hashTimer = null; 8808 }, 300); 8809 }, 8810 8811 "beforeClose.fb": function (e, instance, current) { 8812 if (!current || current.opts.hash === false) { 8813 return; 8814 } 8815 8816 clearTimeout(instance.hashTimer); 8817 8818 // Goto previous history entry 8819 if (instance.currentHash && instance.hasCreatedHistory) { 8820 window.history.back(); 8821 } else if (instance.currentHash) { 8822 if ("replaceState" in window.history) { 8823 window.history.replaceState({}, document.title, window.location.pathname + window.location.search + (instance.origHash || "")); 8824 } else { 8825 window.location.hash = instance.origHash; 8826 } 8827 } 8828 8829 instance.currentHash = null; 8830 } 8831 }); 8832 8833 // Check if need to start/close after url has changed 8834 $(window).on("hashchange.fb", function () { 8835 var url = parseUrl(), 8836 fb = null; 8837 8838 // Find last fancyBox instance that has "hash" 8839 $.each( 8840 $(".fancybox-container") 8841 .get() 8842 .reverse(), 8843 function (index, value) { 8844 var tmp = $(value).data("FancyBox"); 8845 8846 if (tmp && tmp.currentHash) { 8847 fb = tmp; 8848 return false;
8849 } 8850 } 8851 ); 8852 8853 if (fb) { 8854 // Now, compare hash values 8855 if (fb.currentHash !== url.gallery + "-" + url.index && !(url.index === 1 && fb.currentHash == url.gallery)) { 8856 fb.currentHash = null; 8857 8858 fb.close(); 8859 } 8860 } else if (url.gallery !== "") { 8861 triggerFromUrl(url); 8862 } 8863 }); 8864 8865 // Check current hash and trigger click event on matching element to start fancyBox, if needed 8866 setTimeout(function () { 8867 if ( $.fancybox.version != '3.5.7' ) { 8868 return; 8869 } 8870 8871 if (!$.fancybox.getInstance()) { 8872 triggerFromUrl(parseUrl()); 8873 } 8874 }, 50); 8875 }); 8876 })(window, document, jQuery); 8877 // ========================================================================== 8878 // 8879 // Wheel 8880 // Basic mouse weheel support for gallery navigation 8881 // 8882 // ========================================================================== 8883 (function (document, $) { 8884 "use strict"; 8885 8886 var prevTime = new Date().getTime(); 8887 8888 $(document).on({ 8889 "onInit.fb": function (e, instance, current) { 8890 instance.$refs.stage.on("mousewheel DOMMouseScroll wheel MozMousePixelScroll", function (e) { 8891 var current = instance.current, 8892 currTime = new Date().getTime(); 8893 8894 if (instance.group.length < 2 || current.opts.wheel === false || (current.opts.wheel === "auto" && current.type !== "image")) { 8895 return; 8896 } 8897 8898 e.preventDefault(); 8899 e.stopPropagation(); 8900 8901 if (current.$slide.hasClass("fancybox-animated")) { 8902 return; 8903 } 8904 8905 e = e.originalEvent || e; 8906 8907 if (currTime - prevTime < 250) { 8908 return; 8909 } 8910 8911 prevTime = currTime; 8912 8913 instance[(-e.deltaY || -e.deltaX || e.wheelDelta || -e.detail) < 0 ? "next" : "previous"](); 8914 }); 8915 } 8916 }); 8917 })(document, jQuery); 8918 8919; 8920(function ($, window, document, undefined) { 8921 8922 var pluginName = "MegaMenu", 8923 defaults = { 8924 propertyName: "value" 8925 }; 8926 8927 // Set delay time for mouseout 8928 var delayOut = 400; // Default delay time is 200 8929 8930 // the list of menus 8931 var menus = []; 8932 8933 function CustomMenu(element, options) { 8934 this.element = element; 8935 8936 this.options = $.extend({}, defaults, options); 8937 8938 this._defaults = defaults; 8939 this._name = pluginName; 8940 8941 this.init(); 8942 } 8943 8944 CustomMenu.prototype = { 8945 isOpen: false, 8946 timeout: null, 8947 init: function () { 8948 8949 var that = this; 8950 8951 $(this).each(function(index, menu) { 8952 that.node = menu.element; 8953 that.addListeners(menu.element); 8954 8955 var $menu = $(menu.element); 8956 $menu.addClass("dropdownJavascript"); 8957 menus.push(menu.element); 8958 8959 $menu.find('ul > li').each(function(index, submenu) { 8960 if ($(submenu).find('ul').length > 0 ) { 8961 $(submenu).addClass('with-menu'); 8962 } 8963 }); 8964 }); 8965 }, 8966 addListeners: function(menu) { 8967 var that = this; 8968 $(menu).on('mouseover',function(e) { 8969 that.handleMouseOver.call(that, e); 8970 }).on('mouseout',function(e) { 8971 that.handleMouseOut.call(that, e); 8972 }); 8973 }, 8974 handleMouseOver: function (e) { 8975 var that = this; 8976 // clear the timeout 8977 this.clearTimeout(); 8978 8979 // find the parent list item 8980 //var item = ('target' in e ? e.target : e.srcElement); 8981 var item = e.target || e.srcElement; 8982 while (item.nodeName != 'LI' && item != this.node) { 8983 item = item.parentNode; 8984 } 8985 8986 // if the target is within a list item, set the timeout 8987 if (item.nodeName == 'LI') { 8988 this.toOpen = item; 8989 this.timeout = setTimeout(function() { 8990 that.open.call(that); 8991 }, this.options.delay); 8992 } 8993 8994 }, 8995 handleMouseOut: function () { 8996 var that = this; 8997 // clear the timeout 8998 this.clearTimeout(); 8999 9000 // Check mouseout delay overriding 9001 var _delayOut = this.options.delay; 9002 if ( delayOut ) { 9003 _delayOut = delayOut; 9004 } 9005 9006 this.timeout = setTimeout(function() { 9007 that.close.call(that); 9008 }, _delayOut ); 9009 9010 }, 9011 clearTimeout: function () { 9012 9013 // clear the timeout 9014 if (this.timeout) { 9015 clearTimeout(this.timeout); 9016 this.timeout = null; 9017 } 9018 9019 }, 9020 open: function () { 9021 9022 var that = this; 9023 // store that the menu is open 9024 this.isOpen = true; 9025 9026 // loop over the list items with the same parent 9027 var items = $(this.toOpen).parent().children('li'); 9028 $(items).each(function(index, item) { 9029 $(item).find("ul").each(function(index, submenu) { 9030 if (item != that.toOpen) { 9031 // close the submenu 9032 $(item).removeClass("dropdownOpen"); 9033 that.close(item); 9034 9035 } else if (!$(item).hasClass('dropdownOpen')) { 9036 9037 // open the submenu 9038 //if ( !$(item).parents('li').hasClass('has-mega-menu') ) { 9039 $(item).addClass("dropdownOpen"); 9040 //} 9041 9042 9043 // determine the location of the edges of the submenu 9044 var left = 0; 9045 var node = submenu; 9046 while (node) { 9047 //abs is because when you make menus right to left 9048 //the offsetLeft would be negative 9049 left += Math.abs(node.offsetLeft); 9050 node = node.offsetParent; 9051 } 9052 var right = left + submenu.offsetWidth; 9053 9054 9055 //We should refactor this code to execute only when menu is vertical 9056 var menuHeight = $(submenu).outerHeight(); 9057 var parentTop = $(submenu).offset().top - $(window).scrollTop(); 9058 var totalHeight = menuHeight + parentTop; 9059 var windowHeight = window.innerHeight; 9060 9061 /* if (totalHeight > windowHeight) { 9062 var bestTop = (windowHeight - totalHeight) - 20; 9063 $(submenu).css('margin-top', bestTop + "px"); 9064 }*/ 9065 9066 //remove any previous classes 9067 $(item).removeClass('dropdownRightToLeft'); 9068 9069 // move the submenu to the right of the item if appropriate 9070 if (left < 0) $(item).addClass('dropdownLeftToRight'); 9071 9072 // move the submenu to the left of the item if appropriate 9073 if (right > document.body.clientWidth) { 9074 $(item).addClass('dropdownRightToLeft'); 9075 } 9076 9077 } 9078 }); 9079 }); 9080 9081 }, 9082 9083 9084 close: function (node) { 9085 9086 // if no node was specified, close all menus 9087 if (!node) { 9088 this.isOpen = false;
9089 node = this.node; 9090 } 9091 9092 // loop over the items, closing their submenus 9093 $(node).find('li').each(function(index, item) { 9094 $(item).removeClass('dropdownOpen'); 9095 }); 9096 9097 } 9098 }; 9099 9100 $.fn[pluginName] = function (options) { 9101 return this.each(function () { 9102 if (!$.data(this, "plugin_" + pluginName)) { 9103 $.data(this, "plugin_" + pluginName, 9104 new CustomMenu(this, options)); 9105 } 9106 }); 9107 }; 9108 9109})(jQuery, window, document); 9110 9111/*! 9112 * modernizr v3.3.1 9113 * Build https://modernizr.com/download?-cssanimations-csstransitions-touchevents-domprefixes-prefixed-prefixes-setclasses-shiv-testallprops-testprop-teststyles-dontmin 9114 * 9115 * Copyright (c) 9116 * Faruk Ates 9117 * Paul Irish 9118 * Alex Sexton 9119 * Ryan Seddon 9120 * Patrick Kettner 9121 * Stu Cox 9122 * Richard Herrera 9123 9124 * MIT License 9125 */ 9126 9127/* 9128 * Modernizr tests which native CSS3 and HTML5 features are available in the 9129 * current UA and makes the results available to you in two ways: as properties on 9130 * a global `Modernizr` object, and as classes on the `<html>` element. This 9131 * information allows you to progressively enhance your pages with a granular level 9132 * of control over the experience. 9133*/ 9134 9135;(function(window, document, undefined){ 9136 var classes = []; 9137 9138 9139 var tests = []; 9140 9141 9142 /** 9143 * 9144 * ModernizrProto is the constructor for Modernizr 9145 * 9146 * @class 9147 * @access public 9148 */ 9149 9150 var ModernizrProto = { 9151 // The current version, dummy 9152 _version: '3.3.1', 9153 9154 // Any settings that don't work as separate modules 9155 // can go in here as configuration. 9156 _config: { 9157 'classPrefix': '', 9158 'enableClasses': true, 9159 'enableJSClass': true, 9160 'usePrefixes': true 9161 }, 9162 9163 // Queue of tests 9164 _q: [], 9165 9166 // Stub these for people who are listening 9167 on: function(test, cb) { 9168 // I don't really think people should do this, but we can 9169 // safe guard it a bit. 9170 // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests. 9171 // This is in case people listen to synchronous tests. I would leave it out, 9172 // but the code to *disallow* sync tests in the real version of this 9173 // function is actually larger than this. 9174 var self = this; 9175 setTimeout(function() { 9176 cb(self[test]); 9177 }, 0); 9178 }, 9179 9180 addTest: function(name, fn, options) { 9181 tests.push({name: name, fn: fn, options: options}); 9182 }, 9183 9184 addAsyncTest: function(fn) { 9185 tests.push({name: null, fn: fn}); 9186 } 9187 }; 9188 9189 9190 9191 // Fake some of Object.create so we can force non test results to be non "own" properties. 9192 var Modernizr = function() {}; 9193 Modernizr.prototype = ModernizrProto; 9194 9195 // Leak modernizr globally when you `require` it rather than force it here. 9196 // Overwrite name so constructor name is nicer :D 9197 Modernizr = new Modernizr(); 9198 9199 9200 9201 /** 9202 * List of property values to set for css tests. See ticket #21 9203 * http://git.io/vUGl4 9204 * 9205 * @memberof Modernizr 9206 * @name Modernizr._prefixes 9207 * @optionName Modernizr._prefixes 9208 * @optionProp prefixes 9209 * @access public 9210 * @example 9211 * 9212 * Modernizr._prefixes is the internal list of prefixes that we test against 9213 * inside of things like [prefixed](#modernizr-prefixed) and [prefixedCSS](#-code-modernizr-prefixedcss). It is simply 9214 * an array of kebab-case vendor prefixes you can use within your code. 9215 * 9216 * Some common use cases include 9217 * 9218 * Generating all possible prefixed version of a CSS property 9219 * ```js 9220 * var rule = Modernizr._prefixes.join('transform: rotate(20deg); '); 9221 * 9222 * rule === 'transform: rotate(20deg); webkit-transform: rotate(20deg); moz-transform: rotate(20deg); o-transform: rotate(20deg); ms-transform: rotate(20deg);' 9223 * ``` 9224 * 9225 * Generating all possible prefixed version of a CSS value 9226 * ```js 9227 * rule = 'display:' + Modernizr._prefixes.join('flex; display:') + 'flex'; 9228 * 9229 * rule === 'display:flex; display:-webkit-flex; display:-moz-flex; display:-o-flex; display:-ms-flex; display:flex' 9230 * ``` 9231 */ 9232 9233 // we use ['',''] rather than an empty array in order to allow a pattern of .`join()`ing prefixes to test 9234 // values in feature detects to continue to work 9235 var prefixes = (ModernizrProto._config.usePrefixes ? ' -webkit- -moz- -o- -ms- '.split(' ') : ['','']); 9236 9237 // expose these for the plugin API. Look in the source for how to join() them against your input 9238 ModernizrProto._prefixes = prefixes; 9239 9240 9241 9242 /** 9243 * is returns a boolean if the typeof an obj is exactly type. 9244 * 9245 * @access private 9246 * @function is 9247 * @param {*} obj - A thing we want to check the type of 9248 * @param {string} type - A string to compare the typeof against 9249 * @returns {boolean} 9250 */ 9251 9252 function is(obj, type) { 9253 return typeof obj === type; 9254 } 9255 ; 9256 9257 /** 9258 * Run through all tests and detect their support in the current UA. 9259 * 9260 * @access private 9261 */ 9262 9263 function testRunner() { 9264 var featureNames; 9265 var feature; 9266 var aliasIdx; 9267 var result; 9268 var nameIdx; 9269 var featureName; 9270 var featureNameSplit; 9271 9272 for (var featureIdx in tests) { 9273 if (tests.hasOwnProperty(featureIdx)) { 9274 featureNames = []; 9275 feature = tests[featureIdx]; 9276 // run the test, throw the return value into the Modernizr, 9277 // then based on that boolean, define an appropriate className 9278 // and push it into an array of classes we'll join later. 9279 // 9280 // If there is no name, it's an 'async' test that is run, 9281 // but not directly added to the object. That should 9282 // be done with a post-run addTest call. 9283 if (feature.name) { 9284 featureNames.push(feature.name.toLowerCase()); 9285 9286 if (feature.options && feature.options.aliases && feature.options.aliases.length) { 9287 // Add all the aliases into the names list 9288 for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) { 9289 featureNames.push(feature.options.aliases[aliasIdx].toLowerCase()); 9290 } 9291 } 9292 } 9293 9294 // Run the test, or use the raw value if it's not a function 9295 result = is(feature.fn, 'function') ? feature.fn() : feature.fn; 9296 9297 9298 // Set each of the names on the Modernizr object 9299 for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) { 9300 featureName = featureNames[nameIdx];
9301 // Support dot properties as sub tests. We don't do checking to make sure 9302 // that the implied parent tests have been added. You must call them in 9303 // order (either in the test, or make the parent test a dependency). 9304 // 9305 // Cap it to TWO to make the logic simple and because who needs that kind of subtesting 9306 // hashtag famous last words 9307 featureNameSplit = featureName.split('.'); 9308 9309 if (featureNameSplit.length === 1) { 9310 Modernizr[featureNameSplit[0]] = result; 9311 } else { 9312 // cast to a Boolean, if not one already 9313 /* jshint -W053 */ 9314 if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) { 9315 Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]); 9316 } 9317 9318 Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result; 9319 } 9320 9321 classes.push((result ? '' : 'no-') + featureNameSplit.join('-')); 9322 } 9323 } 9324 } 9325 } 9326 ; 9327 9328 /** 9329 * docElement is a convenience wrapper to grab the root element of the document 9330 * 9331 * @access private 9332 * @returns {HTMLElement|SVGElement} The root element of the document 9333 */ 9334 9335 var docElement = document.documentElement; 9336 9337 9338 /** 9339 * A convenience helper to check if the document we are running in is an SVG document 9340 * 9341 * @access private 9342 * @returns {boolean} 9343 */ 9344 9345 var isSVG = docElement.nodeName.toLowerCase() === 'svg'; 9346 9347 9348 /** 9349 * setClasses takes an array of class names and adds them to the root element 9350 * 9351 * @access private 9352 * @function setClasses 9353 * @param {string[]} classes - Array of class names 9354 */ 9355 9356 // Pass in an and array of class names, e.g.:
9357 // ['no-webp', 'borderradius', ...] 9358 function setClasses(classes) { 9359 var className = docElement.className; 9360 var classPrefix = Modernizr._config.classPrefix || ''; 9361 9362 if (isSVG) { 9363 className = className.baseVal; 9364 } 9365 9366 // Change `no-js` to `js` (independently of the `enableClasses` option) 9367 // Handle classPrefix on this too 9368 if (Modernizr._config.enableJSClass) { 9369 var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)'); 9370 className = className.replace(reJS, '$1' + classPrefix + 'js$2'); 9371 } 9372 9373 if (Modernizr._config.enableClasses) { 9374 // Add the new classes 9375 className += ' ' + classPrefix + classes.join(' ' + classPrefix); 9376 isSVG ? docElement.className.baseVal = className : docElement.className = className; 9377 } 9378 9379 } 9380 9381 ; 9382 9383/** 9384 * @optionName html5shiv 9385 * @optionProp html5shiv 9386 */ 9387 9388 // Take the html5 variable out of the html5shiv scope so we can return it. 9389 var html5; 9390 if (!isSVG) { 9391 /** 9392 * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 9393 */ 9394 ;(function(window, document) { 9395 /*jshint evil:true */ 9396 /** version */ 9397 var version = '3.7.3'; 9398 9399 /** Preset options */ 9400 var options = window.html5 || {}; 9401 9402 /** Used to skip problem elements */ 9403 var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 9404 9405 /** Not all elements can be cloned in IE **/ 9406 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; 9407 9408 /** Detect whether the browser supports default html5 styles */ 9409 var supportsHtml5Styles; 9410 9411 /** Name of the expando, to work with multiple documents or to re-shiv one document */ 9412 var expando = '_html5shiv'; 9413 9414 /** The id for the the documents expando */ 9415 var expanID = 0; 9416 9417 /** Cached data for each document */ 9418 var expandoData = {}; 9419 9420 /** Detect whether the browser supports unknown elements */ 9421 var supportsUnknownElements; 9422 9423 (function() { 9424 try { 9425 var a = document.createElement('a'); 9426 a.innerHTML = '<xyz></xyz>'; 9427 //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles 9428 supportsHtml5Styles = ('hidden' in a); 9429 9430 supportsUnknownElements = a.childNodes.length == 1 || (function() { 9431 // assign a false positive if unable to shiv 9432 (document.createElement)('a'); 9433 var frag = document.createDocumentFragment(); 9434 return ( 9435 typeof frag.cloneNode == 'undefined' || 9436 typeof frag.createDocumentFragment == 'undefined' || 9437 typeof frag.createElement == 'undefined' 9438 ); 9439 }()); 9440 } catch(e) { 9441 // assign a false positive if detection fails => unable to shiv 9442 supportsHtml5Styles = true; 9443 supportsUnknownElements = true; 9444 } 9445 9446 }()); 9447 9448 /*--------------------------------------------------------------------------*/ 9449 9450 /** 9451 * Creates a style sheet with the given CSS text and adds it to the document. 9452 * @private 9453 * @param {Document} ownerDocument The document. 9454 * @param {String} cssText The CSS text. 9455 * @returns {StyleSheet} The style element. 9456 */ 9457 function addStyleSheet(ownerDocument, cssText) { 9458 var p = ownerDocument.createElement('p'), 9459 parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 9460 9461 p.innerHTML = 'x<style>' + cssText + '</style>'; 9462 return parent.insertBefore(p.lastChild, parent.firstChild); 9463 } 9464 9465 /** 9466 * Returns the value of `html5.elements` as an array. 9467 * @private 9468 * @returns {Array} An array of shived element node names. 9469 */ 9470 function getElements() { 9471 var elements = html5.elements; 9472 return typeof elements == 'string' ? elements.split(' ') : elements; 9473 } 9474 9475 /** 9476 * Extends the built-in list of html5 elements 9477 * @memberOf html5 9478 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv 9479 * @param {Document} ownerDocument The context document. 9480 */ 9481 function addElements(newElements, ownerDocument) { 9482 var elements = html5.elements; 9483 if(typeof elements != 'string'){ 9484 elements = elements.join(' '); 9485 } 9486 if(typeof newElements != 'string'){ 9487 newElements = newElements.join(' '); 9488 } 9489 html5.elements = elements +' '+ newElements; 9490 shivDocument(ownerDocument); 9491 } 9492 9493 /** 9494 * Returns the data associated to the given document 9495 * @private 9496 * @param {Document} ownerDocument The document. 9497 * @returns {Object} An object of data. 9498 */ 9499 function getExpandoData(ownerDocument) { 9500 var data = expandoData[ownerDocument[expando]]; 9501 if (!data) { 9502 data = {}; 9503 expanID++; 9504 ownerDocument[expando] = expanID; 9505 expandoData[expanID] = data; 9506 } 9507 return data; 9508 } 9509 9510 /** 9511 * returns a shived element for the given nodeName and document 9512 * @memberOf html5 9513 * @param {String}
vendor: 4,418 bytes, lines 9513-9624
9513 nodeName name of the element 9514 * @param {Document|DocumentFragment} ownerDocument The context document. 9515 * @returns {Object} The shived element. 9516 */ 9517 function createElement(nodeName, ownerDocument, data){ 9518 if (!ownerDocument) { 9519 ownerDocument = document; 9520 } 9521 if(supportsUnknownElements){ 9522 return ownerDocument.createElement(nodeName); 9523 } 9524 if (!data) { 9525 data = getExpandoData(ownerDocument); 9526 } 9527 var node; 9528 9529 if (data.cache[nodeName]) { 9530 node = data.cache[nodeName].cloneNode(); 9531 } else if (saveClones.test(nodeName)) { 9532 node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 9533 } else { 9534 node = data.createElem(nodeName); 9535 } 9536 9537 // Avoid adding some elements to fragments in IE < 9 because 9538 // * Attributes like `name` or `type` cannot be set/changed once an element 9539 // is inserted into a document/fragment 9540 // * Link elements with `src` attributes that are inaccessible, as with 9541 // a 403 response, will cause the tab/window to crash 9542 // * Script elements appended to fragments will execute when their `src` 9543 // or `text` property is set 9544 return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; 9545 } 9546 9547 /** 9548 * returns a shived DocumentFragment for the given document 9549 * @memberOf html5 9550 * @param {Document} ownerDocument The context document. 9551 * @returns {Object} The shived DocumentFragment. 9552 */ 9553 function createDocumentFragment(ownerDocument, data){ 9554 if (!ownerDocument) { 9555 ownerDocument = document; 9556 } 9557 if(supportsUnknownElements){ 9558 return ownerDocument.createDocumentFragment(); 9559 } 9560 data = data || getExpandoData(ownerDocument); 9561 var clone = data.frag.cloneNode(), 9562 i = 0, 9563 elems = getElements(), 9564 l = elems.length; 9565 for(;i<l;i++){ 9566 clone.createElement(elems[i]); 9567 } 9568 return clone; 9569 } 9570 9571 /** 9572 * Shivs the `createElement` and `createDocumentFragment` methods of the document. 9573 * @private 9574 * @param {Document|DocumentFragment} ownerDocument The document. 9575 * @param {Object} data of the document. 9576 */ 9577 function shivMethods(ownerDocument, data) { 9578 if (!data.cache) { 9579 data.cache = {}; 9580 data.createElem = ownerDocument.createElement; 9581 data.createFrag = ownerDocument.createDocumentFragment; 9582 data.frag = data.createFrag(); 9583 } 9584 9585 9586 ownerDocument.createElement = function(nodeName) { 9587 //abort shiv 9588 if (!html5.shivMethods) { 9589 return data.createElem(nodeName); 9590 } 9591 return createElement(nodeName, ownerDocument, data); 9592 }; 9593 9594 ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' + 9595 'var n=f.cloneNode(),c=n.createElement;' + 9596 'h.shivMethods&&(' + 9597 // unroll the `createElement` calls 9598 getElements().join().replace(/[\w\-:]+/g, function(nodeName) { 9599 data.createElem(nodeName); 9600 data.frag.createElement(nodeName); 9601 return 'c("' + nodeName + '")'; 9602 }) + 9603 ');return n}' 9604 )(html5, data.frag); 9605 } 9606 9607 /*--------------------------------------------------------------------------*/ 9608 9609 /** 9610 * Shivs the given document. 9611 * @memberOf html5 9612 * @param {Document} ownerDocument The document to shiv. 9613 * @returns {Document} The shived document. 9614 */ 9615 function shivDocument(ownerDocument) { 9616 if (!ownerDocument) { 9617 ownerDocument = document; 9618 } 9619 var data = getExpandoData(ownerDocument); 9620 9621 if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) { 9622 data.hasCSS = !!addStyleSheet(ownerDocument, 9623 // corrects block display not defined in IE6/7/8/9 9624 'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{dis
9624play:block}' + 9625 // adds styling not present in IE6/7/8/9 9626 'mark{background:#FF0;color:#000}' + 9627 // hides non-rendered elements 9628 'template{display:none}' 9629 ); 9630 } 9631 if (!supportsUnknownElements) { 9632 shivMethods(ownerDocument, data); 9633 } 9634 return ownerDocument; 9635 } 9636 9637 /*--------------------------------------------------------------------------*/ 9638 9639 /** 9640 * The `html5` object is exposed so that more elements can be shived and 9641 * existing shiving can be detected on iframes. 9642 * @type Object 9643 * @example 9644 * 9645 * // options can be changed before the script is included 9646 * html5 = { 'elements': 'mark section', 'shivCSS': false, 'shivMethods': false }; 9647 */ 9648 var html5 = { 9649 9650 /** 9651 * An array or space separated string of node names of the elements to shiv. 9652 * @memberOf html5 9653 * @type Array|String 9654 */ 9655 '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', 9656 9657 /** 9658 * current version of html5shiv 9659 */ 9660 'version': version, 9661 9662 /** 9663 * A flag to indicate that the HTML5 style sheet should be inserted. 9664 * @memberOf html5 9665 * @type Boolean 9666 */ 9667 'shivCSS': (options.shivCSS !== false), 9668 9669 /** 9670 * Is equal to true if a browser supports creating unknown/HTML5 elements 9671 * @memberOf html5 9672 * @type boolean 9673 */ 9674 'supportsUnknownElements': supportsUnknownElements, 9675 9676 /** 9677 * A flag to indicate that the document's `createElement` and `createDocumentFragment` 9678 * methods should be overwritten. 9679 * @memberOf html5 9680 * @type Boolean 9681 */ 9682 'shivMethods': (options.shivMethods !== false), 9683 9684 /** 9685 * A string to describe the type of `html5` object ("default" or "default print"). 9686 * @memberOf html5
9687 * @type String 9688 */ 9689 'type': 'default', 9690 9691 // shivs the document according to the specified `html5` object options 9692 'shivDocument': shivDocument, 9693 9694 //creates a shived element 9695 createElement: createElement, 9696 9697 //creates a shived documentFragment 9698 createDocumentFragment: createDocumentFragment, 9699 9700 //extends list of elements 9701 addElements: addElements 9702 }; 9703 9704 /*--------------------------------------------------------------------------*/ 9705 9706 // expose html5 9707 window.html5 = html5; 9708 9709 // shiv the document 9710 shivDocument(document); 9711 9712 if(typeof module == 'object' && module.exports){ 9713 module.exports = html5; 9714 } 9715 9716 }(typeof window !== "undefined" ? window : this, document)); 9717 } 9718; 9719 9720 /** 9721 * If the browsers follow the spec, then they would expose vendor-specific style as: 9722 * elem.style.WebkitBorderRadius 9723 * instead of something like the following, which would be technically incorrect: 9724 * elem.style.webkitBorderRadius 9725 9726 * Webkit ghosts their properties in lowercase but Opera & Moz do not. 9727 * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ 9728 * erik.eae.net/archives/2008/03/10/21.48.10/ 9729 9730 * More here: github.com/Modernizr/Modernizr/issues/issue/21 9731 * 9732 * @access private 9733 * @returns {string} The string representing the vendor-specific style properties 9734 */ 9735 9736 var omPrefixes = 'Moz O ms Webkit'; 9737 9738 9739 /** 9740 * List of JavaScript DOM values used for tests 9741 * 9742 * @memberof Modernizr 9743 * @name Modernizr._domPrefixes 9744 * @optionName Modernizr._domPrefixes 9745 * @optionProp domPrefixes 9746 * @access public 9747 * @example 9748 * 9749 * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather 9750 * than kebab-case properties, all properties are their Capitalized variant 9751 * 9752 * ```js 9753 * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ]; 9754 * ``` 9755 */ 9756 9757 var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []); 9758 ModernizrProto._domPrefixes = domPrefixes; 9759 9760 9761 /** 9762 * cssToDOM takes a kebab-case string and converts it to camelCase 9763 * e.g. box-sizing -> boxSizing 9764 * 9765 * @access private 9766 * @function cssToDOM 9767 * @param {string} name - String name of kebab-case prop we want to convert 9768 * @returns {string} The camelCase version of the supplied name 9769 */ 9770 9771 function cssToDOM(name) { 9772 return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) { 9773 return m1 + m2.toUpperCase(); 9774 }).replace(/^-/, ''); 9775 } 9776 ; 9777 9778 var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []); 9779 ModernizrProto._cssomPrefixes = cssomPrefixes; 9780 9781 9782 /** 9783 * atRule returns a given CSS property at-rule (eg @keyframes), possibly in 9784 * some prefixed form, or false, in the case of an unsupported rule 9785 * 9786 * @memberof Modernizr 9787 * @name Modernizr.atRule 9788 * @optionName Modernizr.atRule() 9789 * @optionProp atRule 9790 * @access public 9791 * @function atRule 9792 * @param {string} prop - String name of the @-rule to test for 9793 * @returns {string|boolean} The string representing the (possibly prefixed) 9794 * valid version of the @-rule, or `false` when it is unsupported. 9795 * @example 9796 * ```js 9797 * var keyframes = Modernizr.atRule('@keyframes'); 9798 * 9799 * if (keyframes) { 9800 * // keyframes are supported 9801 * // could be `@-webkit-keyframes` or `@keyframes` 9802 * } else { 9803 * // keyframes === `false` 9804 * } 9805 * ``` 9806 * 9807 */ 9808 9809 var atRule = function(prop) { 9810 var length = prefixes.length; 9811 var cssrule = window.CSSRule; 9812 var rule; 9813 9814 if (typeof cssrule === 'undefined') { 9815 return undefined; 9816 } 9817 9818 if (!prop) { 9819 return false; 9820 } 9821 9822 // remove literal @ from beginning of provided property 9823 prop = prop.replace(/^@/, ''); 9824 9825 // CSSRules use underscores instead of dashes 9826 rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE'; 9827 9828 if (rule in cssrule) { 9829 return '@' + prop; 9830 } 9831 9832 for (var i = 0; i < length; i++) { 9833 // prefixes gives us something like -o-, and we want O_ 9834 var prefix = prefixes[i]; 9835 var thisRule = prefix.toUpperCase() + '_' + rule; 9836 9837 if (thisRule in cssrule) { 9838 return '@-' + prefix.toLowerCase() + '-' + prop; 9839 } 9840 } 9841 9842 return false;
9843 }; 9844 9845 ModernizrProto.atRule = atRule; 9846 9847 9848 9849 9850 /** 9851 * contains checks to see if a string contains another string 9852 * 9853 * @access private 9854 * @function contains 9855 * @param {string} str - The string we want to check for substrings 9856 * @param {string} substr - The substring we want to search the first string for 9857 * @returns {boolean} 9858 */ 9859 9860 function contains(str, substr) { 9861 return !!~('' + str).indexOf(substr); 9862 } 9863 9864 ; 9865 9866 /** 9867 * createElement is a convenience wrapper around document.createElement. Since we 9868 * use createElement all over the place, this allows for (slightly) smaller code 9869 * as well as abstracting away issues with creating elements in contexts other than 9870 * HTML documents (e.g. SVG documents). 9871 * 9872 * @access private 9873 * @function createElement 9874 * @returns {HTMLElement|SVGElement} An HTML or SVG element 9875 */ 9876 9877 function createElement() { 9878 if (typeof document.createElement !== 'function') { 9879 // This is the case in IE7, where the type of createElement is "object". 9880 // For this reason, we cannot call apply() as Object is not a Function. 9881 return document.createElement(arguments[0]); 9882 } else if (isSVG) { 9883 return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]); 9884 } else { 9885 return document.createElement.apply(document, arguments); 9886 } 9887 } 9888 9889 ; 9890 9891 /** 9892 * getBody returns the body of a document, or an element that can stand in for 9893 * the body if a real body does not exist 9894 * 9895 * @access private 9896 * @function getBody 9897 * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an 9898 * artificially created element that stands in for the body 9899 */ 9900 9901 function getBody() { 9902 // After page load injecting a fake body doesn't work so check if body exists 9903 var body = document.body; 9904 9905 if (!body) { 9906 // Can't use the real body create a fake one. 9907 body = createElement(isSVG ? 'svg' : 'body'); 9908 body.fake = true; 9909 } 9910 9911 return body; 9912 } 9913 9914 ; 9915 9916 /** 9917 * injectElementWithStyles injects an element with style element and some CSS rules 9918 * 9919 * @access private 9920 * @function injectElementWithStyles 9921 * @param {string} rule - String representing a css rule 9922 * @param {function} callback - A function that is used to test the injected element 9923 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 9924 * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes 9925 * @returns {boolean} 9926 */ 9927 9928 function injectElementWithStyles(rule, callback, nodes, testnames) { 9929 var mod = 'modernizr'; 9930 var style; 9931 var ret; 9932 var node; 9933 var docOverflow; 9934 var div = createElement('div'); 9935 var body = getBody(); 9936 9937 if (parseInt(nodes, 10)) { 9938 // In order not to give false positives we create a node for each test 9939 // This also allows the method to scale for unspecified uses 9940 while (nodes--) { 9941 node = createElement('div'); 9942 node.id = testnames ? testnames[nodes] : mod + (nodes + 1); 9943 div.appendChild(node); 9944 } 9945 } 9946 9947 style = createElement('style'); 9948 style.type = 'text/css'; 9949 style.id = 's' + mod; 9950 9951 // 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. 9952 // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 9953 (!body.fake ? div : body).appendChild(style); 9954 body.appendChild(div); 9955 9956 if (style.styleSheet) { 9957 style.styleSheet.cssText = rule; 9958 } else { 9959 style.appendChild(document.createTextNode(rule)); 9960 } 9961 div.id = mod; 9962 9963 if (body.fake) { 9964 //avoid crashing IE8, if background image is used 9965 body.style.background = ''; 9966 //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible 9967 body.style.overflow = 'hidden'; 9968 docOverflow = docElement.style.overflow; 9969 docElement.style.overflow = 'hidden'; 9970 docElement.appendChild(body); 9971 } 9972 9973 ret = callback(div, rule); 9974 // If this is done after page load we don't want to remove the body so check if body exists 9975 if (body.fake) { 9976 body.parentNode.removeChild(body); 9977 docElement.style.overflow = docOverflow; 9978 // Trigger layout so kinetic scrolling isn't disabled in iOS6+ 9979 docElement.offsetHeight; 9980 } else { 9981 div.parentNode.removeChild(div); 9982 } 9983 9984 return !!ret; 9985 9986 } 9987 9988 ; 9989 9990 /** 9991 * testStyles injects an element with style element and some CSS rules 9992 * 9993 * @memberof Modernizr 9994 * @name Modernizr.testStyles 9995 * @optionName Modernizr.testStyles() 9996 * @optionProp testStyles 9997 * @access public 9998 * @function testStyles 9999 * @param {string} rule - String representing a css rule 10000 * @param {function} callback - A function that is used to test the injected element 10001 * @param {number} [nodes] - An integer representing the number of additional nodes you want injected 10002 * @param {string[]}
10002 [testnames] - An array of strings that are used as ids for the additional nodes 10003 * @returns {boolean} 10004 * @example 10005 * 10006 * `Modernizr.testStyles` takes a CSS rule and injects it onto the current page 10007 * along with (possibly multiple) DOM elements. This lets you check for features 10008 * 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). 10009 * 10010 * ```js 10011 * Modernizr.testStyles('#modernizr { width: 9px; color: papayawhip; }', function(elem, rule) { 10012 * // elem is the first DOM node in the page (by default #modernizr) 10013 * // rule is the first argument you supplied - the CSS rule in string form 10014 * 10015 * addTest('widthworks', elem.style.width === '9px') 10016 * }); 10017 * ``` 10018 * 10019 * If your test requires multiple nodes, you can include a third argument 10020 * indicating how many additional div elements to include on the page. The 10021 * additional nodes are injected as children of the `elem` that is returned as 10022 * the first argument to the callback. 10023 * 10024 * ```js 10025 * Modernizr.testStyles('#modernizr {width: 1px}; #modernizr2 {width: 2px}', function(elem) { 10026 * document.getElementById('modernizr').style.width === '1px'; // true 10027 * document.getElementById('modernizr2').style.width === '2px'; // true 10028 * elem.firstChild === document.getElementById('modernizr2'); // true 10029 * }, 1); 10030 * ``` 10031 * 10032 * By default, all of the additional elements have an ID of `modernizr[n]`, where 10033 * `n` is its index (e.g. the first additional, second overall is `#modernizr2`, 10034 * the second additional is `#modernizr3`, etc.). 10035 * If you want to have more meaningful IDs for your function, you can provide 10036 * them as the fourth argument, as an array of strings 10037 * 10038 * ```js 10039 * Modernizr.testStyles('#foo {width: 10px}; #bar {height: 20px}', function(elem) { 10040 * elem.firstChild === document.getElementById('foo'); // true 10041 * elem.lastChild === document.getElementById('bar'); // true 10042 * }, 2, ['foo', 'bar']); 10043 * ``` 10044 * 10045 */ 10046 10047 var testStyles = ModernizrProto.testStyles = injectElementWithStyles; 10048 10049 10050 /** 10051 * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill. 10052 * 10053 * @access private 10054 * @function fnBind 10055 * @param {function} fn - a function you want to change `this` reference to 10056 * @param {object} that - the `this` you want to call the function with 10057 * @returns {function} The wrapped version of the supplied function 10058 */ 10059 10060 function fnBind(fn, that) { 10061 return function() { 10062 return fn.apply(that, arguments); 10063 }; 10064 } 10065 10066 ; 10067 10068 /** 10069 * testDOMProps is a generic DOM property test; if a browser supports 10070 * a certain property, it won't return undefined for it. 10071 * 10072 * @access private 10073 * @function testDOMProps 10074 * @param {array.<string>} props - An array of properties to test for 10075 * @param {object} obj - An object or Element you want to use to test the parameters again 10076 * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check 10077 */ 10078 function testDOMProps(props, obj, elem) { 10079 var item; 10080 10081 for (var i in props) { 10082 if (props[i] in obj) { 10083 10084 // return the property name as a string 10085 if (elem === false) { 10086 return props[i]; 10087 } 10088 10089 item = obj[props[i]]; 10090 10091 // let's bind a function 10092 if (is(item, 'function')) { 10093 // bind to obj unless overriden 10094 return fnBind(item, elem || obj); 10095 } 10096 10097 // return the unbound function or obj or value 10098 return item; 10099 } 10100 } 10101 return false; 10102 } 10103 10104 ; 10105 10106 /** 10107 * Create our "modernizr" element that we do most feature tests on. 10108 * 10109 * @access private 10110 */ 10111 10112 var modElem = { 10113 elem: createElement('modernizr') 10114 }; 10115 10116 // Clean up this element 10117 Modernizr._q.push(function() { 10118 delete modElem.elem; 10119 }); 10120 10121 10122 10123 var mStyle = { 10124 style: modElem.elem.style 10125 }; 10126 10127 // kill ref for gc, must happen before mod.elem is removed, so we unshift on to 10128 // the front of the queue. 10129 Modernizr._q.unshift(function() { 10130 delete mStyle.style; 10131 }); 10132 10133 10134 10135 /** 10136 * domToCSS takes a camelCase string and converts it to kebab-case 10137 * e.g. boxSizing -> box-sizing 10138 * 10139 * @access private 10140 * @function domToCSS 10141 * @param {string} name - String name of camelCase prop we want to convert 10142 * @returns {string} The kebab-case version of the supplied name 10143 */ 10144 10145 function domToCSS(name) { 10146 return name.replace(/([A-Z])/g, function(str, m1) { 10147 return '-' + m1.toLowerCase(); 10148 }).replace(/^ms-/, '-ms-'); 10149 } 10150 ; 10151 10152 /** 10153 * nativeTestProps allows for us to use native feature detection functionality if available. 10154 * some prefixed form, or false, in the case of an unsupported rule 10155 * 10156 * @access private 10157 * @function nativeTestProps 10158 * @param {array} props - An array of property names 10159 * @param {string} value - A string representing the value we want to check via @supports 10160 * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise 10161 */ 10162 10163 // Accepts a list of property names and a single value 10164 // Returns `undefined` if native detection not available 10165 function nativeTestProps(props, value) { 10166 var i = props.length; 10167 // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface 10168 if ('CSS' in window && 'supports' in window.CSS) { 10169 // Try every prefixed variant of the property 10170 while (i--) { 10171 if (window.CSS.supports(domToCSS(props[i]), value)) { 10172 return true; 10173 } 10174 } 10175 return false;
10176 } 10177 // Otherwise fall back to at-rule (for Opera 12.x) 10178 else if ('CSSSupportsRule' in window) { 10179 // Build a condition string for every prefixed variant 10180 var conditionText = []; 10181 while (i--) { 10182 conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')'); 10183 } 10184 conditionText = conditionText.join(' or '); 10185 return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) { 10186 return getComputedStyle(node, null).position == 'absolute'; 10187 }); 10188 } 10189 return undefined; 10190 } 10191 ; 10192 10193 // testProps is a generic CSS / DOM property test. 10194 10195 // In testing support for a given CSS property, it's legit to test: 10196 // `elem.style[styleName] !== undefined` 10197 // If the property is supported it will return an empty string, 10198 // if unsupported it will return undefined. 10199 10200 // We'll take advantage of this quick test and skip setting a style 10201 // on our modernizr element, but instead just testing undefined vs 10202 // empty string. 10203 10204 // Property names can be provided in either camelCase or kebab-case. 10205 10206 function testProps(props, prefixed, value, skipValueTest) { 10207 skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest; 10208 10209 // Try native detect first 10210 if (!is(value, 'undefined')) { 10211 var result = nativeTestProps(props, value); 10212 if (!is(result, 'undefined')) { 10213 return result; 10214 } 10215 } 10216 10217 // Otherwise do it properly 10218 var afterInit, i, propsLength, prop, before; 10219 10220 // If we don't have a style element, that means we're running async or after 10221 // the core tests, so we'll need to create our own elements to use 10222 10223 // inside of an SVG element, in certain browsers, the `style` element is only 10224 // defined for valid tags. Therefore, if `modernizr` does not have one, we 10225 // fall back to a less used element and hope for the best. 10226 // for strict XHTML browsers the hardly used samp element is used 10227 var elems = ['modernizr', 'tspan', 'samp']; 10228 while (!mStyle.style && elems.length) { 10229 afterInit = true; 10230 mStyle.modElem = createElement(elems.shift()); 10231 mStyle.style = mStyle.modElem.style; 10232 } 10233 10234 // Delete the objects if we created them. 10235 function cleanElems() { 10236 if (afterInit) { 10237 delete mStyle.style; 10238 delete mStyle.modElem; 10239 } 10240 } 10241 10242 propsLength = props.length; 10243 for (i = 0; i < propsLength; i++) { 10244 prop = props[i]; 10245 before = mStyle.style[prop]; 10246 10247 if (contains(prop, '-')) { 10248 prop = cssToDOM(prop); 10249 } 10250 10251 if (mStyle.style[prop] !== undefined) { 10252 10253 // If value to test has been passed in, do a set-and-check test. 10254 // 0 (integer) is a valid property value, so check that `value` isn't 10255 // undefined, rather than just checking it's truthy. 10256 if (!skipValueTest && !is(value, 'undefined')) { 10257 10258 // Needs a try catch block because of old IE. This is slow, but will 10259 // be avoided in most cases because `skipValueTest` will be used. 10260 try { 10261 mStyle.style[prop] = value; 10262 } catch (e) {} 10263 10264 // If the property value has changed, we assume the value used is 10265 // supported. If `value` is empty string, it'll fail here (because 10266 // it hasn't changed), which matches how browsers have implemented 10267 // CSS.supports() 10268 if (mStyle.style[prop] != before) { 10269 cleanElems(); 10270 return prefixed == 'pfx' ? prop : true; 10271 } 10272 } 10273 // Otherwise just return true, or the property name if this is a 10274 // `prefixed()` call 10275 else { 10276 cleanElems(); 10277 return prefixed == 'pfx' ? prop : true; 10278 } 10279 } 10280 } 10281 cleanElems(); 10282 return false; 10283 } 10284 10285 ; 10286 10287 /** 10288 * testProp() investigates whether a given style property is recognized 10289 * Property names can be provided in either camelCase or kebab-case. 10290 * 10291 * @memberof Modernizr 10292 * @name Modernizr.testProp 10293 * @access public 10294 * @optionName Modernizr.testProp() 10295 * @optionProp testProp 10296 * @function testProp 10297 * @param {string} prop - Name of the CSS property to check 10298 * @param {string} [value] - Name of the CSS value to check 10299 * @param {boolean} [useValue] - Whether or not to check the value if @supports isn't supported 10300 * @returns {boolean} 10301 * @example 10302 * 10303 * Just like [testAllProps](#modernizr-testallprops), only it does not check any vendor prefixed 10304 * version of the string. 10305 * 10306 * Note that the property name must be provided in camelCase (e.g. boxSizing not box-sizing) 10307 * 10308 * ```js 10309 * Modernizr.testProp('pointerEvents') // true 10310 * ``` 10311 * 10312 * You can also provide a value as an optional second argument to check if a 10313 * specific value is supported 10314 * 10315 * ```js
10316 * Modernizr.testProp('pointerEvents', 'none') // true 10317 * Modernizr.testProp('pointerEvents', 'penguin') // false 10318 * ``` 10319 */ 10320 10321 var testProp = ModernizrProto.testProp = function(prop, value, useValue) { 10322 return testProps([prop], undefined, value, useValue); 10323 }; 10324 10325 10326 /** 10327 * testPropsAll tests a list of DOM properties we want to check against. 10328 * We specify literally ALL possible (known and/or likely) properties on 10329 * the element including the non-vendor prefixed one, for forward- 10330 * compatibility. 10331 * 10332 * @access private 10333 * @function testPropsAll 10334 * @param {string} prop - A string of the property to test for 10335 * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip 10336 * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against 10337 * @param {string} [value] - A string of a css value 10338 * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set 10339 */ 10340 function testPropsAll(prop, prefixed, elem, value, skipValueTest) { 10341 10342 var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 10343 props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 10344 10345 // did they call .prefixed('boxSizing') or are we just testing a prop? 10346 if (is(prefixed, 'string') || is(prefixed, 'undefined')) { 10347 return testProps(props, prefixed, value, skipValueTest); 10348 10349 // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) 10350 } else { 10351 props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 10352 return testDOMProps(props, prefixed, elem); 10353 } 10354 } 10355 10356 // Modernizr.testAllProps() investigates whether a given style property, 10357 // or any of its vendor-prefixed variants, is recognized 10358 // 10359 // Note that the property names must be provided in the camelCase variant. 10360 // Modernizr.testAllProps('boxSizing') 10361 ModernizrProto.testAllProps = testPropsAll; 10362 10363 10364 10365 /** 10366 * prefixed returns the prefixed or nonprefixed property name variant of your input 10367 * 10368 * @memberof Modernizr 10369 * @name Modernizr.prefixed 10370 * @optionName Modernizr.prefixed() 10371 * @optionProp prefixed 10372 * @access public 10373 * @function prefixed 10374 * @param {string} prop - String name of the property to test for 10375 * @param {object} [obj] - An object to test for the prefixed properties on 10376 * @param {HTMLElement} [elem] - An element used to test specific properties against 10377 * @returns {string|false} The string representing the (possibly prefixed) valid 10378 * version of the property, or `false` when it is unsupported. 10379 * @example 10380 * 10381 * Modernizr.prefixed takes a string css value in the DOM style camelCase (as 10382 * opposed to the css style kebab-case) form and returns the (possibly prefixed) 10383 * version of that property that the browser actually supports. 10384 * 10385 * For example, in older Firefox... 10386 * ```js 10387 * prefixed('boxSizing') 10388 * ``` 10389 * returns 'MozBoxSizing' 10390 * 10391 * In newer Firefox, as well as any other browser that support the unprefixed 10392 * version would simply return `boxSizing`. Any browser that does not support 10393 * the property at all, it will return `false`. 10394 * 10395 * By default, prefixed is checked against a DOM element. If you want to check 10396 * for a property on another object, just pass it as a second argument 10397 * 10398 * ```js 10399 * var rAF = prefixed('requestAnimationFrame', window); 10400 * 10401 * raf(function() { 10402 * renderFunction(); 10403 * }) 10404 * ``` 10405 * 10406 * Note that this will return _the actual function_ - not the name of the function. 10407 * If you need the actual name of the property, pass in `false` as a third argument 10408 * 10409 * ```js 10410 * var rAFProp = prefixed('requestAnimationFrame', window, false); 10411 * 10412 * rafProp === 'WebkitRequestAnimationFrame' // in older webkit 10413 * ``` 10414 * 10415 * One common use case for prefixed is if you're trying to determine which transition 10416 * end event to bind to, you might do something like... 10417 * ```js 10418 * var transEndEventNames = { 10419 * 'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser 10420 * 'MozTransition' : 'transitionend', * only for FF < 15 10421 * 'transition' : 'transitionend' * IE10, Opera, Chrome, FF 15+, Saf 7+ 10422 * }; 10423 * 10424 * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 10425 * ``` 10426 * 10427 * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss). 10428 */ 10429 10430 var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) { 10431 if (prop.indexOf('@') === 0) { 10432 return atRule(prop); 10433 } 10434 10435 if (prop.indexOf('-') != -1) { 10436 // Convert kebab-case to camelCase 10437 prop = cssToDOM(prop); 10438 } 10439 if (!obj) { 10440 return testPropsAll(prop, 'pfx'); 10441 } else { 10442 // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' 10443 return testPropsAll(prop, obj, elem); 10444 } 10445 }; 10446 10447 10448 10449 /** 10450 * testAllProps determines whether a given CSS property is supported in the browser 10451 * 10452 * @memberof Modernizr 10453 * @name Modernizr.testAllProps 10454 * @optionName Modernizr.testAllProps() 10455 * @optionProp testAllProps 10456 * @access public 10457 * @function testAllProps 10458 * @param {string} prop - String naming the property to test (either camelCase or kebab-case) 10459 * @param {string} [value] - String of the value to test 10460 * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection 10461 * @example 10462 * 10463 * testAllProps determines whether a given CSS property, in some prefixed form, 10464 * is supported by the browser. 10465 * 10466 * ```js 10467 * testAllProps('boxSizing') // true 10468 * ``` 10469 * 10470 * It can optionally be given a CSS value in string form to test if a property 10471 * value is valid 10472 * 10473 * ```js 10474 * testAllProps('display', 'block') // true 10475 * testAllProps('display', 'penguin') // false 10476 * ``` 10477 * 10478 * A boolean can be passed as a third parameter to skip the value check when 10479 * native detection (@supports) isn't available. 10480 * 10481 * ```js 10482 * testAllProps('shapeOutside', 'content-box', true); 10483 * ``` 10484 */ 10485 10486 function testAllProps(prop, value, skipValueTest) { 10487 return testPropsAll(prop, undefined, undefined, value, skipValueTest); 10488 } 10489 ModernizrProto.testAllProps = testAllProps; 10490 10491/*! 10492{ 10493 "name": "CSS Animations", 10494 "property": "cssanimations", 10495 "caniuse": "css-animation", 10496 "polyfills": ["transformie", "csssandpaper"], 10497 "tags": ["css"], 10498 "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"], 10499 "notes": [{ 10500 "name" : "Article: 'Dispelling the Android CSS animation myths'", 10501 "href": "https://goo.gl/OGw5Gm" 10502 }] 10503} 10504!*/ 10505/* DOC 10506Detects whether or not elements can be animated using CSS 10507*/ 10508 10509 Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true)); 10510 10511/*! 10512{ 10513 "name": "CSS Transitions", 10514 "property": "csstransitions", 10515 "caniuse": "css-transitions", 10516 "tags": ["css"] 10517} 10518!*/ 10519 10520 Modernizr.addTest('csstransitions', testAllProps('transition', 'all', true)); 10521 10522/*! 10523{ 10524 "name": "Touch Events", 10525 "property": "touchevents", 10526 "caniuse" : "touch", 10527 "tags": ["media", "attribute"], 10528 "notes": [{ 10529 "name": "Touch Events spec", 10530 "href": "https://www.w3.org/TR/2013/WD-touch-events-20130124/" 10531 }], 10532 "warnings": [ 10533 "Indicates if the browser supports the Touch Events spec, and does not necessarily reflect a touchscreen device" 10534 ], 10535 "knownBugs": [ 10536 "False-positive on some configurations of Nokia N900", 10537 "False-positive on some BlackBerry 6.0 builds – https://github.com/Modernizr/Modernizr/issues/372#issuecomment-3112695" 10538 ] 10539} 10540!*/ 10541/* DOC 10542Indicates if the browser supports the W3C Touch Events API. 10543 10544This *does not* necessarily reflect a touchscreen device: 10545 10546* Older touchscreen devices only emulate mouse events 10547* Modern IE touch devices implement the Pointer Events API instead: use `Modernizr.pointerevents` to detect support for that 10548* Some browsers & OS setups may enable touch APIs when no touchscreen is connected 10549* Future browsers may implement other event models for touch interactions 10550 10551See this article: [You Can't Detect A Touchscreen](http://www.stucox.com/blog/you-cant-detect-a-touchscreen/). 10552 10553It's recommended to bind both mouse and touch/pointer events simultaneously – see [this HTML5 Rocks tutorial](http://www.html5rocks.com/en/mobile/touchandmouse/). 10554 10555This test will also return `true` for Firefox 4 Multitouch support. 10556*/ 10557 10558 // Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415 10559 Modernizr.addTest('touchevents', function() { 10560 var bool; 10561 if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 10562 bool = true; 10563 } else { 10564 // include the 'heartz' as a way to have a non matching MQ to help terminate the join 10565 // https://git.io/vznFH 10566 var query = ['@media (', prefixes.join('touch-enabled),('), 'heartz', ')', '{#modernizr{top:9px;position:absolute}}'].join(''); 10567 testStyles(query, function(node) { 10568 bool = node.offsetTop === 9; 10569 }); 10570 } 10571 return bool; 10572 }); 10573 10574 10575 // Run each test 10576 testRunner(); 10577 10578 // Remove the "no-js" class if it exists 10579 setClasses(classes); 10580 10581 delete ModernizrProto.addTest; 10582 delete ModernizrProto.addAsyncTest; 10583 10584 // Run the things that are supposed to run after the tests 10585 for (var i = 0; i < Modernizr._q.length; i++) { 10586 Modernizr._q[i](); 10587 } 10588 10589 // Leak Modernizr namespace 10590 window.Modernizr = Modernizr; 10591 10592 10593; 10594 10595})(window, document); 10596$(function() { 10597 ParallaxScroll.init(); 10598}); 10599 10600// Credit: https://github.com/alumbo/jquery.parallax-scroll 10601var ParallaxScroll = { 10602 /* PUBLIC VARIABLES */ 10603 showLogs: false, 10604 round: 1000, 10605 10606 /* PUBLIC FUNCTIONS */ 10607 init: function() { 10608 this._log("init"); 10609 if (this._inited) { 10610 this._log("Already Inited"); 10611 this._inited = true; 10612 return; 10613 } 10614 this._requestAnimationFrame = (function(){ 10615 return window.requestAnimationFrame || 10616 window.webkitRequestAnimationFrame || 10617 window.mozRequestAnimationFrame || 10618 window.oRequestAnimationFrame || 10619 window.msRequestAnimationFrame || 10620 function(/* function */ callback, /* DOMElement */ element){ 10621 window.setTimeout(callback, 1000 / 60); 10622 }; 10623 })(); 10624 this._onScroll(true); 10625 }, 10626 10627 /* PRIVATE VARIABLES */ 10628 _inited: false, 10629 _properties: ['x', 'y', 'z', 'rotateX', 'rotateY', 'rotateZ', 'scaleX', 'scaleY', 'scaleZ', 'scale'], 10630 _requestAnimationFrame:null, 10631 10632 /* PRIVATE FUNCTIONS */ 10633 _log: function(message) { 10634 if (this.showLogs) console.log("Parallax Scroll / " + message); 10635 }, 10636 _onScroll: function(noSmooth) { 10637 var scroll = $(document).scrollTop(); 10638 var windowHeight = $(window).height(); 10639 this._log("onScroll " + scroll); 10640 $("[data-parallax]").each($.proxy(function(index, el) { 10641 var $el = $(el); 10642 var properties = []; 10643 var applyProperties = false;
10644 var style = $el.data("style"); 10645 if (style == undefined) { 10646 style = $el.attr("style") || ""; 10647 $el.data("style", style); 10648 } 10649 var datas = [$el.data("parallax")]; 10650 var iData; 10651 for(iData = 2; ; iData++) { 10652 if($el.data("parallax"+iData)) { 10653 datas.push($el.data("parallax-"+iData)); 10654 } 10655 else { 10656 break; 10657 } 10658 } 10659 var datasLength = datas.length; 10660 for(iData = 0; iData < datasLength; iData ++) { 10661 var data = datas[iData]; 10662 var scrollFrom = data["from-scroll"]; 10663 if (scrollFrom == undefined) scrollFrom = Math.max(0, $(el).offset().top - windowHeight); 10664 scrollFrom = scrollFrom | 0; 10665 var scrollDistance = data["distance"]; 10666 var scrollTo = data["to-scroll"]; 10667 if (scrollDistance == undefined && scrollTo == undefined) scrollDistance = windowHeight; 10668 scrollDistance = Math.max(scrollDistance | 0, 1); 10669 var easing = data["easing"]; 10670 var easingReturn = data["easing-return"]; 10671 if (easing == undefined || !$.easing|| !$.easing[easing]) easing = null; 10672 if (easingReturn == undefined || !$.easing|| !$.easing[easingReturn]) easingReturn = easing; 10673 if (easing) { 10674 var totalTime = data["duration"]; 10675 if (totalTime == undefined) totalTime = scrollDistance; 10676 totalTime = Math.max(totalTime | 0, 1); 10677 var totalTimeReturn = data["duration-return"]; 10678 if (totalTimeReturn == undefined) totalTimeReturn = totalTime; 10679 scrollDistance = 1; 10680 var currentTime = $el.data("current-time"); 10681 if(currentTime == undefined) currentTime = 0; 10682 } 10683 if (scrollTo == undefined) scrollTo = scrollFrom + scrollDistance; 10684 scrollTo = scrollTo | 0; 10685 var smoothness = data["smoothness"]; 10686 if (smoothness == undefined) smoothness = 30; 10687 smoothness = smoothness | 0; 10688 if (noSmooth || smoothness == 0) smoothness = 1; 10689 smoothness = smoothness | 0; 10690 var scrollCurrent = scroll; 10691 scrollCurrent = Math.max(scrollCurrent, scrollFrom); 10692 scrollCurrent = Math.min(scrollCurrent, scrollTo); 10693 if(easing) { 10694 if($el.data("sens") == undefined) $el.data("sens", "back"); 10695 if(scrollCurrent>scrollFrom) { 10696 if($el.data("sens") == "back") { 10697 currentTime = 1; 10698 $el.data("sens", "go"); 10699 } 10700 else { 10701 currentTime++; 10702 } 10703 } 10704 if(scrollCurrent<scrollTo) { 10705 if($el.data("sens") == "go") { 10706 currentTime = 1; 10707 $el.data("sens", "back"); 10708 } 10709 else { 10710 currentTime++; 10711 } 10712 } 10713 if(noSmooth) currentTime = totalTime; 10714 $el.data("current-time", currentTime); 10715 } 10716 this._properties.map($.proxy(function(prop) { 10717 var defaultProp = 0; 10718 var to = data[prop]; 10719 if (to == undefined) return; 10720 if(prop=="scale" || prop=="scaleX" || prop=="scaleY" || prop=="scaleZ" ) { 10721 defaultProp = 1; 10722 } 10723 else { 10724 to = to | 0; 10725 } 10726 var prev = $el.data("_" + prop); 10727 if (prev == undefined) prev = defaultProp; 10728 var next = ((to-defaultProp) * ((scrollCurrent - scrollFrom) / (scrollTo - scrollFrom))) + defaultProp; 10729 var val = prev + (next - prev) / smoothness; 10730 if(easing && currentTime>0 && currentTime<=totalTime) { 10731 var from = defaultProp; 10732 if($el.data("sens") == "back") { 10733 from = to; 10734 to = -to; 10735 easing = easingReturn; 10736 totalTime = totalTimeReturn; 10737 } 10738 val = $.easing[easing](null, currentTime, from, to, totalTime); 10739 }
10740 val = Math.ceil(val * this.round) / this.round; 10741 if(val==prev&&next==to) val = to; 10742 if(!properties[prop]) properties[prop] = 0; 10743 properties[prop] += val; 10744 if (prev != properties[prop]) { 10745 $el.data("_" + prop, properties[prop]); 10746 applyProperties = true; 10747 } 10748 }, this)); 10749 } 10750 if (applyProperties) { 10751 if (properties["z"] != undefined) { 10752 var perspective = data["perspective"]; 10753 if (perspective == undefined) perspective = 800; 10754 var $parent = $el.parent(); 10755 if(!$parent.data("style")) $parent.data("style", $parent.attr("style") || ""); 10756 $parent.attr("style", "perspective:" + perspective + "px; -webkit-perspective:" + perspective + "px; "+ $parent.data("style")); 10757 } 10758 if(properties["scaleX"] == undefined) properties["scaleX"] = 1; 10759 if(properties["scaleY"] == undefined) properties["scaleY"] = 1; 10760 if(properties["scaleZ"] == undefined) properties["scaleZ"] = 1; 10761 if (properties["scale"] != undefined) { 10762 properties["scaleX"] *= properties["scale"]; 10763 properties["scaleY"] *= properties["scale"]; 10764 properties["scaleZ"] *= properties["scale"]; 10765 } 10766 var translate3d = "translate3d(" + (properties["x"] ? properties["x"] : 0) + "px, " + (properties["y"] ? properties["y"] : 0) + "px, " + (properties["z"] ? properties["z"] : 0) + "px)"; 10767 var rotate3d = "rotateX(" + (properties["rotateX"] ? properties["rotateX"] : 0) + "deg) rotateY(" + (properties["rotateY"] ? properties["rotateY"] : 0) + "deg) rotateZ(" + (properties["rotateZ"] ? properties["rotateZ"] : 0) + "deg)"; 10768 var scale3d = "scaleX(" + properties["scaleX"] + ") scaleY(" + properties["scaleY"] + ") scaleZ(" + properties["scaleZ"] + ")"; 10769 var cssTransform = translate3d + " " + rotate3d + " " + scale3d + ";"; 10770 this._log(cssTransform); 10771 $el.attr("style", "transform:" + cssTransform + " -webkit-transform:" + cssTransform + " " + style); 10772 } 10773 }, this)); 10774 if(window.requestAnimationFrame) { 10775 window.requestAnimationFrame($.proxy(this._onScroll, this, false)); 10776 } 10777 else { 10778 this._requestAnimationFrame($.proxy(this._onScroll, this, false)); 10779 } 10780 } 10781}; 10782; 10783(function($, window, undefined) { 10784 10785 'use strict'; 10786 10787 // global 10788 var Modernizr = window.Modernizr, 10789 $body = $('body'); 10790 10791 $.DLMenu = function(options, element) { 10792 this.$el = $(element); 10793 this._init(options); 10794 }; 10795 10796 $.DLMenu.defaults = { 10797 animationClasses: { 10798 classin: 'mk-vm-animate-in-' + mk_vertical_header_anim, 10799 classout: 'mk-vm-animate-out-' + mk_vertical_header_anim 10800 }, 10801 onLevelClick: function(el, name) { 10802 return false; 10803 }, 10804 onLinkClick: function(el, ev) { 10805 return false; 10806 } 10807 }; 10808 10809 $.DLMenu.prototype = { 10810 _init: function(options) { 10811 10812 this.options = $.extend(true, {}, $.DLMenu.defaults, options); 10813 this._config(); 10814 10815 var animEndEventNames = { 10816 'WebkitAnimation': 'webkitAnimationEnd', 10817 'OAnimation': 'oAnimationEnd', 10818 'msAnimation': 'MSAnimationEnd', 10819 'animation': 'animationend' 10820 }, 10821 transEndEventNames = { 10822 'WebkitTransition': 'webkitTransitionEnd', 10823 'MozTransition': 'transitionend', 10824 'OTransition': 'oTransitionEnd', 10825 'msTransition': 'MSTransitionEnd', 10826 'transition': 'transitionend' 10827 }; 10828 10829 this.animEndEventName = animEndEventNames[Modernizr.prefixed('animation')] + '.dlmenu'; 10830 this.transEndEventName = transEndEventNames[Modernizr.prefixed('transition')] + '.dlmenu'; 10831 10832 this.animEndEventNameUnsufixed = animEndEventNames[Modernizr.prefixed('animation')]; 10833 this.transEndEventNameUnsufixed = transEndEventNames[Modernizr.prefixed('transition')]; 10834 10835 this.supportAnimations = Modernizr.cssanimations; 10836 this.supportTransitions = Modernizr.csstransitions; 10837 10838 this._initEvents(); 10839 10840 }, 10841 _config: function() { 10842 this.open = false;
10843 this.$trigger = this.$el.children('.mk-vm-trigger'); 10844 this.$menu = this.$el.children('ul.mk-vm-menu'); 10845 this.$menuitems = this.$menu.find('li:not(.mk-vm-back)'); 10846 this.$back = this.$menu.find('li.mk-vm-back'); 10847 }, 10848 _initEvents: function() { 10849 10850 var self = this; 10851 10852 $('.mk-vm-menuwrapper a').on('transitionend', function(event) { 10853 event.stopPropagation(); 10854 }); 10855 10856 this.$menuitems.on('click.dlmenu', 'a', function(event) { 10857 10858 // Breaks smooth scroll in vertical menu 10859 // event.stopPropagation(); 10860 10861 var $item = $(event.delegateTarget), 10862 $submenu = $(event.currentTarget).siblings('ul.sub-menu'); 10863 10864 if ($submenu.length > 0) { 10865 var $flyin = $submenu.clone().css('opacity', 0).insertAfter(self.$menu); 10866 var onAnimationEndFn = function() { 10867 var $parent = $item.parents('.mk-vm-subviewopen:first'); 10868 self.$menu.off(self.animEndEventName).removeClass(self.options.animationClasses.classout).addClass('mk-vm-subview'); 10869 $item.addClass('mk-vm-subviewopen') 10870 $parent.removeClass('mk-vm-subviewopen').addClass('mk-vm-subview'); 10871 $flyin.remove(); 10872 10873 // Redraw for firefox issues 10874 var $txt = $item.find('.meni-item-text'); 10875 $txt.css('opacity', 0.99); 10876 setTimeout(function() { $txt.css('opacity', 1) }, 0); 10877 }; 10878 10879 setTimeout(function() { 10880 $flyin.addClass(self.options.animationClasses.classin); 10881 self.$menu.addClass(self.options.animationClasses.classout); 10882 if (self.supportAnimations) { 10883 self.$menu.on(self.animEndEventName, onAnimationEndFn); 10884 } else { 10885 onAnimationEndFn.call(); 10886 } 10887 10888 self.options.onLevelClick($item, $item.children('a:first').text()); 10889 }); 10890 10891 10892 // This caused firefox issue. Test properly if need to bring it back 10893 // if (self.open) { 10894 // self._closeMenu(); 10895 // } else { 10896 // self._openMenu(); 10897 // } 10898 10899 return false; 10900 10901 } else { 10902 self.options.onLinkClick($item, event); 10903 } 10904 10905 }); 10906 10907 10908 // this.$trigger.on('click.dlmenu', function() { 10909 10910 // if (self.open) { 10911 // self._closeMenu(); 10912 // } else { 10913 // // if( ! $(this).hasClass('menu-item-has-children') ) return false; 10914 // self._openMenu(); 10915 // } 10916 // return false; 10917 10918 // }); 10919 10920 this.$back.on('click.dlmenu', function(event) { 10921 10922 var $this = $(this), 10923 $submenu = $this.parents('ul.sub-menu:first'), 10924 $item = $submenu.parent(), 10925 10926 $flyin = $submenu.clone().insertAfter(self.$menu); 10927 10928 var onAnimationEndFn = function() { 10929 self.$menu.off(self.animEndEventName).removeClass(self.options.animationClasses.classin); 10930 $flyin.remove(); 10931 }; 10932 10933 setTimeout(function() { 10934 $flyin.addClass(self.options.animationClasses.classout); 10935 self.$menu.addClass(self.options.animationClasses.classin); 10936 if (self.supportAnimations) { 10937 self.$menu.on(self.animEndEventName, onAnimationEndFn); 10938 } else { 10939 onAnimationEndFn.call(); 10940 } 10941 10942 $item.removeClass('mk-vm-subviewopen'); 10943 10944 var $subview = $this.parents('.mk-vm-subview:first'); 10945 if ($subview.is('li')) { 10946 $subview.addClass('mk-vm-subviewopen'); 10947 } 10948 $subview.removeClass('mk-vm-subview'); 10949 }); 10950 10951 return false; 10952 10953 }); 10954 10955 }, 10956 closeMenu: function() { 10957 if (this.open) { 10958 this._closeMenu(); 10959 } 10960 }, 10961 _closeMenu: function() { 10962 var self = this, 10963 onTransitionEndFn = function() { 10964 self.$menu.off(self.transEndEventName); 10965 self._resetMenu(); 10966 }; 10967 10968 this.$menu.removeClass('mk-vm-menuopen'); 10969 this.$menu.addClass('mk-vm-menu-toggle'); 10970 this.$trigger.removeClass('mk-vm-active'); 10971 10972 if (this.supportTransitions) { 10973 this.$menu.on(this.transEndEventName, onTransitionEndFn); 10974 } else { 10975 onTransitionEndFn.call(); 10976 } 10977 10978 this.open = false; 10979 }, 10980 openMenu: function() { 10981 if (!this.open) { 10982 this._openMenu(); 10983 } 10984 }, 10985 _openMenu: function() { 10986 var self = this; 10987 $body.off('click').on('click.dlmenu', function() { 10988 self._closeMenu(); 10989 }); 10990 this.$menu.addClass('mk-vm-menuopen mk-vm-menu-toggle').on(this.transEndEventName, function() { 10991 $(this).removeClass('mk-vm-menu-toggle'); 10992 }); 10993 this.$trigger.addClass('mk-vm-active'); 10994 this.open = true; 10995 }, 10996 _resetMenu: function() { 10997 this.$menu.removeClass('mk-vm-subview'); 10998 this.$menuitems.removeClass('mk-vm-subview mk-vm-subviewopen'); 10999 } 11000 }; 11001 11002 var logError = function(message) { 11003 if (window.console) { 11004 window.console.error(message); 11005 } 11006 }; 11007 11008 $.fn.dlmenu = function(options) { 11009 if (typeof options === 'string') { 11010 var args = Array.prototype.slice.call(arguments, 1); 11011 this.each(function() { 11012 var instance = $.data(this, 'dlmenu'); 11013 if (!instance) { 11014 logError("cannot call methods on dlmenu prior to initialization; " + 11015 "attempted to call method '" + options + "'"); 11016 return; 11017 } 11018 if (!$.isFunction(instance[options]) || options.charAt(0) === "_") { 11019 logError("no such method '" + options + "' for dlmenu instance"); 11020 return; 11021 } 11022 instance[options].apply(instance, args); 11023 }); 11024 } else { 11025 this.each(function() { 11026 var instance = $.data(this, 'dlmenu'); 11027 if (instance) { 11028 instance._init(); 11029 } else { 11030 instance = $.data(this, 'dlmenu', new $.DLMenu(options, this)); 11031 } 11032 }); 11033 } 11034 return this; 11035 }; 11036 11037})(jQuery, window); 11038( function($) { 11039 'use strict'; 11040 11041 /* 11042 * Define popup / hover states manually to prevent click for IE on touchdevices 11043 */ 11044 11045 $('.mk-main-navigation .menu-item-has-children').children('a').attr('aria-haspopup', 'true'); 11046 $('.animated-column-item').attr('aria-haspopup', 'true'); 11047 11048})( jQuery ); 11049(function($) { 11050 'use strict'; 11051 11052 var Accordion = function(el) { 11053 // Private 11054 var that = this, 11055 $el = $(el), 11056 initial = $el.data('initialindex'), 11057 timeout; 11058 11059 // Public 11060 this.$el = $el; 11061 this.$single = $('.' + this.dom.single, $el); 11062 this.isExpendable = ($el.data('style') === 'toggle-action'); 11063 11064 // Init 11065 this.bindClicks(); 11066 // Reveal initial tab on load event (wait for possible images inside) 11067 $(window).on('load', function() {
11068 if( initial !== -1 ) that.show(that.$single.eq(initial), true) 11069 }); 11070 $(window).on('resize', function() { 11071 clearTimeout(timeout); 11072 timeout = setTimeout(that.bindClicks.bind(that), 500); 11073 }); 11074 } 11075 11076 Accordion.prototype.dom = { 11077 // only class names please! 11078 single : 'mk-accordion-single', 11079 tab : 'mk-accordion-tab', 11080 pane : 'mk-accordion-pane', 11081 current : 'current', 11082 mobileToggle : 'mobile-false', 11083 mobileBreakPoint : 767 11084 } 11085 11086 Accordion.prototype.bindClicks = function() { 11087 // Prevent multiple events binding 11088 this.$single.off('click', '.' + this.dom.tab); 11089 11090 if( !(window.matchMedia('(max-width: ' + this.dom.mobileBreakPoint +'px)').matches 11091 && this.$el.hasClass(this.dom.mobileToggle)) ) { 11092 11093 this.$single.on('click', '.' + this.dom.tab, this.handleEvent.bind(this)); 11094 // When website is loaded in mobile view and resized to desktop 'current' will 11095 // inherit display: none from css. Repair it by calling show() on this element 11096 var $current = $('.' + this.dom.current, this.$el); 11097 if($('.' + this.dom.pane, $current).css('display') === 'none') this.show($current); 11098 } 11099 } 11100 11101 Accordion.prototype.handleEvent = function(e) { 11102 e.preventDefault(); 11103 e.stopPropagation(); 11104 11105 var $single = $(e.delegateTarget); 11106 11107 if(!$single.hasClass(this.dom.current)) { 11108 this.show($single); 11109 } 11110 else { 11111 if(this.isExpendable) this.hide($single); 11112 } 11113 } 11114 11115 Accordion.prototype.hide = function($single) { 11116 $single.removeClass(this.dom.current);
11117 $('.' + this.dom.pane, $single).slideUp(); 11118 } 11119 11120 Accordion.prototype.show = function($single, initial) { 11121 // hide currently opened tab 11122 if(!this.isExpendable) { 11123 var that = this; 11124 this.hide($('.' + this.dom.current, that.$el)); 11125 } 11126 11127 $single.addClass(this.dom.current); 11128 $('.' + this.dom.pane, $single).slideDown('', function() { 11129 if ( initial || ! $(this).parents('.mk-accordion').hasClass('scroll-click') ) { 11130 return; 11131 } 11132 11133 if (typeof $single.prev() === 'undefined') { 11134 $single = $single.prev() 11135 } 11136 11137 window.scrollTo({ top: $single.offset().top - 100, left: 0, behavior: 'smooth' }); 11138 }); 11139 } 11140 11141 11142 11143 // /////////////////////////////////////// 11144 // 11145 // Apply to: 11146 // 11147 // /////////////////////////////////////// 11148 11149 function init() { 11150 $('.mk-accordion').each(function() { 11151 new Accordion(this); 11152 }); 11153 } 11154 11155 init(); 11156 $(window).on('vc_reload', init); 11157 11158})(jQuery); 11159 11160(function($) { 11161 11162 'use strict'; 11163 11164 var SkillDiagram = function( el ) { 11165 this.el = el; 11166 } 11167 11168 SkillDiagram.prototype = { 11169 init : function() { 11170 this.cacheElements(); 11171 this.createDiagram(); 11172 this.$skills.each( this.createSkill.bind( this ) ); 11173 }, 11174 11175 cacheElements : function() { 11176 this.$el = $( this.el ); 11177 this.$skills = this.$el.find( '.mk-meter-arch'); 11178 this.config = this.$el.data(); 11179 this.config.radius = this.config.dimension / 2; 11180 }, 11181 11182 random : function( l, u ) { 11183 return Math.floor( ( Math.random() * ( u - l + 1 ) ) + l ); 11184 }, 11185 11186 createDiagram : function() { 11187 var self = this; 11188 $(this.el).find('svg').remove(); 11189 11190 this.diagram = Raphael( this.el, this.config.dimension, this.config.dimension ); 11191 11192 // Make svg scalable in different screen sizes 11193 this.diagram.setViewBox(0,0,this.config.dimension,this.config.dimension,true); 11194 this.diagram.setSize('90%', '90%'); 11195 11196 this.diagram.circle( this.config.radius, this.config.radius, 80 ).attr({ 11197 stroke: 'none', 11198 fill: this.config.circleColor 11199 }); 11200 11201 // Export title 11202 this.title = this.diagram.text( this.config.radius, this.config.radius, this.config.defaultText ).attr({ 11203 font: "22px helvetica", 11204 fill: this.config.defaultTextColor 11205 }).toFront(); 11206 11207 this.diagram.customAttributes.arc = function(value, color, rad){ 11208 var v = 3.6 * value, 11209 alpha = v == 360 ? 359.99 : v, 11210 r = self.random( 91, 240 ), 11211 a = (r - alpha) * Math.PI/180, 11212 b = r * Math.PI/180, 11213 sx = self.config.radius + rad * Math.cos(b), 11214 sy = self.config.radius - rad * Math.sin(b), 11215 x = self.config.radius + rad * Math.cos(a), 11216 y = self.config.radius - rad * Math.sin(a), 11217 path = [['M', sx, sy], ['A', rad, rad, 0, +(alpha > 180), 1, x, y]]; 11218 11219 return { 11220 path: path, 11221 stroke: color 11222 } 11223 } 11224 }, 11225 11226 createSkill : function( id, el ) { 11227 var self = this, 11228 $this = $( el ), 11229 config = $this.data(), 11230 radMin = 72, 11231 radVal = 27, 11232 newRad = radMin + ( radVal * (id + 1) ); 11233 11234 var $path = this.diagram.path().attr({ 11235 'stroke-width': 28, 11236 arc: [config.percent, config.color, newRad] 11237 }); 11238 11239 $path.mouseover( function() { 11240 self.showSkill( this, config.name, config.percent ); 11241 }).mouseout( function() { 11242 self.hideSkill( this ) 11243 }); 11244 }, 11245 11246 showSkill : function( self, name, percent ) { 11247 var $this = self, 11248 time = 250; 11249 11250 //solves IE problem 11251 if(Raphael.type != 'VML') $this.toFront(); 11252 11253 $this.animate({ 11254 'stroke-width': 50, 11255 'opacity': 0.9, 11256 }, 800, 'elastic' ); 11257 11258 this.title.stop() 11259 .animate({ opacity: 0 }, time, '>', function(){ 11260 this.attr({ text: name + '\n' + percent + '%' }).animate({ opacity: 1 }, time, '<'); 11261 }).toFront(); 11262 }, 11263 11264 hideSkill : function( self ) { 11265 var $this = self, 11266 self = this, 11267 time = 250; 11268 11269 $this.stop().animate({ 11270 'stroke-width': 28, 11271 opacity: 1 11272 }, time * 4, 'elastic' ); 11273 11274 self.title.stop() 11275 .animate({ opacity: 0 }, time, '>', function(){ 11276 self.title.attr({ text: self.config.defaultText }) 11277 .animate({ opacity: 1 }, time, '<'); 11278 }); 11279 } 11280 } 11281
11282 var init = function init() { 11283 if( typeof Raphael === 'undefined' ) return; 11284 $( '.mk-skill-diagram' ).each( function() { 11285 var diagram = new SkillDiagram( this ); 11286 diagram.init(); 11287 }); 11288 } 11289 11290 init(); 11291 $(window).on('vc_reload', init); 11292 11293})(jQuery); 11294 11295/* 11296 * Tab delegation 11297 * Action for modules when we don't have access to chidren DOM on processing templates 11298 * yet we want ass option of opening link in new tab. 11299 * Helpful for use with external widgets like flickr 11300 */ 11301 11302(function($) { 11303 11304 'use strict'; 11305 11306 $( '[data-js="tab-delegation"]' ).each( tabDelegation ); 11307 11308 function tabDelegation() { 11309 var $this = $( this ), 11310 data = $this.data(); 11311 11312 // Create delegation on parent element to affect async loaded children 11313 if( data.tab ) $this.on( 'click', 'a', openInTab ); 11314 } 11315 11316 function openInTab( e ) { 11317 e.preventDefault(); 11318 11319 var $this = $( this ), 11320 url = $this.attr( 'href' ); 11321 11322 window.open( url, '_blank' ); 11323 } 11324 11325})(jQuery); 11326(function($) { 11327 'use strict'; 11328 11329 var init = function init() { 11330 var Toggle = function(el) { 11331 var that = this, 11332 $el = $(el); 11333 11334 this.$el = $el; 11335 $el.on('click', function() { 11336 $el.hasClass('active-toggle') ? that.close() : that.open() 11337 }); 11338 }; 11339 11340 Toggle.prototype.dom = { 11341 pane : 'mk-toggle-pane', 11342 active : 'active-toggle' 11343 }; 11344 11345 Toggle.prototype.open = function() { 11346 var $this = this.$el; 11347 $this.addClass(this.dom.active); 11348 $this.siblings('.' + this.dom.pane).slideDown(200); 11349 }; 11350 11351 Toggle.prototype.close = function() { 11352 var $this = this.$el; 11353 $this.removeClass(this.dom.active); 11354 $this.siblings('.' + this.dom.pane).slideUp(200); 11355 }; 11356 11357 // Apply to. 11358 var $toggle = $('.mk-toggle-title'); 11359 11360 if(!$toggle.length) return; 11361 11362 $toggle.each(function() { 11363 new Toggle(this); 11364 }); 11365 } 11366 $(window).on('load vc_reload', init); 11367 11368})(jQuery); 11369 11370////////////////////////////////////////////////////////////////////////// 11371// 11372// Init all scripts 11373// 11374////////////////////////////////////////////////////////////////////////// 11375 11376// This is bad but we don't have other access to this scope. 11377// Ajax Portfolio is defined as plugin and on success needs these to be reinited 11378// We'll refactor all of this. 11379window.ajaxInit = function() { 11380 mk_lightbox_init(); 11381 mk_click_events(); 11382 mk_social_share_global(); 11383 // mk_social_share(); 11384 mk_gallery(); 11385 loop_audio_init(); 11386}; 11387 11388window.ajaxDelayedInit = function() { 11389 mk_flexslider_init(); 11390 // mk_portfolio_ajax(); 11391}; 11392 11393$(document).ready(function() { 11394 mk_lightbox_init(); 11395 mk_login_form(); 11396 mk_backgrounds_parallax(); 11397 mk_flexslider_init(); 11398 mk_event_countdown(); 11399 mk_skill_meter(); 11400 mk_milestone(); 11401 mk_ajax_search(); 11402 mk_hover_events(); 11403 mk_portfolio_ajax(); 11404 product_loop_add_cart(); 11405 // mk_social_share(); 11406 loop_audio_init(); 11407 mk_portfolio_widget(); 11408 mk_contact_form(); 11409 mk_blog_carousel(); 11410 mk_header_searchform(); 11411 mk_click_events(); 11412 mk_text_typer(); 11413 mk_tab_slider_func(); 11414 mkPositionSidebar(); 11415 11416 $(window).on('load', function() { 11417 mk_unfold_footer(); 11418 mk_tabs(); 11419 mk_accordion_toggles_tooltip(); 11420 mk_gallery(); 11421 mk_theatre_responsive_calculator(); 11422 mk_tabs_responsive(); 11423 mk_start_tour_resize(); 11424 mk_header_social_resize(); 11425 mk_page_section_social_video_bg(); 11426 mk_one_page_scroller(); 11427 11428 setTimeout(function() { 11429 /* 11430 Somehow the values are not correctly updated for the screens 11431 and we need to put setTimeout to fix the issue 11432 */ 11433 mk_mobile_tablet_responsive_calculator(); 11434 }, 300); 11435 11436 console.log("ready for rock"); 11437 }); 11438 11439 11440 var onDebouncedResize = function() { 11441 mk_theatre_responsive_calculator(); 11442 mk_mobile_tablet_responsive_calculator(); 11443 mk_tabs_responsive(); 11444 mk_accordion_toggles_tooltip(); 11445 mk_start_tour_resize(); 11446 mk_header_social_resize(); 11447 11448 setTimeout(function() { 11449 mk_unfold_footer(); 11450 }, 300); 11451 }; 11452 11453 var debounceResize = null; 11454 $(window).on("resize", function() { 11455 if( debounceResize !== null ) { clearTimeout( debounceResize ); } 11456 debounceResize = setTimeout( onDebouncedResize, 300 ); 11457 }); 11458 11459 var onDebouncedScroll = function() { 11460 mk_skill_meter(); 11461 //TODO: Ask to Bart how we can call javascript component 11462 //mk_charts(); 11463 mk_milestone(); 11464 }; 11465 11466 var debounceScroll = null; 11467 $(window).on("scroll", function() { 11468 if( debounceScroll !== null ) { clearTimeout( debounceScroll ); } 11469 debounceScroll = setTimeout( onDebouncedScroll, 100 ); 11470 }); 11471 11472 if (MK.utils.isMobile()) { 11473 $('body').addClass('no-transform'); 11474 } 11475 11476}); 11477 11478/* VC frontend editor */ 11479/* -------------------------------------------------------------------- */ 11480$(window).on("vc_reload",function () { 11481 mk_flexslider_init(); 11482 loop_audio_init(); 11483 mk_tab_slider_func();
11484 mk_event_countdown(); 11485 videoLoadState(); 11486 mk_page_section_social_video_bg(); 11487 mk_hover_events(); 11488 11489 setTimeout(function() { 11490 mkPositionSidebar(); 11491 }, 200); 11492}); 11493 11494// Replace this if new event for remove found. 11495$( document ).on( 'click', '.vc_control-btn-delete', function() { 11496 $( window ).trigger( 'vc_reload' ); 11497} ); 11498 11499$( document ).on( 'sortupdate', '.ui-sortable', function() { 11500 $( window ).trigger( 'vc_reload' ); 11501} ); 11502 11503/* Typer */ 11504/* -------------------------------------------------------------------- */ 11505function mk_text_typer() { 11506 11507 "use strict"; 11508 11509 $('[data-typer-targets]').each(function() { 11510 var that = this; 11511 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.typed.js' ], function() { 11512 var $this = $(that), 11513 $first_string = [$this.text()], 11514 $rest_strings = $this.attr('data-typer-targets').split(','), 11515 $strings = $first_string.concat($rest_strings); 11516 11517 $this.text(''); 11518 11519 $this.typed({ 11520 strings: $strings, 11521 typeSpeed: 30, // typing speed 11522 backDelay: 1200, // pause before backspacing 11523 loop: true, // loop on or off (true or false) 11524 loopCount: false, // number of loops, false = infinite 11525 }); 11526 }); 11527 }); 11528} 11529 11530 11531 11532/* Tab Slider */ 11533/* -------------------------------------------------------------------- */ 11534 11535function mk_tab_slider_func() { 11536 11537 "use strict"; 11538 11539 $('.mk-tab-slider').each(function() { 11540 var that = this; 11541 11542 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.swiper.js' ], function() { 11543 var $this = $(that), 11544 id = $this.data('id'), 11545 $autoplayTime = $this.data('autoplay'), 11546 $content = $('.mk-slider-content'); 11547 11548 var mk_tab_slider = $this.swiper({ 11549 wrapperClass: 'mk-tab-slider-wrapper', 11550 slideClass: 'mk-tab-slider-item', 11551 calculateHeight: true, 11552 speed: 500, 11553 autoplay: isTest ? false : $autoplayTime, 11554 onSlideChangeStart: function() { 11555 $('.mk-tab-slider-nav[data-id="' + id + '"]').find(".active").removeClass('active') 11556 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").eq(mk_tab_slider.activeIndex).addClass('active') 11557 } 11558 }); 11559 11560 // Simple repaint for firefox issue (can't handle 100% height after plugin init) 11561 function repaintFirefox() { 11562 $content.css('display','block'); 11563 setTimeout(function() { 11564 mk_tab_slider.reInit(); 11565 $content.css('display','table'); 11566 },100); 11567 } 11568 11569 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").first().addClass('active'); 11570 11571 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").on('touchstart mousedown', function(e) { 11572 e.preventDefault() 11573 $('.mk-tab-slider-nav[data-id="' + id + '"]').find(".active").removeClass('active') 11574 $(this).addClass('active') 11575 mk_tab_slider.swipeTo($(this).index()) 11576 }); 11577 11578 $('.mk-tab-slider-nav[data-id="' + id + '"]').find("a").on('click',function(e) { 11579 e.preventDefault(); 11580 }); 11581 11582 repaintFirefox(); 11583 $(window).on('resize', repaintFirefox); 11584 }); 11585 11586 }); 11587 11588} 11589 11590 11591 11592/* Edge One Pager */ 11593/* -------------------------------------------------------------------- */ 11594function mk_one_page_scroller() { 11595 11596 "use strict"; 11597 11598 $('.mk-edge-one-pager').each(function() { 11599 var self = this; 11600 11601 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.fullpage.js' ], function() { 11602 11603 var $this = $(self), 11604 anchorArr = []; 11605 11606 $this.find('.section').each(function() { 11607 anchorArr.push($(this).attr('data-title')); 11608 }); 11609 11610 var scrollable = true; 11611 $this.find('.section').each(function() { 11612 var $section = $(this), 11613 $content = $section.find('.edge-slide-content'), 11614 sectionHeight = $section.height(),
11615 contentHeight = $content.innerHeight(); 11616 11617 if((contentHeight + 30) > $(window).height()) { 11618 scrollable = false; 11619 } 11620 }); 11621 11622 if(!scrollable){ 11623 $this.find('.section').each(function() { 11624 var $section = $(this); 11625 $section.addClass('active').css({ 11626 'padding-bottom': '50px' 11627 }); 11628 }); 11629 } 11630 11631 if(scrollable) { 11632 $this.fullpage({ 11633 verticalCentered: false, 11634 resize: true, 11635 slidesColor: ['#ccc', '#fff'], 11636 anchors: anchorArr, 11637 scrollingSpeed: 600, 11638 easing: 'easeInQuart', 11639 menu: false, 11640 navigation: true, 11641 navigationPosition: 'right', 11642 navigationTooltips: false, 11643 slidesNavigation: true, 11644 slidesNavPosition: 'bottom', 11645 loopBottom: false, 11646 loopTop: false, 11647 loopHorizontal: true, 11648 autoScrolling: true, 11649 scrollOverflow: false, 11650 css3: true, 11651 paddingTop: 0, 11652 paddingBottom: 0, 11653 normalScrollElements: '.mk-header, .mk-responsive-wrap', 11654 normalScrollElementTouchThreshold: 5, 11655 keyboardScrolling: true, 11656 touchSensitivity: 15, 11657 continuousVertical: false, 11658 animateAnchor: true, 11659 11660 onLeave: function(index, nextIndex, direction) { 11661 var currentSkin = $this.find('.one-pager-slide').eq(nextIndex - 1).attr('data-header-skin'); 11662 MK.utils.eventManager.publish( 'firstElSkinChange', currentSkin ); 11663 $('#fullPage-nav').removeClass('light-skin dark-skin').addClass(currentSkin + '-skin'); 11664 11665 }, 11666 afterRender: function() { 11667 11668 var $nav = $('#fullPage-nav'); 11669 11670 setTimeout(function() { 11671 var currentSkin = $this.find('.one-pager-slide').eq(0).attr('data-header-skin'); 11672 MK.utils.eventManager.publish( 'firstElSkinChange', currentSkin ); 11673 if($nav.length) $nav.removeClass('light-skin dark-skin').addClass(currentSkin + '-skin'); 11674 }, 300); 11675 11676 var $slide = $this.find('.section'), 11677 headerHeight = MK.val.offsetHeaderHeight(0), 11678 windowHeight = $(window).height(); 11679 11680 $slide.height(windowHeight - headerHeight); 11681 11682 if($nav.length) { 11683 $nav.css({ 11684 'top': 'calc(50% + ' + (headerHeight/2) + 'px)', 11685 'marginTop': 0 11686 }); 11687 11688 var style = $this.attr('data-pagination'); 11689 $nav.addClass('pagination-' + style); 11690 } 11691 11692 setTimeout(mk_one_pager_resposnive, 1000); 11693 }, 11694 afterResize: function() { 11695 var $slide = $this.find('.section'), 11696 headerHeight = MK.val.offsetHeaderHeight(0), 11697 windowHeight = $(window).height(); 11698 11699 $slide.height(windowHeight - headerHeight); 11700 11701 $('#fullPage-nav').css({ 11702 'top': 'calc(50% + ' + (headerHeight/2) + 'px)', 11703 'marginTop': 0 11704 }); 11705 11706 setTimeout(mk_one_pager_resposnive, 1000); 11707 console.log('Reposition pager content.'); 11708 }, 11709 }); 11710 } 11711 11712 // Linking to slides available for desktop and mobile scenarios 11713 function swipeTo(href, e) { 11714 href = '_' + href; // ensure a char before # 11715 if (!~href.indexOf('#')) return; 11716 var section = href.split('#')[1]; 11717 if (~anchorArr.indexOf(section)) { 11718 if (typeof e !== 'undefined') e.preventDefault(); 11719 if (scrollable) $.fn.fullpage.moveTo(section); 11720 else MK.utils.scrollToAnchor('[data-title="'+section+'"]'); 11721 } 11722 } 11723 11724 // onload 11725 var loc = window.location; 11726 if(loc.hash) swipeTo(loc.hash); 11727 11728 $(document).on('click', 'a', function(e) { 11729 var $link = $(e.currentTarget); 11730 swipeTo($link.attr('href'), e); 11731 }); 11732 }); 11733 }); 11734 11735 11736 11737} 11738 11739 11740function mk_one_pager_resposnive() { 11741 "use strict"; 11742 11743 $('.mk-edge-one-pager').each(function() { 11744 var $pager = $(this), 11745 headerHeight = MK.val.offsetHeaderHeight(0), 11746 windowHeight = $(window).height() - headerHeight; 11747 11748 $pager.find('.one-pager-slide').each(function() { 11749 var $slide = $(this), 11750 $content = $slide.find('.edge-slide-content'); 11751 11752 if ($slide.hasClass('left_center') || $slide.hasClass('center_center') || $slide.hasClass('right_center')) { 11753 var contentHeight = $content.height(), 11754 distanceFromTop = (windowHeight - contentHeight) / 2; 11755 11756 distanceFromTop = (distanceFromTop < 50) ? 50 + headerHeight : distanceFromTop; 11757 11758 $content.css('marginTop', distanceFromTop); 11759 } 11760 11761 if ($slide.hasClass('left_bottom') || $slide.hasClass('center_bottom') || $slide.hasClass('right_bottom')) { 11762 var distanceFromTop = windowHeight - $content.height() - 90; 11763 $content.css('marginTop', (distanceFromTop)); 11764 } 11765 }); 11766 11767 /** 11768 * Fix AM-2853 11769 * 11770 * @since 6.0.3 11771 * 11772 * At the init of Edge One Pager (EOP), EOP will render all image 11773 * background of each sections from top to bottom. In this case, 11774 * the page height will be more than screen height and the scroll 11775 * bar will appear. At the same time, the full width row will set 11776 * the container width into 100%. But, after all Slides are set up, 11777 * EOP height will be 100% of the screen height, so the scroll bar 11778 * will be disappeared. It's caused spacing issues on the left and 11779 * right side of the EOP container. To fix this, the row width 11780 * should be resized and row position should be readjusted. 11781 */ 11782 var $row = $pager.parents( '.vc_row.vc_row-fluid.mk-fullwidth-true' ); 11783 11784 // Run only if the Edge One Pager is wrapped inside full width row. 11785 if ( $row.length > 0 ) { 11786 // Set the wrapper and row width. 11787 var $wrapper = $( '.mk-main-wrapper-holder' ); 11788 var $grid = $row.children( '.mk-grid' ); 11789 var rowWidth = $row.width(); // Original width.
11790 var wrapperWidth = $wrapper.width(); // The new width. 11791 11792 // Run only if original width is smaller than the new width. 11793 if ( rowWidth >= wrapperWidth || $grid.length > 0 ) { 11794 return; 11795 } 11796 11797 // Get the content left offset. 11798 var $content = $wrapper.find( '.theme-content' ); 11799 var oriPos = $content.position(); 11800 var oriPadLeft = $content.css( 'padding-left' ); 11801 var oriLeft = parseInt( oriPos.left ) + parseInt( oriPadLeft ); 11802 11803 // Ensure the new width and left position is more than 0. 11804 if ( wrapperWidth <= 0 || oriLeft <= 0 ) { 11805 return; 11806 } 11807 11808 // Resize the width and left position of row full width. 11809 $row.css({ 11810 'width': wrapperWidth, 11811 'left': oriLeft * -1, 11812 }); 11813 } 11814 }); 11815} 11816 11817/* Image Gallery */ 11818/* -------------------------------------------------------------------- */ 11819 11820function mk_gallery() { 11821 11822 "use strict"; 11823 11824 $('.mk-gallery .mk-gallery-item.hover-overlay_layer .item-holder').each(function() { 11825 var itemHolder = $(this), 11826 galleryDesc = itemHolder.find('.gallery-desc'); 11827 11828 function updatePosition() { 11829 var parentHeight = itemHolder.outerHeight(), 11830 contentHeight = galleryDesc.innerHeight(); 11831 11832 var paddingVal = (parentHeight - contentHeight) / 2; 11833 galleryDesc.css({ 11834 'top': paddingVal, 11835 // 'padding-bottom': paddingVal 11836 }); 11837 11838 // console.log(parentHeight); 11839 // console.log(contentHeight); 11840 11841 11842 } 11843 updatePosition(); 11844 11845 $(window).on('resize', function() { 11846 setTimeout(function() { 11847 updatePosition(); 11848 }, 1000); 11849 }); 11850 }); 11851 // Execute hover state for mk gallery item 11852 if ($(window).width() <= 1024) { 11853 $('.mk-gallery .mk-gallery-item').on('click', function (e) { 11854 var clicks = $(this).data('clicks'); 11855 if (clicks) { 11856 // First click 11857 $(this).toggleClass('hover-state'); 11858 } else { 11859 // Second click 11860 $(this).toggleClass('hover-state'); 11861 } 11862 $(this).data("clicks", !clicks); 11863 }); 11864 } 11865} 11866 11867/* Theatre Slider Responsive Calculator */ 11868/* -------------------------------------------------------------------- */ 11869 11870function mk_theatre_responsive_calculator() { 11871 var $laptopContainer = $(".laptop-theatre-slider"); 11872 var $computerContainer = $(".desktop-theatre-slider"); 11873 $laptopContainer.each(function() { 11874 var $this = $(this), 11875 $window = $(window), 11876 $windowWidth = $window.outerWidth(), 11877 $windowHeight = $window.outerHeight(), 11878 $width = $this.outerWidth(), 11879 $height = $this.outerHeight(), 11880 $paddingTop = 38, 11881 $paddingRight = 143, 11882 $paddingBottom = 78, 11883 $paddingLeft = 143; 11884 11885 var $player = $this.find('.player-container'); 11886 11887 if ($windowWidth > $width) { 11888 $player.css({ 11889 'padding-left': parseInt(($width * $paddingLeft) / 1200), 11890 'padding-right': parseInt(($width * $paddingRight) / 1200), 11891 'padding-top': parseInt(($height * $paddingTop) / 690), 11892 'padding-bottom': parseInt(($height * $paddingBottom) / 690), 11893 }); 11894 } 11895 11896 }); 11897 11898 $computerContainer.each(function() { 11899 var $this = $(this), 11900 $window = $(window), 11901 $windowWidth = $window.outerWidth(), 11902 $windowHeight = $window.outerHeight(), 11903 $width = $this.outerWidth(), 11904 $height = $this.outerHeight(), 11905 $paddingTop = 60, 11906 $paddingRight = 52, 11907 $paddingBottom = 290, 11908 $paddingLeft = 49; 11909 11910 var $player = $this.find('.player-container'); 11911 11912 if ($windowWidth > $width) { 11913 $player.css({ 11914 'padding-left': parseInt(($width * $paddingLeft) / 1200), 11915 'padding-right': parseInt(($width * $paddingRight) / 1200), 11916 'padding-top': parseInt(($height * $paddingTop) / 969), 11917 'padding-bottom': parseInt(($height * $paddingBottom) / 969), 11918 }); 11919 } 11920 11921 }); 11922 11923} 11924 11925/* Mobile and Tablet Slideshow Responsive Calculator */ 11926/* -------------------------------------------------------------------- */ 11927function mk_mobile_tablet_responsive_calculator() { 11928 var $laptopSlideshow = $(".mk-laptop-slideshow-shortcode"); 11929 var $lcdSlideshow = $(".mk-lcd-slideshow"); 11930 11931 if ($.exists(".mk-laptop-slideshow-shortcode")) { 11932 $laptopSlideshow.each(function() { 11933 var $this = $(this), 11934 $window = $(window), 11935 $windowWidth = $window.outerWidth(), 11936 $windowHeight = $window.outerHeight(), 11937 $width = $this.outerWidth(), 11938 $height = $this.outerHeight(), 11939 $paddingTop = 28, 11940 $paddingRight = 102, 11941 $paddingBottom = 52, 11942 $paddingLeft = 102; 11943 11944 var $player = $this.find(".slideshow-container"); 11945 11946 $player.css({ 11947 "padding-left": parseInt(($width * $paddingLeft) / 836), 11948 "padding-right": parseInt(($width * $paddingRight) / 836), 11949 "padding-top": parseInt(($height * $paddingTop) / 481), 11950 "padding-bottom": parseInt(($height * $paddingBottom) / 481), 11951 }); 11952 11953 }); 11954 } 11955 11956 if ($.exists(".mk-lcd-slideshow")) {
11957 $lcdSlideshow.each(function() { 11958 var $this = $(this), 11959 $window = $(window), 11960 $windowWidth = $window.outerWidth(), 11961 $windowHeight = $window.outerHeight(), 11962 $width = $this.outerWidth(), 11963 $height = $this.outerHeight(), 11964 $paddingTop = 35, 11965 $paddingRight = 39, 11966 $paddingBottom = 213, 11967 $paddingLeft = 36; 11968 11969 var $player = $this.find(".slideshow-container"); 11970 $player.css({ 11971 "padding-left": parseInt(($width * $paddingLeft) / 886), 11972 "padding-right": parseInt(($width * $paddingRight) / 886), 11973 "padding-top": parseInt(($height * $paddingTop) / 713), 11974 "padding-bottom": parseInt(($height * $paddingBottom) / 713), 11975 }); 11976 }); 11977 } 11978} 11979 11980 11981/* Start a tour resize function */ 11982/* -------------------------------------------------------------------- */ 11983function mk_start_tour_resize() { 11984 11985 $('.mk-header-start-tour').each(function() { 11986 11987 var $windowWidth = $(document).width(), 11988 $this = $(this), 11989 $linkWidth = $this.width() + 15, 11990 $padding = ($windowWidth - mk_responsive_nav_width) / 2; 11991 11992 11993 11994 function updateStartTour(){ 11995 if($windowWidth < mk_responsive_nav_width){ 11996 $this.removeClass('hidden'); 11997 $this.addClass('show'); 11998 }else{ 11999 if($padding < $linkWidth){ 12000 $this.removeClass('show'); 12001 $this.addClass('hidden'); 12002 }else{ 12003 $this.removeClass('hidden'); 12004 $this.addClass('show'); 12005 } 12006 } 12007 } 12008 12009 setTimeout(function() { 12010 updateStartTour(); 12011 }, 300); 12012 }); 12013} 12014 12015/* Header social resize function */ 12016/* -------------------------------------------------------------------- */ 12017function mk_header_social_resize() { 12018 12019 $('.mk-header-social.header-section').each(function() { 12020 12021 var $windowWidth = $(document).width(), 12022 $this = $(this), 12023 $linkWidth = $this.width() + 15, 12024 $padding = ($windowWidth - mk_responsive_nav_width) / 2; 12025 12026 12027 12028 function updateStartTour(){ 12029 if($windowWidth < mk_responsive_nav_width){ 12030 $this.removeClass('hidden'); 12031 $this.addClass('show'); 12032 }else{ 12033 if($padding < $linkWidth){ 12034 $this.removeClass('show'); 12035 $this.addClass('hidden'); 12036 }else{ 12037 $this.removeClass('hidden'); 12038 $this.addClass('show'); 12039 } 12040 } 12041 } 12042 12043 setTimeout(function() { 12044 updateStartTour(); 12045 }, 300); 12046 }); 12047} 12048 12049/* Page Section Socail Video Player Controls */ 12050/* -------------------------------------------------------------------- */ 12051 12052function mk_page_section_social_video_bg() { 12053 $(".mk-page-section.social-hosted").each(function() { 12054 var $container = $(this), 12055 $sound = $container.data('sound'), 12056 $source = $container.data('source'), 12057 player, 12058 timer = 1000; 12059 12060 if ( $( 'body' ).hasClass( '.compose-mode' ) ) { 12061 timer = 2000; 12062 } 12063 12064 if ($source == 'youtube') { 12065 var youtube = $container.find('iframe')[0]; 12066 try { 12067 player = new YT.Player(youtube, { 12068 events: { 12069 'onReady': function () { 12070 player.playVideo(); 12071 if($sound == false) { 12072 player.mute(); 12073 } 12074 } 12075 } 12076 }); 12077 12078 12079 } catch (e) { 12080 console.log( e ); 12081 } 12082 } 12083 if ($source == 'vimeo') { 12084 var vimeo = $container.find('iframe')[0]; 12085 player = $f(vimeo); 12086 setTimeout(function() { 12087 player.api('play'); 12088 if($sound === false) {
12089 player.api('setVolume', 0); 12090 } 12091 }, timer); 12092 } 12093 12094 }); 12095} 12096 12097// Pre RequireJS hot bug fixing 12098 12099function videoLoadState() { 12100 $('.mk-section-video video').each(function() { 12101 var mkVideo = this; 12102 12103 mkVideo.play(); 12104 this.onload = fire(); 12105 12106 function fire() { 12107 setTimeout(function() { 12108 $(mkVideo).animate({ 12109 'opacity': 1 12110 }, 300); 12111 }, 1000); 12112 } 12113 }); 12114} 12115videoLoadState(); 12116 12117 12118// Gmap Widget 12119(function($) { 12120 12121 $(window).on('load vc_reload', initialize); 12122 12123 function initialize() { 12124 var $gmap = $('.gmap_widget'); 12125 if($gmap.length && typeof google !== 'undefined') $gmap.each(run); 12126 } 12127 12128 function run() { 12129 var $mapHolder = $(this); 12130 var myLatlng = new google.maps.LatLng($mapHolder.data('latitude'), $mapHolder.data('longitude')); 12131 var mapOptions = $mapHolder.data('options'); 12132 mapOptions.mapTypeId = google.maps.MapTypeId.ROADMAP; 12133 mapOptions.center = myLatlng; 12134 var map = new google.maps.Map(this, mapOptions); 12135 12136 new google.maps.Marker({ 12137 position: myLatlng, 12138 map: map 12139 }); 12140 } 12141 12142}(jQuery)); 12143 12144// Instagram Widget 12145(function($) { 12146 12147 $(window).on('load', function() { 12148 var $feeds = $('.mk-instagram-feeds'); 12149 if($feeds.length) $feeds.each(run); 12150 }); 12151 12152 function run() { 12153 var options = $(this).data('options'); 12154 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>'; 12155 var feed = new Instafeed(options); 12156 feed.run(); 12157 } 12158}(jQuery)); 12159 12160 12161// Flipbox backface visibility fix for chrome 12162(function($) { 12163 $(window).on('load', function() { 12164 setTimeout( function() { 12165 $('.chrome-flipbox-backface-fix').removeClass('chrome-flipbox-backface-fix'); 12166 }, 300); 12167 }); 12168}(jQuery)); 12169 12170 12171/* Product in VC Tab Bug Fix 12172/* -------------------------------------------------------------------- */ 12173(function($) { 12174 $(window).on('load', function() { 12175 $('.vc_tta-tab a').on('click', function() { 12176 setTimeout( function() { 12177 $(window).trigger('resize'); 12178 }, 100); 12179 }); 12180 }); 12181}(jQuery)); 12182 12183 12184/* Vertical menu fix when childrens exceed screen height 12185/* -------------------------------------------------------------------- */ 12186(function($) { 12187 $(window).on('load', function() { 12188 $('#mk-vm-menu .menu-item-has-children, #mk-vm-menu .mk-vm-back').on('mouseenter', function() { 12189 var $header_inner = $(this).closest('.mk-header-inner'), 12190 $header_inner_height = $header_inner.outerHeight(), 12191 $header_bg = $header_inner.find('.mk-header-bg'), 12192 total_height = 0; 12193 $header_bg.css('height', '100%'); 12194 setTimeout( function() { 12195 $header_inner.children(':visible').each(function() { 12196 total_height += $(this).outerHeight(true); 12197 }); 12198 total_height -= $header_bg.height(); 12199 if ( total_height < $header_inner_height ) { 12200 $header_bg.css('height', '100%'); 12201 } else { 12202 $header_bg.css('height', total_height + 'px'); 12203 } 12204 }, 600); 12205 }); 12206 }); 12207}(jQuery)); 12208 12209 12210/* Woocommerce varitions lightbox fix 12211/* -------------------------------------------------------------------- */ 12212(function($) { 12213 $(window).on('load', function() { 12214 12215 if ( $('.woo-variation-gallery-thumbnail-wrapper').length > 0 ) { 12216 return; 12217 } 12218 12219 var $variations_form = $('.variations_form'); 12220 12221 if ( $variations_form.length ) { 12222 12223 var $varitions_selects = $variations_form.find('.variations').find('.value').find('select'); 12224 $varitions_selects.on('change', function() { 12225 12226 // Woocommerce variations lightbox with galleries 12227 var $all_img_container = $('.mk-product-image .mk-woocommerce-main-image'); 12228 if ( $all_img_container.length ) { 12229 $( $all_img_container ).each( set_lightbox_href ); 12230 } 12231 12232 }); 12233 $varitions_selects.trigger('change'); 12234 12235 } 12236 12237 }); 12238 12239 function set_lightbox_href() { 12240 12241 var $product_img = $( this ).find( 'img' ), 12242 $lightbox = $( this ).find( '.mk-lightbox' ); 12243 12244 setTimeout( function() { 12245 var image_url = $product_img.attr( 'src' ), 12246 image_suffix = image_url.substr( image_url.lastIndexOf('.') - image_url.length ), // Get image suffix 12247 image_url = image_url.slice( 0 , image_url.lastIndexOf('-') ); // Remove image size 12248 $lightbox.attr('href', image_url + image_suffix ); 12249 }, 300); 12250 } 12251 12252}(jQuery)); 12253 12254 12255/* Remove video section when on mobile */ 12256/* -------------------------------------------------------------------- */
12257if( ! MK.utils.showBackgroundVideo ) { 12258 (function($) { 12259 if ( MK.utils.isMobile() ) { 12260 $('.mk-section-video video').remove(); 12261 $('.mk-section-video').addClass('mk-section-video-disable'); 12262 } 12263 }(jQuery)); 12264} 12265 12266 12267/* Yith AJAX Product Filter & Yith Infinite Scrolling Plugin Fix 12268/* -------------------------------------------------------------------- */ 12269(function($) { 12270 $(window).on('load', function() { 12271 12272 $(document).on( 'yith-wcan-ajax-filtered yith_infs_added_elem yith-wcan-ajax-reset-filtered', function(){ 12273 setTimeout( function() { 12274 MK.utils.eventManager.publish('ajaxLoaded'); 12275 MK.core.initAll( document ); 12276 }, 1000 ); 12277 }); 12278 12279 // Fixed YITH Filter plugin causes issue for dropdown sort 12280 $(document).on( 'yith-wcan-ajax-filtered yith-wcan-ajax-reset-filtered', function(){ 12281 setTimeout( function() { 12282 $( '.woocommerce-ordering' ).on( 'change', 'select.orderby', function() { 12283 $( this ).closest( 'form' ).submit(); 12284 }); 12285 }, 1000 ); 12286 }); 12287 12288 }); 12289}(jQuery)); 12290 12291 12292/* Toggle loading state in URL for anchor links. 12293 * - Add a filter to escape meta-chars from hash string. 12294/* -------------------------------------------------------------------- */ 12295!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); 12296 12297 12298/* Determine the top spacing of sidebar for full-width page section & row. 12299/* -------------------------------------------------------------------- */ 12300function mkPositionSidebar() { 12301 12302 var themeContent = $( '.theme-content' ), 12303 lastFullWidthChild = themeContent.find( '.vc_row-full-width' ).last(), 12304 top, 12305 sidebar = $( '#theme-page > .mk-main-wrapper-holder > .theme-page-wrapper > #mk-sidebar' ); 12306 12307 if ( ! lastFullWidthChild.length ) { 12308 sidebar.removeAttr( 'style' ); 12309 return; 12310 } 12311 12312 top = lastFullWidthChild.offset().top - themeContent.offset().top; 12313 sidebar.css( 'padding-top', top ); 12314} 12315 12316function mk_accordion_toggles_tooltip() { 12317 12318 "use strict"; 12319 12320 12321 /* Message Boxes */ 12322 /* -------------------------------------------------------------------- */ 12323 12324 $('.box-close-btn').on('click', function() { 12325 $(this).parent().fadeOut(300); 12326 return false; 12327 12328 }); 12329 12330} 12331 12332function mk_portfolio_ajax() { 12333 "use strict"; 12334 12335 var headerHeight = 0; 12336 if ($.exists("#wpadminbar")) { 12337 headerHeight += $("#wpadminbar").height(); 12338 } 12339 if (!$.exists('.mk-vm-menuwrapper')) { 12340 headerHeight += parseInt($('.mk-header').attr('data-sticky-height')); 12341 } 12342 12343 function init() { 12344 var $portfolio = $('.portfolio-grid.portfolio-ajax-enabled'); 12345 if (!$portfolio.length) return; // quit if no portfolio wrappers 12346 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.ajax.portfolio.js' ], function() { 12347 // wait for ajax response propagation and insertion 12348 setTimeout(function() { 12349 $portfolio.each( function() { 12350 $(this).ajaxPortfolio({ extraOffset: headerHeight }); 12351 }); 12352 }, 100); 12353 }); 12354 } 12355 12356 init(); 12357 12358 // Reinit when ajax loaded stuff 12359 // We have global reinit on Components. so move the ajaxPortfolio to component namespace as a standalone module to get it right. 12360 MK.utils.eventManager.subscribe('ajaxLoaded', init); 12361} 12362 12363/** 12364 * Ajax Search on header nav. 12365 */ 12366function mk_ajax_search() { 12367 "use strict"; 12368 12369 // Check if ajax search box enabled. 12370 if (mk_ajax_search_option !== "beside_nav") { 12371 return; 12372 } 12373 12374 // Set minimum length of text to get ajax result. 12375 var minimumLengthToSearch = 3; 12376 var $mkAjaxSearchInput = $('#mk-ajax-search-input'); 12377 var security = $mkAjaxSearchInput.siblings('input[name="security"]').val(); 12378 var wpHttpReferer = $mkAjaxSearchInput.siblings('input[name="_wp_http_referer"]').val(); 12379 var querySpliter = (ajaxurl.indexOf('?') > -1) ? '&' : '?'; 12380 var ul = document.getElementById('mk-nav-search-result'); 12381 var searchTerm; // Search term 12382 var requestCounter = 0; 12383 var responseCounter = 0; 12384 12385 // Add listener for getting result quickly while user entering text, past text and etc. 12386 $mkAjaxSearchInput.on('paste input propertychange', onSearchBoxInput); 12387 12388 /** 12389 * Callback function for entering search term listener (e.g. keyup). 12390 * It will generate AJAX request to get data. 12391 * 12392 * @param e 12393 */ 12394 function onSearchBoxInput(e) { 12395 var target = e.target || e.srcElement; 12396 var newValue = target.value; 12397 12398 // Check if user change the value of text box 12399 if (searchTerm !== newValue) { 12400 searchTerm = newValue; 12401 ul.innerHTML = ''; 12402 if (searchTerm.length >= minimumLengthToSearch) { 12403 // Show loading icon 12404 $mkAjaxSearchInput.addClass('ajax-searching'); 12405 requestCounter++; 12406 $.getJSON(ajaxurl + querySpliter + 'callback=?&action=mk_ajax_search&security=' + security + '&_wp_http_referer=' + wpHttpReferer + '&term=' + searchTerm) 12407 .done(showSearchResult) 12408 .fail(showErrorMessage); 12409 } 12410 } 12411 } 12412 12413 /** 12414 * Callback function for the response of the search request. 12415 * 12416 * @param data 12417 */ 12418 function showSearchResult(data) { 12419 responseCounter++; 12420 12421 // Only show response of latest request. 12422 if (isCorrectResponse()) { 12423 if (data.length > 0) { 12424 // Append result to result container 12425 for (var i = 0; i < data.length; i++) { 12426 var item = data[i]; 12427 $('<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); 12428 } 12429 } else { 12430 // Show no result message 12431 ul.innerHTML = '<li class="mk-nav-search-result-zero">No Result.</li>'; 12432 } 12433 12434 // Hide loading icon 12435 $mkAjaxSearchInput.parent('form').removeClass('ajax-searching').addClass('ajax-search-complete'); 12436 } 12437 } 12438 12439 /** 12440 * If any errors, we need a handler to show error message. 12441 */ 12442 function showErrorMessage() { 12443 responseCounter++; 12444 12445 if (isCorrectResponse()) { 12446 ul.innerHTML = '<li class="mk-nav-search-error-message">Can not search! Please try again.</li>'; 12447 } 12448 } 12449 12450 /** 12451 * Check if the response is right for the request. we show latest response only. 12452 * 12453 * @returns {boolean} 12454 */ 12455 function isCorrectResponse() { 12456 return requestCounter === responseCounter; 12457 } 12458}
12459/* Background Parallax Effects */ 12460/* -------------------------------------------------------------------- */ 12461 12462function mk_backgrounds_parallax() { 12463 12464 "use strict"; 12465 12466 if (mk_header_parallax == true) { 12467 $('.mk-header-bg').addClass('mk-parallax-enabled'); 12468 } 12469 if (mk_body_parallax == true) { 12470 $('body').addClass('mk-parallax-enabled'); 12471 } 12472 if (mk_banner_parallax == true) { 12473 $('.mk-header').addClass('mk-parallax-enabled'); 12474 } 12475 if (mk_footer_parallax == true) { 12476 $('#mk-footer').addClass('mk-parallax-enabled'); 12477 } 12478 12479 $('.mk-parallax-enabled').each(function () { 12480 var $this = $( this ); 12481 if (!MK.utils.isMobile()) { 12482 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.parallax.js' ], function() { 12483 $this.parallax("49%", 0.3); 12484 }); 12485 } 12486 }); 12487 12488 $('.mk-fullwidth-slideshow.parallax-slideshow').each(function () { 12489 var $this = $( this ); 12490 if (!MK.utils.isMobile()) { 12491 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.parallax.js' ], function() { 12492 var speed_factor = $this.attr('data-speedFactor'); 12493 $this.parallax("49%", speed_factor); 12494 }); 12495 } 12496 }); 12497 12498} 12499/* Blog, Portfolio Audio */ 12500/* -------------------------------------------------------------------- */ 12501 12502function loop_audio_init() { 12503 if (!$.exists('.jp-jplayer')) { 12504 return; 12505 } 12506 12507 $('.jp-jplayer.mk-blog-audio').each(function () { 12508 var $this = $(this); 12509 var $thisControls = $this.next('.jp-audio'); 12510 var audio = $this.find( '.mk-audio' )[0]; 12511 12512 // Set initial values. 12513 $thisControls.find('.jp-current-time').text('0:0'); 12514 $thisControls.find('.jp-volume-bar-value').css('width', 25); 12515 12516 /* 12517 * When audio data is loaded. 12518 */ 12519 audio.addEventListener('loadeddata', jsPlayerloaded(audio, $thisControls), false); 12520 12521 audio.addEventListener('loadedmetadata', function() { 12522 var minutes = Math.floor(audio.duration / 60); 12523 var seconds = Math.floor(audio.duration % 60); 12524 12525 // Update the duration time. 12526 if (!isNaN(minutes)) { 12527 $thisControls.find('.jp-duration').text(minutes + ':' + seconds); 12528 } 12529 12530 // Show play button when audio is loaded. 12531 $thisControls.removeClass('jp-audio-loading').addClass('jp-audio-loaded'); 12532 }); 12533 12534 /* 12535 * When audio is playing. 12536 */ 12537 audio.addEventListener('timeupdate', function() { 12538 var minutes = Math.floor(audio.currentTime / 60); 12539 var seconds = Math.floor(audio.currentTime % 60); 12540 12541 // Update curren time. 12542 $thisControls.find('.jp-current-time').text(minutes + ':' + seconds); 12543 12544 // Convert length of audio to 0 - 100 while keeping ratio. 12545 var position = ((audio.currentTime - 0) / (audio.duration - 0)) * (100 - 0) + 0; 12546 12547 // Update position of progress bar. 12548 $thisControls.find('.jp-play-bar').css('width', position + '%'); 12549 }); 12550 12551 /* 12552 * When audio is ended. 12553 */ 12554 audio.addEventListener('ended', function() { 12555 // Show play button. 12556 $thisControls.removeClass('jp-audio-playing'); 12557 12558 // Reset the position of progress bar. 12559 $thisControls.find('.jp-play-bar').css('width', 0); 12560 12561 // Reset the current time. 12562 $thisControls.find('.jp-current-time').text('0:0'); 12563 }); 12564 12565 /* 12566 * Play the audio. 12567 */ 12568 $thisControls.find('.jp-play').on('click', function() { 12569 audio.play(); 12570 12571 // Show pause button. 12572 $thisControls.addClass('jp-audio-playing').removeClass('jp-audio-paused'); 12573 }); 12574 12575 /* 12576 * Pause the audio.. 12577 */ 12578 $thisControls.find('.jp-pause').on('click', function() { 12579 audio.pause(); 12580 12581 // Show play button. 12582 $thisControls.addClass('jp-audio-paused').removeClass('jp-audio-playing'); 12583 }); 12584 12585 /* 12586 * Mute the audio. 12587 */ 12588 $thisControls.find('.jp-volume-bar svg').on('click', function() { 12589 audio.muted = !audio.muted; 12590 12591 $(this).parent().toggleClass('jp-muted'); 12592 }); 12593 12594 /* 12595 * Adjust the volume. 12596 */ 12597 $thisControls.find('.inner-value-adjust').on('click', function(e) { 12598 // Get the posiiton of mouse click, between 0 to 25. 12599 var position = e.pageX - $(this).offset().left; 12600 12601 // Scale the number from 0 - 25 to 0 - 1 while keeping ratio. 12602 var volume = ((position - 0) / (25 - 0)) * (1 - 0) + 0; 12603 12604 // Set volume. 12605 audio.volume = volume; 12606 12607 // Update the position of volume bar. 12608 $(this).find('.jp-volume-bar-value').css('width', position); 12609 }); 12610 12611 /* 12612 * Adjust current time from progress bar. 12613 */ 12614 $thisControls.find('.jp-seek-bar').on('click', function(e) { 12615 // Get the position of mouse click. 12616 var position = e.pageX - $(this).offset().left; 12617
12618 // Scale the number from 0 - currentTime to 0 - 100 while keeping ratio. 12619 var currnetTime = ((position - 0) / ($(this).width() - 0)) * (audio.duration - 0) + 0; 12620 12621 // Set current time. 12622 audio.currentTime = currnetTime; 12623 12624 // Update the position of progress bar. 12625 $thisControls.find('.jp-play-bar').css('width', currnetTime + '%'); 12626 }); 12627 }); 12628 12629 function jsPlayerloaded(audio, $thisControls) { 12630 var minutes = Math.floor(audio.duration / 60); 12631 var seconds = Math.floor(audio.duration % 60); 12632 12633 // Update the duration time. 12634 if (!isNaN(minutes)) { 12635 $thisControls.find('.jp-duration').text(minutes + ':' + seconds); 12636 } 12637 12638 // Show play button when audio is loaded. 12639 $thisControls.removeClass('jp-audio-loading').addClass('jp-audio-loaded'); 12640 } 12641} 12642 12643/* Blog Loop Carousel Shortcode */ 12644/* -------------------------------------------------------------------- */ 12645 12646 12647function mk_blog_carousel() { 12648 12649 "use strict"; 12650 12651 if (!$.exists('.mk-blog-showcase')) { 12652 return; 12653 } 12654 $('.mk-blog-showcase ul li').each(function () { 12655 12656 $(this).mouseenter( function () { 12657 12658 $(this).siblings('li').removeClass('mk-blog-first-el').end().addClass('mk-blog-first-el'); 12659 12660 }); 12661 12662 }); 12663 12664 12665} 12666 12667 12668 12669 12670/** 12671 * Contact Form 12672 * 12673 * Mostly implemented in Vanilla JS instead of jQuery. 12674 */ 12675function mk_contact_form() { 12676 "use strict"; 12677 var mkContactForms = document.getElementsByClassName('mk-contact-form'); 12678 if (mkContactForms.length === 0) { 12679 return; 12680 } 12681 var captchaImageHolder = $('.captcha-image-holder'); 12682 var activeClassName = 'is-active'; 12683 var invalidClassName = 'mk-invalid'; 12684 for (var i = 0; i < mkContactForms.length; i++) { 12685 initializeForm(mkContactForms[i], activeClassName, invalidClassName); 12686 } 12687 if (captchaImageHolder.length > 0) { 12688 $(window).on('load', initializeCaptchas); 12689 } 12690 /** 12691 * Initialize mk forms. e.g add activeClassName for inputs. 12692 * 12693 * @param form 12694 * @param activeClassName 12695 * @param invalidClassName 12696 */ 12697 function initializeForm(form, activeClassName, invalidClassName) { 12698 var inputs = getFormInputs(form); 12699 for (var i = 0; i < inputs.length; i++) { 12700 markActiveClass(inputs[i]); 12701 } 12702 form.addEventListener('submit', function(e) { 12703 validateForm(e, invalidClassName); 12704 }); 12705 /** 12706 * Set activeClassName for the parent node of the inout 12707 */ 12708 function setActiveClass() { 12709 addClass(this.parentNode, activeClassName); 12710 } 12711 /** 12712 * Unset activeClassName from the parent node of the input. 12713 * We need to unset activeClassName only if the data was empty. 12714 * e.g. in the line style of the mk-contact-form, we set labels position based on activeClassName. 12715 */ 12716 function unsetActiveClass() { 12717 if (this.value === '') { 12718 removeClass(this.parentNode, activeClassName); 12719 } 12720 } 12721 /** 12722 * Add event listeners (focus,blur) for input to set and unset activeClassName. 12723 * 12724 * @param input 12725 */ 12726 function markActiveClass(input) { 12727 input.addEventListener('focus', setActiveClass); 12728 input.addEventListener('blur', unsetActiveClass); 12729 } 12730 } 12731 /** 12732 * Validate form when it's submitted. If everything was valid, we with post form in ajax request. 12733 * 12734 * @param e 12735 * @param invalidClassName 12736 */ 12737 function validateForm(e, invalidClassName) { 12738 e.preventDefault(); 12739 var form = e.target || e.srcElement; 12740 var inputs = getFormInputs(form); 12741 var isValidForm = true; 12742 var hasCaptchaField = false; 12743 for (var i = 0; i < inputs.length; i++) { 12744 var input = inputs[i]; 12745 input.value = String(input.value).trim(); 12746 switch (input.type) { 12747 case 'hidden': 12748 break; 12749 case 'checkbox': 12750 isValidForm = validateCheckBox(input, invalidClassName) && isValidForm; 12751 break; 12752 case 'email': 12753 isValidForm = validateEmail(input, invalidClassName) && isValidForm; 12754 break; 12755 case 'textarea': 12756 isValidForm = validateText(input, invalidClassName) && isValidForm; 12757 break; 12758 case 'text': 12759 /** 12760 * Some old browsers such as IE 8 and 9 detect email type as text. 12761 * So, we need to extra check for data-type attribute 12762 */ 12763 if (input.dataset.type === 'captcha') { 12764 isValidForm = validateText(input, invalidClassName) && isValidForm; 12765 hasCaptchaField = true; 12766 } else if (input.dataset.type === 'email') { 12767 isValidForm = validateEmail(input, invalidClassName) && isValidForm; 12768 } else { 12769 isValidForm = validateText(input, invalidClassName) && isValidForm; 12770 } 12771 break; 12772 default: 12773 /** 12774 * e.g. validating for radiobox, selectbox and etc. 12775 */ 12776 console.warn('Implement validation for ' + input.name + ':' + input.type); 12777 break; 12778 } 12779 } 12780 if (isValidForm) { 12781 if (hasCaptchaField) { 12782 validateCaptcha(form, invalidClassName, sendForm); 12783 } else { 12784 sendForm(form); 12785 } 12786 } 12787 } 12788 /** 12789 * Validate captcha of form. If everything was, we will execute captchaIsValidCallback which as sendForm(). 12790 * 12791 * @param form 12792 * @param invalidClassName 12793 * @param captchaIsValidCallback 12794 * @returns boolean 12795 */ 12796 function validateCaptcha(form, invalidClassName, captchaIsValidCallback) { 12797 var input = form.querySelectorAll('[data-type="captcha"]')[0]; 12798 if (input.value.length === 0) { 12799 addClass(input, invalidClassName); 12800 return false;
12801 } else { 12802 window.get.captcha(input.value).done(function(data) { 12803 loadCaptcha(); 12804 input.value = ''; 12805 if (data !== 'ok') { 12806 addClass(input, invalidClassName); 12807 addClass(input, 'contact-captcha-invalid'); 12808 removeClass(input, 'contact-captcha-valid'); 12809 input.placeholder = mk_captcha_invalid_txt; 12810 } else { 12811 removeClass(input, invalidClassName); 12812 removeClass(input, 'contact-captcha-invalid'); 12813 addClass(input, 'contact-captcha-valid'); 12814 input.placeholder = mk_captcha_correct_txt; 12815 captchaIsValidCallback(form); 12816 } 12817 }); 12818 } 12819 } 12820 /** 12821 * Send submitted form. 12822 * 12823 * @param form 12824 */ 12825 function sendForm(form) { 12826 var $form = $(form); 12827 var data = getFormData(form); 12828 progressButton.loader($form); 12829 $.post(ajaxurl, data, function(response) { 12830 var res = JSON.parse(response); 12831 if (res.action_Status) { 12832 progressButton.success($form); 12833 $form.find('.text-input').val(''); 12834 $form.find('textarea').val(''); 12835 $form.find('input[type=checkbox]').attr("checked", false); 12836 $form.find('.contact-form-message').slideDown().addClass('state-success').html(res.message); 12837 setTimeout(function() { 12838 $form.find('.contact-form-message').slideUp(); 12839 }, 5000); 12840 } else { 12841 progressButton.error($form); 12842 $form.find('.contact-form-message').removeClass('state-success').html(res.message); 12843 } 12844 }); 12845 } 12846 /** 12847 * Initialize all captcha images for first time. All captcha images is always same. e.g. if we have multiple form, 12848 * all of them will have the same image. 12849 * It will also add event listener for '.captcha-change-image' objects to reload the captcha. 12850 */ 12851 function initializeCaptchas() { 12852 var captchaChangeImageButtons = document.getElementsByClassName('captcha-change-image'); 12853 for (var i = 0; i < captchaChangeImageButtons.length; i++) { 12854 captchaChangeImageButtons[i].addEventListener('click', loadCaptcha); 12855 } 12856 } 12857 /** 12858 * Load captcha text and append the image to captcha container. 12859 * If it used as a callback, it will prevent default behave of the event. 12860 * e.g. loading new captcha by click on <a> without changing url. 12861 */ 12862 function loadCaptcha(e) { 12863 if (e) { 12864 e.preventDefault(); 12865 } 12866 $.post(ajaxurl, { 12867 action: 'mk_create_captcha_image' 12868 }, appendImage); 12869 /** 12870 * The callback function for append or change old image src based on response. T 12871 * The captchaImageURL is the url of the captcha which is provided in ajax response of mk_create_captcha_image. 12872 * @param captchaImageURL 12873 */ 12874 function appendImage(captchaImageURL) { 12875 if (captchaImageHolder.find('.captcha-image').length === 0) { 12876 captchaImageHolder.html('<img src="' + captchaImageURL + '" class="captcha-image" alt="captcha txt">'); 12877 } else { 12878 captchaImageHolder.find('.captcha-image').attr("src", captchaImageURL + '?' + new Date().getTime()); 12879 } 12880 } 12881 } 12882 /** 12883 * Get form inputs using querySelectorAll(). 12884 * It returns <input> and <textarea> tags. If you need any other tags such as <select>, please update this function. 12885 * 12886 * @param form 12887 * @returns {NodeList} 12888 */ 12889 function getFormInputs(form) { 12890 return form.querySelectorAll('input,textarea'); 12891 } 12892 /** 12893 * Get data of the form inputs and textareas as a object. 12894 * 12895 * @param form 12896 * @returns {{action: string}} 12897 */ 12898 function getFormData(form) { 12899 var data = { 12900 action: 'mk_contact_form' 12901 }; 12902 var inputs = getFormInputs(form); 12903 for (var i = 0; i < inputs.length; i++) {
12904 data[inputs[i].name] = inputs[i].value; 12905 } 12906 return data; 12907 } 12908} 12909/* Ajax Login Form */ 12910/* -------------------------------------------------------------------- */ 12911function mk_login_form() { 12912 $('form.mk-login-form').each(function() { 12913 var $this = $(this); 12914 $this.on('submit', function(e) { 12915 $('p.mk-login-status', $this).show().text(ajax_login_object.loadingmessage); 12916 $.ajax({ 12917 type: 'POST', 12918 dataType: 'json', 12919 url: ajax_login_object.ajaxurl, 12920 data: { 12921 'action': 'ajaxlogin', 12922 'username': $('#username', $this).val(), 12923 'password': $('#password', $this).val(), 12924 'security': $('#security', $this).val() 12925 }, 12926 success: function(data) { 12927 $('p.mk-login-status', $this).text(data.message); 12928 if (data.loggedin === true) { 12929 document.location.href = ajax_login_object.redirecturl; 12930 } 12931 } 12932 }); 12933 e.preventDefault(); 12934 }); 12935 }); 12936} 12937/* Progress Button */ 12938/* -------------------------------------------------------------------- */ 12939var progressButton = { 12940 loader: function(form) { 12941 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12942 var $form = form, 12943 progressBar = $form.find(".mk-progress-button .mk-progress-inner"), 12944 buttonText = $form.find(".mk-progress-button .mk-progress-button-content"), 12945 progressButton = new TimelineLite(); 12946 progressButton.to(progressBar, 0, { 12947 width: "100%", 12948 scaleX: 0, 12949 scaleY: 1 12950 }).to(buttonText, .3, { 12951 y: -5 12952 }).to(progressBar, 1.5, { 12953 scaleX: 1, 12954 ease: Power2.easeInOut 12955 }, "-=.1").to(buttonText, .3, { 12956 y: 0 12957 }).to(progressBar, .3, { 12958 scaleY: 0 12959 }); 12960 }); 12961 }, 12962 success: function(form) { 12963 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12964 var $form = form, 12965 buttonText = $form.find(".mk-button .mk-progress-button-content, .mk-contact-button .mk-progress-button-content"), 12966 successIcon = $form.find(".mk-progress-button .state-success"), 12967 progressButtonSuccess = new TimelineLite({ 12968 onComplete: hideSuccessMessage 12969 }); 12970 progressButtonSuccess.to(buttonText, .3, { 12971 paddingRight: 20, 12972 ease: Power2.easeInOut 12973 }, "+=1").to(successIcon, .3, { 12974 opacity: 1 12975 }).to(successIcon, 2, { 12976 opacity: 1 12977 }); 12978 12979 function hideSuccessMessage() { 12980 progressButtonSuccess.reverse() 12981 } 12982 }); 12983 }, 12984 error: function(form) { 12985 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12986 var $form = form, 12987 buttonText = $form.find(".mk-button .mk-progress-button-content, .mk-contact-button .mk-progress-button-content"), 12988 errorIcon = $form.find(".mk-progress-button .state-error"), 12989 progressButtonError = new TimelineLite({ 12990 onComplete: hideErrorMessage 12991 }); 12992 progressButtonError.to(buttonText, .3, { 12993 paddingRight: 20 12994 }, "+=1").to(errorIcon, .3, { 12995 opacity: 1 12996 }).to(errorIcon, 2, { 12997 opacity: 1 12998 }); 12999 13000 function hideErrorMessage() { 13001 progressButtonError.reverse() 13002 } 13003 }); 13004 } 13005}; 13006function mk_click_events() { 13007 "use strict"; 13008 13009 var eventtype = 'click'; 13010 13011 $(".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) { 13012 if (event.stopPropagation) { 13013 event.stopPropagation(); 13014 } else if (window.event) { 13015 window.event.cancelBubble = true; 13016 } 13017 }); 13018 $("html").on(eventtype, function() {
13019 $(".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); 13020 $('.mk-quick-contact-link').removeClass('quick-contact-active'); 13021 // Removed By Maki for repairing fullnav scroll issue. Hope it odesnt break anything 13022 // $('body').css('overflow', 'visible'); 13023 }); 13024 13025 $('.mk-fullscreen-search-overlay').on(eventtype,function(){ 13026 $(this).removeClass('mk-fullscreen-search-overlay-show'); 13027 }); 13028 13029 $('.mk-forget-password').on(eventtype, function() { 13030 $('.mk-forget-panel').siblings().hide().end().show(); 13031 }); 13032 13033 $('.mk-create-account').on(eventtype, function() { 13034 $('#mk-register-panel').siblings().hide().end().show(); 13035 }); 13036 13037 $('.mk-return-login').on(eventtype, function() { 13038 $('#mk-login-panel').siblings().hide().end().show(); 13039 }); 13040 13041 13042 $('.mk-quick-contact-link').on(eventtype, function() { 13043 var $this = $(this), 13044 $quickContact = $('#mk-quick-contact'); 13045 if (!$this.hasClass('quick-contact-active')) { 13046 $quickContact.addClass('quick-contact-anim').fadeIn(250); 13047 $this.addClass('quick-contact-active'); 13048 } else { 13049 $quickContact.removeClass('quick-contact-anim').fadeOut(100); 13050 $this.removeClass('quick-contact-active'); 13051 } 13052 return false; 13053 }); 13054 13055} 13056 13057function mk_social_share_global() { 13058 13059 "use strict"; 13060 13061 var eventtype = 'click'; 13062 13063 $('.twitter-share').on(eventtype, function() { 13064 var $this = $(this), 13065 $url = $this.attr('data-url'), 13066 $title = $this.attr('data-title'); 13067 13068 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"); 13069 return false; 13070 }); 13071 13072 $('.pinterest-share').on(eventtype, function() { 13073 var $this = $(this), 13074 $url = $this.attr('data-url'), 13075 $title = $this.attr('data-title'), 13076 $image = $this.attr('data-image'); 13077 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"); 13078 return false; 13079 }); 13080 13081 $('.facebook-share').on(eventtype, function() { 13082 var $url = $(this).attr('data-url'); 13083 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"); 13084 return false; 13085 }); 13086 13087 $('.googleplus-share').on(eventtype, function() { 13088 var $url = $(this).attr('data-url'); 13089 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"); 13090 return false; 13091 }); 13092 13093 $('.linkedin-share').on(eventtype, function() { 13094 var $this = $(this), 13095 $url = $this.attr('data-url'), 13096 $title = $this.attr('data-title'), 13097 $desc = $this.attr('data-desc'); 13098 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"); 13099 return false; 13100 }); 13101} 13102 13103 13104/* Event Count Down */ 13105/* -------------------------------------------------------------------- */ 13106 13107function mk_event_countdown() { 13108 if ($.exists('.mk-event-countdown')) { 13109 13110 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.countdown.js' ], function() { 13111 13112 $('.mk-event-countdown').each(function () { 13113 if (isTest) return; 13114 var $this = $(this), 13115 $date = $this.attr('data-date'), 13116 $offset = $this.attr('data-offset'); 13117 13118 $this.downCount({ 13119 date: $date, 13120 offset: $offset 13121 }); 13122 }); 13123 13124 }); 13125 } 13126} 13127/* Flexslider init */ 13128/* -------------------------------------------------------------------- */ 13129 13130function mk_flexslider_init() { 13131 13132 var $lcd = $('.mk-lcd-slideshow'), 13133 $laptop = $('.mk-laptop-slideshow-shortcode'); 13134 13135 if($lcd.length) $lcd.find('.mk-lcd-image').fadeIn(); 13136 if($laptop.length) $laptop.find(".mk-laptop-image").fadeIn(); 13137 13138 $('.js-flexslider').each(function () { 13139 13140 if ($(this).parents('.mk-tabs').length || $(this).parents('.mk-accordion').length) { 13141 $(this).removeData("flexslider"); 13142 } 13143 13144 var $this = $(this), 13145 $selector = $this.attr('data-selector'), 13146 $animation = $this.attr('data-animation'), 13147 $easing = $this.attr('data-easing'), 13148 $direction = $this.attr('data-direction'), 13149 $smoothHeight = $this.attr('data-smoothHeight') == "true" ? true : false,
13150 $slideshowSpeed = $this.attr('data-slideshowSpeed'), 13151 $animationSpeed = $this.attr('data-animationSpeed'), 13152 $controlNav = $this.attr('data-controlNav') == "true" ? true : false, 13153 $directionNav = $this.attr('data-directionNav') == "true" ? true : false, 13154 $pauseOnHover = $this.attr('data-pauseOnHover') == "true" ? true : false, 13155 $isCarousel = $this.attr('data-isCarousel') == "true" ? true : false; 13156 13157 13158 if ($selector !== undefined) { 13159 var $selector_class = $selector; 13160 } else { 13161 var $selector_class = ".mk-flex-slides > li"; 13162 } 13163 13164 if ($isCarousel === true) { 13165 var $itemWidth = parseInt($this.attr('data-itemWidth')), 13166 $itemMargin = parseInt($this.attr('data-itemMargin')), 13167 $minItems = parseInt($this.attr('data-minItems')), 13168 $maxItems = parseInt($this.attr('data-maxItems')), 13169 $move = parseInt($this.attr('data-move')); 13170 } else { 13171 var $itemWidth = $itemMargin = $minItems = $maxItems = $move = 0; 13172 } 13173 13174 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.flexslider.js' ], function() { 13175 $this.flexslider({ 13176 selector: $selector_class, 13177 animation: $animation, 13178 easing: $easing, 13179 direction: $direction, 13180 smoothHeight: $smoothHeight, 13181 slideshow: !isTest, // autoplay 13182 slideshowSpeed: $slideshowSpeed, 13183 animationSpeed: $animationSpeed, 13184 controlNav: $controlNav, 13185 directionNav: $directionNav, 13186 pauseOnHover: $pauseOnHover, 13187 prevText: "", 13188 nextText: "", 13189 itemWidth: $itemWidth, 13190 itemMargin: $itemMargin, 13191 minItems: $minItems, 13192 maxItems: $maxItems, 13193 move: $move 13194 }); 13195 }); 13196 13197 }); 13198 13199} 13200 13201/* Header Search Form */ 13202/* -------------------------------------------------------------------- */ 13203 13204function mk_header_searchform() { 13205 13206 $('.mk-search-trigger').on('click',function(){ 13207 setTimeout(function() { 13208 $('#mk-ajax-search-input').focus(); 13209 },500); 13210 }); 13211 13212 "use strict"; 13213 13214 $('.mk-header-toolbar .mk-header-searchform .text-input').on('focus', function () { 13215 13216 if ($('.mk-header-toolbar .mk-header-searchform .text-input').hasClass('on-close-state')) { 13217 $('.mk-header-toolbar .mk-header-searchform .text-input').removeClass('on-close-state').animate({ 13218 'width': '200px' 13219 }, 200); 13220 return false; 13221 } 13222 }); 13223 13224 $(".mk-header-toolbar .mk-header-searchform").on('click',function (event) { 13225 if (event.stopPropagation) { 13226 event.stopPropagation(); 13227 } else if (window.event) { 13228 window.event.cancelBubble = true; 13229 } 13230 }); 13231 13232 13233 $("html").on('click',function () { 13234 $(this).find(".mk-header-toolbar .mk-header-searchform .text-input").addClass('on-close-state').animate({ 13235 'width': 90 13236 }, 300); 13237 }); 13238 13239 // Fix search for edge browser 13240 if(MK.utils.browser.name === 'Edge') { 13241 $('#mk-fullscreen-search-input').on('keydown', function(e) { 13242 if (e.which == 13) { 13243 e.preventDefault(); 13244 $('#mk-fullscreen-searchform').submit(); 13245 } 13246 }); 13247 } 13248 13249} 13250 13251 13252 13253 13254/* Hover Events */ 13255/* -------------------------------------------------------------------- */ 13256 13257function mk_hover_events() { 13258 13259 "use strict"; 13260 13261 $('.shopping-cart-header').hover( 13262 function() { 13263 $(this).find('.mk-shopping-cart-box').stop(true, true).fadeIn(250); 13264 }, 13265 function() { 13266 $(this).find('.mk-shopping-cart-box').stop(true, true).delay(500).fadeOut(250); 13267 } 13268 ); 13269 13270 13271 $('.widget-sub-navigation > ul > li, .widget_nav_menu ul.menu > li, .widget_product_categories ul > .cat-item').each(function() { 13272 13273 var $this = $(this), 13274 $subLevel = $this.find('ul').first(); 13275 13276 if ($this.hasClass('page_item_has_children') || $this.hasClass('menu-item-has-children') || $this.hasClass('cat-parent')) { 13277 $this.on('click', function() { 13278 if($this.hasClass('toggle-active')) { 13279 $subLevel.stop(true, true).slideUp(700); 13280 $this.removeClass('toggle-active'); 13281 } else { 13282 $subLevel.stop(true, true).slideDown(700); 13283 $this.addClass('toggle-active'); 13284 } 13285 }); 13286 13287 $subLevel.on('click', function(e){ 13288 e.stopPropagation(); 13289 }); 13290 } 13291 13292 }); 13293 13294 // var eventtype = mobilecheck() ? 'touchstart' : 'click'; 13295 var eventtype = 'click'; 13296 13297 $('.mk-fullscreen-trigger').on(eventtype, function(e) { 13298 $('.mk-fullscreen-search-overlay').addClass('mk-fullscreen-search-overlay-show'); 13299 setTimeout(function(){ 13300 $("#mk-fullscreen-search-input").focus(); 13301 }, 300); 13302 e.preventDefault(); 13303 }); 13304 13305 $('.mk-fullscreen-close').on(eventtype, function(e) { 13306 $('.mk-fullscreen-search-overlay').removeClass('mk-fullscreen-search-overlay-show'); 13307 e.preventDefault(); 13308 }); 13309 13310} 13311 13312function mk_unfold_footer() { 13313 var $this = $('#mk-footer'), 13314 $spacer = $('#mk-footer-unfold-spacer'), 13315 $footerHeight = $this.outerHeight(); 13316 13317 // Stick with CSS media query breakpoint to target exact screen width 13318 if( !window.matchMedia("(max-width: 767px)").matches ) { 13319 if ($this.hasClass('mk-footer-unfold')) { 13320 $spacer.css('height', $footerHeight); 13321 } 13322 } else { 13323 $spacer.css('height', 0); 13324 } 13325} 13326 13327/* jQuery fancybox lightbox */ 13328/* -------------------------------------------------------------------- */ 13329function mk_lightbox_init() { 13330 13331 $(".mk-lightbox").fancybox({ 13332 loop: true, 13333 }); 13334 13335 $.fancybox.defaults.hash = false;
13336 13337} 13338 13339 13340/* Milestone Number Shortcode */ 13341/* -------------------------------------------------------------------- */ 13342 13343function mk_milestone() { 13344 13345 "use strict"; 13346 13347 if( isTest || !$.exists('.mk-milestone') ) return; 13348 13349 $('.mk-milestone').each(function () { 13350 var $this = $(this), 13351 stop_number = $this.find('.milestone-number').attr('data-stop'), 13352 animation_speed = parseInt($this.find('.milestone-number').attr('data-speed')); 13353 13354 var build = function() { 13355 if (!$this.hasClass('scroll-animated')) { 13356 $this.addClass('scroll-animated'); 13357 13358 $({ 13359 countNum: $this.find('.milestone-number').text() 13360 }).animate({ 13361 countNum: stop_number 13362 }, { 13363 duration: animation_speed, 13364 easing: 'linear', 13365 step: function () { 13366 $this.find('.milestone-number').text(Math.floor(this.countNum)); 13367 }, 13368 complete: function () { 13369 $this.find('.milestone-number').text(this.countNum); 13370 } 13371 }); 13372 } 13373 }; 13374 13375 if ( !MK.utils.isMobile() ) { 13376 // refactored only :in-viewport logic. rest is to-do 13377 MK.utils.scrollSpy( this, { 13378 position: 'bottom', 13379 after: build 13380 }); 13381 } else { 13382 build(); 13383 } 13384 13385 }); 13386 13387} 13388 13389 13390 13391 13392/* Recent Works Widget */ 13393/* -------------------------------------------------------------------- */ 13394 13395function mk_portfolio_widget() { 13396 13397 "use strict"; 13398 13399 $('.widget_recent_portfolio li').each(function () { 13400 13401 $(this).find('.portfolio-widget-thumb').on('hover',function () { 13402 13403 $(this).siblings('.portfolio-widget-info').animate({ 13404 'opacity': 1 13405 }, 200); 13406 }, function () { 13407 13408 $(this).siblings('.portfolio-widget-info').animate({ 13409 'opacity': 0 13410 }, 200); 13411 }); 13412 13413 }); 13414} 13415 13416 13417 13418/* Skill Meter and Charts */ 13419/* -------------------------------------------------------------------- */ 13420function mk_skill_meter() { 13421 "use strict"; 13422 if ($.exists('.mk-skill-meter')) { 13423 if (!MK.utils.isMobile()) { 13424 $(".mk-skill-meter .progress-outer").each(function() { 13425 var $this = $(this); 13426 13427 var build = function() { 13428 if (!$this.hasClass('scroll-animated')) { 13429 $this.addClass('scroll-animated'); 13430 $this.animate({ 13431 width: $this.attr("data-width") + '%' 13432 }, 2000); 13433 } 13434 }; 13435 13436 MK.utils.scrollSpy( this, { 13437 position: 'bottom', 13438 after: build 13439 }); 13440 }); 13441 } else { 13442 $(".mk-skill-meter .progress-outer").each(function() { 13443 var $this = $(this); 13444 if (!$this.hasClass('scroll-animated')) { 13445 $this.addClass('scroll-animated'); 13446 $this.css({ 13447 width: $(this).attr("data-width") + '%' 13448 }); 13449 } 13450 }); 13451 } 13452 } 13453} 13454 13455// function mk_charts() { 13456// "use strict"; 13457 13458// if( !$.exists('.mk-chart') ) return; 13459 13460// MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.easyPieChart.js' ], function() { 13461 13462// $('.mk-chart').each(function() { 13463 13464// var $this = $(this), 13465// $parent_width = $(this).parent().width(), 13466// $chart_size = parseInt($this.attr('data-barSize')); 13467 13468// if ($parent_width < $chart_size) { 13469// $chart_size = $parent_width; 13470// $this.css('line-height', $chart_size); 13471// $this.find('i').css({ 13472// 'line-height': $chart_size + 'px' 13473// }); 13474// $this.css({ 13475// 'line-height': $chart_size + 'px' 13476// }); 13477// } 13478 13479// var build = function() { 13480// $this.easyPieChart({ 13481// animate: 1300, 13482// lineCap: 'butt', 13483// lineWidth: $this.attr('data-lineWidth'), 13484// size: $chart_size, 13485// barColor: $this.attr('data-barColor'), 13486// trackColor: $this.attr('data-trackColor'), 13487// scaleColor: 'transparent', 13488// onStep: function(value) {
13489// this.$el.find('.chart-percent span').text(Math.ceil(value)); 13490// } 13491// }); 13492// }; 13493 13494// // refactored only :in-viewport logic. rest is to-do 13495// MK.utils.scrollSpy( this, { 13496// position: 'bottom', 13497// after: build 13498// }); 13499 13500 13501// }); 13502// }); 13503// } 13504/* Toolbar subscribe form */ 13505/* -------------------------------------------------------------------- */ 13506 13507$( "#mc-embedded-subscribe-form" ).on('submit',function( e ){ 13508 var $this = $(this); 13509 13510 e.preventDefault(); 13511 $.ajax({ 13512 url: MK.core.path.ajaxUrl, 13513 type: "POST", 13514 data: { 13515 action: "mk_ajax_subscribe", 13516 email: $this.find( ".mk-subscribe--email" ).val(), 13517 list_id: $this.find( ".mk-subscribe--list-id" ).val(), 13518 optin: $this.find( ".mk-subscribe--optin" ).val() 13519 }, 13520 success: function ( res ) { 13521 $this.parent().find( ".mk-subscribe--message" ).html( $.parseJSON( res ).message ); 13522 } 13523 }); 13524}); 13525 13526/** 13527 * Add class to tag. It's vanilla js instead of jQuery. 13528 * @param tag 13529 * @param className 13530 */ 13531function addClass(tag, className) { 13532 tag.className += ' ' + className; 13533} 13534 13535/** 13536 * Remove class from tag. It's vanilla js instead of jQuery. 13537 * Replacing should be with g for replacing all occurrence. 13538 * 13539 * @param tag 13540 * @param className 13541 */ 13542function removeClass(tag, className) { 13543 tag.className = tag.className.replace(new RegExp(className, 'g'), ''); 13544} 13545 13546 13547/** 13548 * If we're running under Node for testing purpose. 13549 */ 13550if(typeof exports !== 'undefined') { 13551 exports.addClass = addClass; 13552 exports.removeClass = removeClass; 13553} 13554 13555/*! 13556* javascript-debounce 1.0.0 13557* 13558* A lightweight, dependency-free JavaScript module for debouncing functions based on David Walsh's debounce function. 13559* 13560* Source code available at: https://github.com/jgarber623/javascript-debounce 13561* 13562* (c) 2015-present Jason Garber (http://sixtwothree.org) 13563* 13564* javascript-debounce may be freely distributed under the MIT license. 13565*/ 13566 13567(function(root, factory) { 13568 if (typeof define === "function" && define.amd) { 13569 define([], factory); 13570 } else if (typeof exports === "object") { 13571 module.exports = factory(); 13572 } else { 13573 root.debounce = factory(); 13574 } 13575})(this, function() { 13576 "use strict"; 13577 return function(callback, delay) { 13578 var timeout; 13579 return function() { 13580 var context = this, args = arguments; 13581 clearTimeout(timeout); 13582 timeout = setTimeout(function() { 13583 callback.apply(context, args); 13584 }, delay); 13585 }; 13586 }; 13587}); 13588 13589/** 13590 * Validate email address. 13591 * 13592 * @param input 13593 * @param invalidClassName 13594 * @returns boolean 13595 */ 13596function validateEmail(input, invalidClassName) { 13597 var value = input.value.trim(); 13598 if ((input.required || value.length > 0) && !/^([a-z0-9_\.\-\+]+)@([\da-z\.\-]+)\.([a-z\.]{2,63})$/i.test(value)) { 13599 if (invalidClassName) { 13600 addClass(input, invalidClassName); 13601 } 13602 return false; 13603 } else { 13604 if (invalidClassName) { 13605 removeClass(input, invalidClassName); 13606 } 13607 return true; 13608 } 13609} 13610/** 13611 * Validate text entry. 13612 * 13613 * @param input 13614 * @param invalidClassName 13615 * @returns boolean 13616 */ 13617function validateText(input, invalidClassName) { 13618 var value = input.value.trim(); 13619 if (input.required && value.length === 0) { 13620 if (invalidClassName) { 13621 addClass(input, invalidClassName); 13622 } 13623 return false; 13624 } else { 13625 if (invalidClassName) { 13626 removeClass(input, invalidClassName); 13627 } 13628 return true; 13629 } 13630} 13631/** 13632 * Validate Checkbox. 13633 * 13634 * @param input 13635 * @param invalidClassName 13636 * @returns boolean 13637 */ 13638function validateCheckBox(input, invalidClassName) { 13639 if (input.required && input.checked == false) { 13640 if (invalidClassName) { 13641 addClass(input, invalidClassName); 13642 } 13643 return false; 13644 } else { 13645 if (invalidClassName) { 13646 removeClass(input, invalidClassName); 13647 } 13648 return true; 13649 } 13650} 13651/** 13652 * If we're running under Node for testing purpose. 13653 */ 13654if (typeof exports !== 'undefined') { 13655 exports.validateEmail = validateEmail; 13656 exports.validateText = validateText; 13657} 13658(function( $ ) { 13659 'use strict'; 13660 13661 var $wrapper = $('.js-bottom-corner-btns'); 13662 var $contactBtn = $wrapper.find('.js-bottom-corner-btn--contact'); 13663 var $backBtn = $wrapper.find('.js-bottom-corner-btn--back'); 13664 var hasContactBtn = $contactBtn.length; 13665 var hasBackBtn = $backBtn.length; 13666 13667 if(!hasBackBtn) return; 13668 13669 function deactivate() { 13670 $contactBtn.removeClass('is-active'); 13671 $backBtn.removeClass('is-active'); 13672 } 13673 13674 function activate() { 13675 $contactBtn.addClass('is-active'); 13676 $backBtn.addClass('is-active'); 13677 } 13678 13679 MK.utils.scrollSpy( 400, {
13680 before: deactivate, 13681 after: activate 13682 }); 13683 13684})( jQuery ); 13685(function($) { 13686 'use strict'; 13687 13688 $('.mk-fullscreen-nav-close, .mk-fullscreen-nav-wrapper, #fullscreen-navigation a').on('click', function(e) { 13689 13690 // Close nav with removing classes 13691 $('.mk-fullscreen-nav').removeClass('opened'); 13692 $('.mk-dashboard-trigger').removeClass('fullscreen-active'); 13693 $('body').removeClass('fullscreen-nav-opened'); 13694 13695 var anchor = MK.utils.detectAnchor( this ), 13696 $this = $( this ); 13697 13698 // Scroll to anchor if exists 13699 if( anchor.length ) { 13700 // Moved 4 these lines after anchor check because console error. If anchor 13701 // return false, split part will throw an error because $this is not a link. 13702 // Fix AM-2027 13703 var href = $this.attr( 'href' ).split( '#' )[0]; 13704 var url = window.location.href; 13705 var isSamePage = url.indexOf( href ) !== -1; 13706 13707 if ( isSamePage ) { 13708 e.preventDefault(); 13709 } 13710 MK.utils.scrollToAnchor( anchor ); 13711 13712 // Or do nothing if pointless # as href 13713 // BAD PRACTICE: it is very popular to use "#" for click elements that we listen to with js. 13714 // GOOD PRACTICE: prefer "javascript:;" as easier to handle and more readable version 13715 } else if( $this.attr( 'href' ) === '#' ) { 13716 e.preventDefault(); 13717 } 13718 }); 13719 13720 $('.fullscreen-navigation-ul .menu-sub-level-arrow').on('click', function(){ 13721 $(this).siblings('.sub-menu').slideToggle(); 13722 }); 13723 13724}(jQuery)); 13725(function($) { 13726 'use strict'; 13727 13728 /** 13729 * Mega menu 13730 */ 13731 var $navList = $(".main-navigation-ul"); 13732 var megaMenu = function megaMenu() { 13733 13734 $navList.MegaMenu({ 13735 type: "vertical", 13736 delay: 200 13737 }); 13738 }; 13739 13740 $(window).on('load', megaMenu); 13741 13742}(jQuery)); 13743(function($) { 13744 'use strict'; 13745 13746 /** 13747 * One pager menu hash update. 13748 * Smooth scroll is appended globally whenever the click element has corresponding #el 13749 */ 13750 var onePageNavItem = function onePageNavItem() { 13751 var $this = $( this ), 13752 link = $this.find( 'a' ), 13753 anchor = MK.utils.detectAnchor( link ); // anchor on current page 13754 13755 if( !anchor.length ) return; 13756 13757 $this.removeClass( 'current-menu-item current-menu-ancestor current-menu-parent' ); 13758 13759 var activeNav = function( state ) { 13760 return function() { 13761 $this[ state ? 'addClass' : 'removeClass' ]( 'current-menu-item' ); 13762 window.history.replaceState( undefined, undefined, [ state ? anchor : ' ' ] ); 13763 }; 13764 }; 13765 13766 MK.utils.scrollSpy( $( anchor )[0], { 13767 before : activeNav( false ), 13768 active : activeNav( true ), 13769 after : activeNav( false ), 13770 }); 13771 }; 13772 13773 var $navItems = $('.js-main-nav').find( 'li' ); 13774 13775 $(window).on('load', function() { 13776 // Wait with spying anchors so we do not assign anchor that is browser scroll to after page load 13777 // Especially when there are anchors on top - refreshing page can cause grabbing one of them into url 13778 // and force unwanted scroll 13779 setTimeout(function() { 13780 $navItems.each( onePageNavItem ); 13781 }, 1000); 13782 }); 13783 13784}(jQuery)); 13785(function($) { 13786 'use strict'; 13787 13788 var $window = $(window); 13789 var $body = $('body'); 13790 var $resMenuWrap = $('.mk-responsive-wrap'); 13791 var $post_nav = $('.mk-post-nav'); 13792 var $toolbar = $('.mk-header-toolbar'); 13793 var $resMenuLink = $('.mk-nav-responsive-link'); 13794 13795 // Flags 13796 var hasResMenu = ($resMenuWrap.length > 0); 13797 13798 //initial window and screen height 13799 var windowHeight = $window.height(); 13800 var screenHeight = screen.height; 13801 13802 // We keep this handler above hasResMenu flag. 13803 // Even if our header doesn't contain droppable responsive menu (in favor of fullscreen or side menu) 13804 // we still transform tollbar into collapsible menu part in responsive state 13805 $('.mk-toolbar-resposnive-icon').on('click', function(e) { 13806 e.preventDefault(); 13807 console.log('clicked'); 13808 if ($body.hasClass('toolbar-opened')) { 13809 $body.removeClass('toolbar-opened').addClass('toolbar-closed'); 13810 $toolbar.hide(); 13811 } else { 13812 $body.removeClass('toolbar-closed').addClass('toolbar-opened'); 13813 $toolbar.show(); 13814 } 13815 }); 13816 13817 13818 if ( ! hasResMenu && ! $('.vc_mk_header') ) return; 13819 13820 function toggleResMenu(e) { 13821 e.preventDefault(); 13822 var $this = $(this); 13823 var $headerInner = $this.parents('header'); 13824 var $resMenu = $headerInner.find('.mk-responsive-wrap'); 13825 var searchBox = $('.responsive-searchform .text-input'); 13826 var adminBarHeight = $('#wpadminbar').height(); /* Fix AM-1918 */ 13827 13828 if ($body.hasClass('mk-opened-nav')) { 13829 $this.removeClass('is-active'); 13830 $body.removeClass('mk-opened-nav').addClass('mk-closed-nav').trigger('mk-closed-nav'); 13831 $resMenu.hide(); 13832 $post_nav.removeClass('post-nav-backward'); 13833 } else { 13834 $this.addClass('is-active'); 13835 $body.removeClass('mk-closed-nav').addClass('mk-opened-nav').trigger('mk-opened-nav'); 13836 $resMenu.show(); 13837 $post_nav.addClass('post-nav-backward'); 13838 13839 } 13840 13841 //for iPhone 5 focus bug , remove search box focused class 13842 if(searchBox.hasClass('input-focused')){ 13843 searchBox.removeClass('input-focused'); 13844 } 13845 13846 } 13847 13848 $resMenuLink.each(function() { 13849 $(this).on('click', toggleResMenu); 13850 }); 13851 13852 $( window ).on( 'vc_reload', function () { 13853 $('.vc_mk_header .mk-nav-responsive-link').each(function() { 13854 $(this).on('click', toggleResMenu); 13855 }); 13856 } ) 13857 13858 var setResMenuHeight = function() { 13859 var height = $window.height() - MK.val.offsetHeaderHeight(0); 13860 $resMenuWrap.css('max-height', height); 13861 }; 13862 13863 // check if device virtual keyboard is active 13864 var isVirtualKeyboard = function() { 13865 var currentWindowHeight = $window.height(); 13866 var currentScreenHeight = screen.height; 13867 var searchBox = $('.responsive-searchform .text-input'); 13868 var searchBoxIsFocused = false; 13869 13870 //for iPhone 5 focus bug , add class for detect focus state 13871 searchBox.on('touchstart touchend', function(e) { 13872 searchBox.addClass('input-focused'); 13873 }); 13874 13875 searchBoxIsFocused = (searchBox.is(':focus') || searchBox.hasClass("input-focused")); 13876 13877 if ($body.hasClass('mk-opened-nav') && searchBoxIsFocused && currentScreenHeight == screenHeight && currentWindowHeight != windowHeight) { 13878 return true; 13879 } else { 13880 return false;
13881 } 13882 }; 13883 13884 var hideResMenu = function hideResMenu() { 13885 if (MK.utils.isResponsiveMenuState()) { 13886 13887 //when search box in responsive menu is focused , window resize fired but at this time responsive menu should be open 13888 if (!isVirtualKeyboard()) { 13889 // hide toggled menu and its states 13890 if ($body.hasClass('mk-opened-nav')) { 13891 $resMenuLink.filter('.is-active').trigger('click'); 13892 } 13893 // hide menu wrapper 13894 $resMenuWrap.hide(); 13895 13896 } 13897 } 13898 }; 13899 13900 $resMenuWrap.on('click', 'a', hideResMenu); 13901 13902}(jQuery)); 13903 13904(function($) { 13905 'use strict'; 13906 13907 var $header = $('.mk-header'); 13908 var hasHeader = ($header.length > 0); 13909 13910 if(!hasHeader && ! $('.vc_mk_header') ) return; 13911 13912 var $sticky_style = $header.attr('data-header-style'); 13913 13914 //if ($sticky_style !== 3) return; 13915 13916 $('.sidedash-navigation-ul > li').each(function() { 13917 var $this = $(this); 13918 13919 $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>'); 13920 }); 13921 13922 function toggleSubMenu(e) { 13923 e.preventDefault(); 13924 var $this = $(this); 13925 13926 if ($this.hasClass('mk-nav-sub-closed')) { 13927 $this.siblings('ul').slideDown(450).end().removeClass('mk-nav-sub-closed').addClass('mk-nav-sub-opened'); 13928 } else { 13929 $this.siblings('ul').slideUp(450).end().removeClass('mk-nav-sub-opened').addClass('mk-nav-sub-closed'); 13930 } 13931 }; 13932 13933 function toggleFullMenu(e) { 13934 e.preventDefault(); 13935 var $this = $(this), 13936 $body = $('body'), 13937 $fullscreen_box = $('.mk-fullscreen-nav'); 13938 13939 if ($this.hasClass('dashboard-style')){ 13940 if (!$this.hasClass('dashboard-active')) { 13941 $this.addClass('dashboard-active'); 13942 $body.addClass('dashboard-opened'); 13943 } else { 13944 $this.removeClass('dashboard-active'); 13945 $body.removeClass('dashboard-opened'); 13946 } 13947 }else if($this.hasClass('fullscreen-style')){ 13948 if (!$this.hasClass('fullscreen-active')) { 13949 $this.addClass('fullscreen-active'); 13950 $body.addClass('fullscreen-nav-opened'); 13951 $fullscreen_box.addClass('opened'); 13952 // MK.utils.scroll.disable(); 13953 } else { 13954 $this.removeClass('fullscreen-active'); 13955 $body.removeClass('fullscreen-nav-opened'); 13956 $fullscreen_box.removeClass('opened'); 13957 } 13958 } 13959 } 13960 13961 $('.mk-nav-arrow').each(function() { 13962 $(this).stop(true).on('click', toggleSubMenu); 13963 }); 13964 13965 $('.mk-dashboard-trigger').each(function() { 13966 $(this).on('click', toggleFullMenu); 13967 }); 13968 13969 $( window ).on( 'vc_reload', function () { 13970 $('.vc_mk_header .mk-dashboard-trigger').each(function() { 13971 $(this).on('click', toggleFullMenu); 13972 }); 13973 13974 $('.vc_mk_header .mk-nav-arrow').each(function() { 13975 $(this).stop(true).on('click', toggleSubMenu); 13976 }); 13977 } ); 13978 13979 $('html').on('click', function() { 13980 $('body').removeClass('dashboard-opened'); 13981 $('.mk-dashboard-trigger').removeClass('dashboard-active'); 13982 }); 13983 13984}(jQuery)); 13985 13986(function($) { 13987 'use strict'; 13988 13989 /** 13990 * Vertical menu 13991 */ 13992 var $verticalMenu = $('#mk-vm-menu'); 13993 var verticalMenu = function verticalMenu() { 13994 if(!$verticalMenu.data('vertical-menu') && !MK.utils.isResponsiveMenuState()) { 13995 $verticalMenu.dlmenu(); 13996 $verticalMenu.data('vertical-menu', true); 13997 } 13998 }; 13999 14000 verticalMenu(); 14001 $(window).on('resize', verticalMenu); 14002 14003}(jQuery)); 14004(function($) { 14005 'use strict'; 14006 14007 // WPML 14008 var $lang_item = $('.mk-main-navigation > .main-navigation-ul > .menu-item-language'); 14009 $lang_item.addClass('no-mega-menu').css('visibility', 'visible'); 14010 $('.mk-main-navigation .menu-item-language > a').addClass('menu-item-link'); 14011 14012})(jQuery); 14013(function($) { 14014 'use strict'; 14015 14016 var $header = $( '.mk-header' ).first(); // Theme header is always first, do not apply to shortcode headers. 14017 var hasHeader = ($header.length > 0); 14018 14019 if(!hasHeader) return; 14020 14021 var $window = $( window ); 14022 var $document = $( document ); 14023 var $headerHolder = $header.find( '.mk-header-holder' ); 14024 var $paddingWrapper = $header.find('.mk-header-padding-wrapper'); 14025 var config = $header.data(); 14026 14027 /** 14028 * Flags 14029 * @type {Boolean} 14030 */ 14031 var isStickyLazy = (config.stickyStyle === 'lazy'); 14032 var isStickyFixed = (config.stickyStyle === 'fixed'); 14033 var isStickySlide = (config.stickyStyle === 'slide'); 14034 14035 function isSticky() { // Check for sticky compatibility 14036 //return config.headerStyle !== 4; header style 4 needs sticky state for transparent header. 14037 return true; 14038 } 14039
14040 function isColorable() { // Check for coloring compatibility 14041 // TODO make it explicit in DOM that header is transparent as this is only scenario for colorable header 14042 // Monkey patch - exclude header styles that don't fit colorable 14043 return config.headerStyle !== 4; 14044 } 14045 14046 14047 /** 14048 * Change header skin 14049 */ 14050 function changeSkin( e, skin ) { 14051 $header.attr( 'data-transparent-skin', skin ); 14052 // Fix for class based skining 14053 var contrast = skin === 'light' ? 'dark' : 'light'; 14054 $header.addClass(skin + '-skin'); 14055 $header.removeClass(contrast + '-skin'); 14056 } 14057 14058 if(isColorable()) MK.utils.eventManager.subscribe( 'firstElSkinChange', changeSkin ); 14059 14060 14061 // Assign sticky scenarions 14062 if (isSticky() && isStickyLazy) { 14063 if(config.headerStyle !== 2) { 14064 lazySticky(); 14065 } 14066 } 14067 else if (isSticky() && isStickyFixed) fixedSticky(); 14068 else if (isSticky() && isStickySlide) slideSticky(); 14069 14070 /** 14071 * Sticky header behavior: Lazy 14072 */ 14073 function lazySticky() { 14074 var elClassHidden = 'header--hidden'; 14075 var elClassSticky = 'a-sticky'; 14076 var wScrollCurrent = 0; 14077 var wScrollBefore = 0; 14078 var wScrollDiff = 0; 14079 var wHeight = 0; 14080 var dHeight = 0; 14081 14082 var setSizes = function setSizes() { 14083 dHeight = $document.height(); 14084 wHeight = $window.height(); 14085 }; 14086 14087 var onScroll = function onScroll() { 14088 wScrollCurrent = MK.val.scroll(); 14089 wScrollDiff = wScrollBefore - wScrollCurrent; 14090 14091 if( wScrollCurrent <= 0 ) { // scrolled to the very top; element sticks to the top 14092 $headerHolder.removeClass( elClassHidden ); 14093 $header.removeClass( elClassSticky ); 14094 $('body').trigger('mk:header-unsticky'); 14095 14096 } else if( wScrollDiff > 0 && $headerHolder.hasClass( elClassHidden ) ) { // scrolled up; element slides in 14097 $headerHolder.removeClass( elClassHidden ); 14098 $header.addClass( elClassSticky ); 14099 $('body').trigger('mk:header-sticky'); 14100 14101 } else if( wScrollDiff < 0 ) { // scrolled down 14102 14103 if( wScrollCurrent + wHeight >= dHeight && $headerHolder.hasClass( elClassHidden ) ) { // scrolled to the very bottom; element slides in 14104 $headerHolder.removeClass( elClassHidden ); 14105 $header.addClass( elClassSticky ); 14106 $('body').trigger('mk:header-sticky'); 14107 } else { // scrolled down; element slides out 14108 $headerHolder.addClass( elClassHidden ); 14109 $header.removeClass( elClassSticky ); 14110 $('body').trigger('mk:header-unsticky'); 14111 } 14112 } 14113 14114 wScrollBefore = wScrollCurrent; 14115 }; 14116 14117 setSizes(); 14118 onScroll(); 14119 $window.on( 'resize', MK.utils.throttle( 100, setSizes ) ); 14120 $window.on( 'scroll', MK.utils.throttle( 500, onScroll ) ); 14121 } 14122 14123 14124 /** 14125 * Sticky header behavior: Fixed 14126 */ 14127 function fixedSticky() { 14128 var sticked = false; 14129 var scrollPos; 14130 14131 var toggleState = function toggleState() { 14132 scrollPos = MK.val.scroll() + MK.val.adminbarHeight(); 14133 14134 if( (scrollPos > MK.val.stickyOffset() ) && ! MK.utils.isResponsiveMenuState() ) { 14135 if(sticked) return; // stop if already sticked 14136 $header.addClass('a-sticky'); 14137 sticked = true; 14138 $('body').trigger('mk:header-sticky'); 14139 } else { 14140 if(!sticked) return; // stop if already unsticked 14141 $header.removeClass('a-sticky'); 14142 sticked = false; 14143 $('body').trigger('mk:header-unsticky'); 14144 } 14145 }; 14146 14147 toggleState(); 14148 $window.on( 'scroll', MK.utils.throttle( 100, toggleState) ); 14149 $window.on( 'resize', MK.utils.throttle( 100, toggleState) ); 14150 } 14151 14152 14153 /** 14154 * Sticky header behavior: Slide 14155 */ 14156 function slideSticky() { 14157 var sticked = false;
14158 var onScroll = function onScroll() { 14159 if (MK.val.scroll() > MK.val.stickyOffset()) { 14160 if(sticked) return; // stop if already sticked 14161 $header.addClass('pre-sticky'); 14162 $paddingWrapper.addClass('enable-padding'); 14163 setTimeout(function() { 14164 $header.addClass('a-sticky'); 14165 $('body').trigger('mk:header-sticky'); 14166 }, 1); 14167 sticked = true; 14168 } else { 14169 if(!sticked) return; // stop if already unsticked 14170 $header.removeClass('a-sticky'); 14171 $header.removeClass('pre-sticky'); 14172 $paddingWrapper.removeClass('enable-padding'); 14173 sticked = false; 14174 $('body').trigger('mk:header-unsticky'); 14175 } 14176 }; 14177 14178 onScroll(); 14179 $window.on( 'scroll', MK.utils.throttle( 100, onScroll) ); 14180 } 14181 14182 // TODO 14183 // var pageIntro = $('body').data('intro'); // block scrolling on page intro 14184 14185 14186})( jQuery ); 14187 14188(function($){ 14189 'use strict'; 14190 14191 // Do not assign at all if no touch events 14192 var hasTouchscreen = ('ontouchstart' in document.documentElement); 14193 if(!hasTouchscreen) return; 14194 14195 $('.mk-main-navigation .menu-item-has-children').each( normalizeClick ); 14196 14197 function normalizeClick() { 14198 $(this).on('click', handleClick); 14199 } 14200 14201 // 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. 14202 // 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 14203 function handleClick(e) { 14204 var $this = $(e.currentTarget); 14205 var $child = $this.find('> ul'); 14206 var isVisible = $child.css('display') !== 'none'; 14207 14208 if(!isVisible) { 14209 e.preventDefault(); 14210 e.stopPropagation(); 14211 } 14212 } 14213 14214}(jQuery)); 14215(function( $ ) { 14216 'use strict'; 14217 14218 MK.ui.preloader = { 14219 dom : { 14220 overlay: '.mk-body-loader-overlay' 14221 }, 14222 14223 hide : function hide() { 14224 $( this.dom.overlay ).fadeOut(600, "easeInOutExpo", function() { 14225 $('body').removeClass('loading'); 14226 //$( this ).remove(); 14227 }); 14228 } 14229 }; 14230 14231})( jQuery ); 14232(function($) { 14233 'use strict'; 14234 14235 var _ajaxUrl = MK.core.path.ajaxUrl; 14236 var _instances = {}; 14237 14238 MK.utils.ajaxLoader = function ajaxLoader(el) { 14239 // retrun a cached instance to have control over state from within multiple places 14240 // we may need for example to reset pageId when do filtering. It is really one instance that controls both filtering and pagination / load more 14241 var id = '#' + ($(el).attr('id')); 14242 if(typeof _instances[id] !== 'undefined') return _instances[id]; 14243 14244 // else lets start new instance 14245 this.id = id; 14246 this.el = el; 14247 this.isLoading = false; 14248 this.xhrCounter = 0; 14249 }; 14250 14251 MK.utils.ajaxLoader.prototype = { 14252 init: function init() { 14253 // prevent double initialization of we return an instance 14254 if ( this.initialized && typeof window.vc_iframe === 'undefined' ) { 14255 return; 14256 } 14257 14258 this.createInstance(); 14259 this.cacheElements(); 14260 14261 this.initialized = true; 14262 }, 14263 14264 cacheElements: function cacheElements() { 14265 this.$container = $(this.el); 14266 this.id = '#' + (this.$container.attr('id')); 14267 this.categories = this.$container.data('loop-categories'); 14268 14269 this.data = {}; 14270 this.data.action = 'mk_load_more'; 14271 this.data.query = this.$container.data('query'); 14272 this.data.atts = this.$container.data('loop-atts'); 14273 this.data.loop_iterator = this.$container.data('loop-iterator'); 14274 this.data.author = this.$container.data('loop-author'); 14275 this.data.posts = this.$container.data('loop-posts'); 14276 this.data.safe_load_more = this.$container.siblings('#safe_load_more').val(); 14277 this.data._wp_http_referer = this.$container.siblings('input[name="_wp_http_referer"]').val(); 14278 this.data.paged = 1; 14279 this.data.maxPages = this.$container.data('max-pages'); 14280 this.data.term = this.categories; 14281 }, 14282 14283 createInstance: function() { 14284 _instances[this.id] = this; 14285 }, 14286 14287 load: function load(unique) { 14288 var self = this; 14289 var seq = ++this.xhrCounter; 14290 this.isLoading = true; 14291 14292 // If mk-ajax-loaded-posts span exists, get the post ids 14293 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 14294 var loaded_posts = this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts'); 14295
14296 // Do not send looaded posts for Classic Pagination Navigation 14297 if ( this.$container.attr('data-pagination-style') != 1 ) { 14298 self.data.loaded_posts = loaded_posts.split(','); 14299 } 14300 } 14301 14302 return $.when( 14303 $.ajax({ 14304 url : _ajaxUrl, 14305 type : "POST", 14306 data : self.data 14307 }) 14308 ).done(function(response) { 14309 self.onDone(response, unique, seq); 14310 }); 14311 }, 14312 14313 onDone: function(response, unique, seq) { 14314 if(seq === this.xhrCounter) { 14315 var self = this; 14316 14317 response = $.parseJSON(response); 14318 response.unique = unique; 14319 response.id = this.id; 14320 14321 // If mk-ajax-loaded-posts span exists, update current post ids 14322 // with new post ids from server's response 14323 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 14324 this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts', response.loaded_posts); 14325 } 14326 14327 this.setData({ 14328 maxPages: response.maxPages, 14329 found_posts: response.found_posts, 14330 loop_iterator: response.i 14331 }); 14332 14333 // Preload images first by creating object from returned content. 14334 // mk_imagesLoaded() method will create a promise that gets resolved when all images inside are loaded. 14335 // Our ajaxLoad is somehow more similar to window.onload event now. 14336 $(response.content).mk_imagesLoaded().then(function() { 14337 MK.utils.eventManager.publish('ajaxLoaded', response); 14338 self.isLoading = false; 14339 self.initNewComponents(); 14340 }); 14341 14342 } else console.log('XHR request nr '+ seq +' aborted'); 14343 14344 }, 14345 14346 setData: function setData(atts) { 14347 for(var att in atts) { 14348 if(att === 'term' && atts[att] === '*') this.data.term = ''; 14349 else this.data[att] = atts[att]; 14350 } 14351 }, 14352 14353 getData: function getData(att) { 14354 return this.data[att]; 14355 }, 14356 14357 initNewComponents: function initNewComponents() { 14358 // Legacy scripts reinit 14359 window.ajaxInit(); 14360 setTimeout(window.ajaxDelayedInit, 1000); 14361 // New way to init apended things 14362 MK.core.initAll(this.el); 14363 } 14364 }; 14365 14366}(jQuery)); 14367MK.component.BackgroundImageSetter = (function ($) { 14368 14369 'use strict'; 14370 14371 var module = {}; 14372 14373 14374 /*---------------------------------------------------------------------------------*/ 14375 /* Private Variables 14376 /*---------------------------------------------------------------------------------*/ 14377 14378 /** 14379 * Take all elements with data-mk-img-set attribute and evaluate best image according to given device orientation and resolution, 14380 * sets style for backround-image on the same node element. * 14381 */ 14382 14383 var $win = $(window), 14384 // $layers = $('[data-mk-img-set]'), 14385 screen = getScreenSize(), 14386 orientation = getOrientation(), 14387 device = getDevice(), 14388 lastOrientation = orientation, 14389 lastDevice = device; 14390 14391 14392 /*---------------------------------------------------------------------------------*/ 14393 /* Private Methods 14394 /*---------------------------------------------------------------------------------*/ 14395 14396 function run($layers) { 14397 $layers.filter( function() { 14398 return !this.hasAttribute("mk-img-loaded"); 14399 }).each(applyBg); 14400 } 14401 14402 // Keep our main side effect out of calculations so they can be run once before loop of applying bg as a result 14403 function applyBg() { 14404 var $this = $(this), 14405 imgs = $this.data('mk-img-set'); 14406 14407 $this.css('background-image', 'url('+ module.getImage(imgs) +')'); 14408 $this.find('.mk-adaptive-image').attr('src', module.getImage(imgs)); 14409 } 14410 14411 // Keep track of current screen size while resizing but update device reference 14412 // and reapply backgrounds only when we discover switch point 14413 function handleResize($layers) { 14414 updateScreenSize(); 14415 if(hasSwitched()) { 14416 updateDevice(); 14417 run($layers); 14418 } 14419 } 14420 14421 function getScreenSize() { 14422 return { 14423 w: $win.width(), 14424 h: $win.height() 14425 }; 14426 } 14427 14428 // Name our device classes and add them id which simply means which one is wider 14429 function getDevice() { 14430 if (screen.w > 1024) return {class: 'desktop', id: 2}; 14431 else if(screen.w > 736) return {class: 'tablet', id: 1}; 14432 else return {class: 'mobile', id: 0}; 14433 } 14434 14435 function getOrientation() { 14436 if(screen.w > screen.h) return 'landscape'; 14437 else return 'portrait'; 14438 } 14439 14440 function updateScreenSize() { 14441 screen = getScreenSize(); 14442 } 14443 14444 function updateDevice() { 14445 if(lastOrientation !== orientation) orientation = lastOrientation; 14446 // Switch device only if going from smaller size to bigger. 14447 // Bigger to smaller is perfectly handled by browsers and doesn't require change and reupload 14448 if(lastDevice.id > device.id) device = lastDevice; 14449 } 14450 14451 function hasSwitched() { 14452 lastOrientation = getOrientation(); 14453 lastDevice = getDevice(); 14454 14455 if(lastOrientation !== orientation || lastDevice.class !== device.class) return true; 14456 else return false;
14457 } 14458 14459 14460 /*---------------------------------------------------------------------------------*/ 14461 /* Public Methods 14462 /*---------------------------------------------------------------------------------*/ 14463 14464 // As desired image might be not available we have to evaluate the best match. 14465 module.getImage = function (imgs) { 14466 if (imgs['responsive'] === 'false') { 14467 return (imgs['landscape']['desktop']) ? imgs['landscape']['desktop'] : (imgs['landscape']['external'] ? imgs['landscape']['external'] : ''); 14468 14469 } 14470 var hasOrientation = !!imgs[orientation]; 14471 // there are only two orientations now and we may get them by string name if both are there 14472 // 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. 14473 // 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 14474 var imgOriented = imgs[ (hasOrientation ? orientation : Object.keys(imgs)[0]) ], 14475 imgExact = (imgOriented[device.class]) ? imgOriented[device.class] : (imgOriented['external'] ? imgOriented['external'] : ''); 14476 return imgExact; 14477 } 14478 14479 module.init = function ($layers) { 14480 14481 // Run and bind 14482 run($layers); 14483 $layers.attr('mk-img-loaded', ''); 14484 }; 14485 14486 module.onResize = function ($layers) { 14487 $win.on('resize', MK.utils.throttle( 500, function() { 14488 handleResize($layers); 14489 })); 14490 }; 14491 14492 return module; 14493 14494}(jQuery)); 14495 14496 14497jQuery(function($) { 14498 14499 var init = function init() { 14500 // Get All Layers, Excluding Edge Slider and Page Section 14501 $allLayers = $('[data-mk-img-set]').filter(function(index) { 14502 return !$(this).hasClass('mk-section-image') && !$(this).hasClass('background-layer') && !$(this).hasClass('mk-video-section-touch'); 14503 });; 14504 14505 // Handle the resize 14506 MK.component.BackgroundImageSetter.onResize($allLayers); 14507 14508 // Set all the BG Layers 14509 MK.component.BackgroundImageSetter.init($allLayers); 14510 } 14511 init(); 14512 14513 $(window).on('vc_reload', init); 14514 14515}); 14516 14517(function( $ ) { 14518 'use strict'; 14519 14520 var val = MK.val; 14521 14522 MK.component.FullHeight = function( el ) { 14523 var $window = $( window ), 14524 $this = $( el ), 14525 config = $this.data( 'fullheight-config' ), 14526 container = document.getElementById( 'mk-theme-container' ), 14527 minH = (config && config.min) ? config.min : 0, 14528 winH = null, 14529 height = null, 14530 update_count = 0, 14531 testing = MK.utils.getUrlParameter('testing'), 14532 offset = null; 14533 14534 // We need to provide height on the same specificity level for workaround to IE bug 14535 // connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview 14536 // stackoverflow.com/questions/19371626/flexbox-not-centering-vertically-in-ie 14537 if(MK.utils.browser.name === ('IE' || 'Edge')) $this.css( 'height', '1px' ); 14538 14539 var update = function() { 14540 14541 if(update_count === 0) { 14542 winH = $window.height(); 14543 // for correct calculate 14544 offset = $this.offset().top - 1; 14545 height = Math.max(minH, winH - val.offsetHeaderHeight( offset )); 14546 $this.css( 'min-height', height ); 14547 if(testing !== undefined ) 14548 update_count++; 14549 } 14550 14551 }; 14552 14553 // TODO remove scroll listener by dynamic offset reader 14554 var init = function() { 14555 update(); 14556 $window.on( 'resize', update ); 14557 $window.on( 'scroll', update ); 14558 window.addResizeListener( container, update ); 14559 }; 14560 14561 return { 14562 init : init 14563 }; 14564 }; 14565 14566})( jQuery ); 14567 14568 14569(function( $ ) { 14570 'use strict'; 14571 14572 var core = MK.core, 14573 utils = MK.utils, 14574 path = MK.core.path; 14575 14576 14577 MK.ui.FullScreenGallery = function( element, settings ) { 14578 this.element = element; 14579 this.config = settings; 14580 14581 this.isFullScreen = false; 14582 }; 14583 14584 14585 // preload slick PLUGIN TO USE THIS 14586 MK.ui.FullScreenGallery.prototype = { 14587 dom : { 14588 fullScrBtn : '.slick-full-screen', 14589 exitFullScrBtn : '.slick-minimize', 14590 playBtn : '.slick-play', 14591 pauseBtn : '.slick-pause', 14592 shareBtn : '.slick-share', 14593 socialShare : '.slick-social-share', 14594 wrapper : '.slick-slider-wrapper', 14595 slider : '.slick-slides', 14596 slides : '.slick-slide', 14597 dots : '.slick-dot', 14598 active : '.slick-active', 14599 hiddenClass : 'is-hidden', 14600 dataId : 'slick-index' 14601 }, 14602 14603 tpl: { 14604 dot : '<div class="slick-dot"></div>', 14605 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>', 14606 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>' 14607 }, 14608 14609 init : function() { 14610 var self = this; 14611 14612 // core.loadDependencies([ path.plugins + 'slick.js' ], function() { 14613 self.cacheElements(); 14614 self.getViewportSizes(); 14615 self.updateSizes( 'window' ); 14616 self.create(); 14617 // update cache with elements propagated by plugin 14618 self.updateCacheElements(); 14619 self.createPagination(); 14620 self.bindEvents(); 14621 // }); 14622 }, 14623 14624 create : function() { 14625 var self = this; 14626 14627 this.slick = this.$gallery.slick({ 14628 dots: true, 14629 arrows: true, 14630 infinite: true, 14631 speed: 300, 14632 slidesToShow: 1, 14633 centerMode: true, 14634 centerPadding: '0px', 14635 variableWidth: true, 14636 autoplay: false, 14637 autoplaySpeed: 3000, 14638 useTransform: true, 14639 prevArrow: self.tpl.prev, 14640 nextArrow: self.tpl.next, 14641 customPaging: function(slider, i) { 14642 return self.tpl.dot; 14643 }, 14644 }); 14645 }, 14646 14647 cacheElements : function() { 14648 this.$window = $( window ); 14649 this.$gallery = $( this.element ); 14650 14651 this.$fullScrBtn = $( this.dom.fullScrBtn ); 14652 this.$exitFullScrBtn = $( this.dom.exitFullScrBtn ); 14653 this.$playBtn = $( this.dom.playBtn ); 14654 this.$pauseBtn = $( this.dom.pauseBtn ); 14655 this.$shareBtn = $( this.dom.shareBtn ); 14656 this.$socialShare = $( this.dom.socialShare ); 14657 14658 this.$wrapper = $( this.dom.wrapper ); 14659 this.$slider = $( this.dom.slider ); 14660 this.$slides = $( this.dom.slides ); 14661 this.$imgs = this.$slides.find( 'img' ); 14662 // store reference to initial images without slides appended by pugin 14663 // - needed for creating of pagination 14664 this.$originalImgs = this.$imgs; 14665 }, 14666 14667 updateCacheElements : function() { 14668 this.$slides = $( this.dom.slides ); 14669 this.$imgs = this.$slides.find( 'img' ); 14670 this.$dots = $( this.dom.dots ); 14671 }, 14672 14673 bindEvents : function() { 14674 var self = this; 14675 this.$fullScrBtn.on( 'click', this.toFullScreen.bind( this ) ); 14676 this.$exitFullScrBtn.on( 'click', this.exitFullScreen.bind( this ) ); 14677 this.$playBtn.on( 'click', this.play.bind( this ) ); 14678 this.$pauseBtn.on( 'click', this.pause.bind( this ) ); 14679 this.$shareBtn.on( 'click', this.toggleShare.bind( this ) ); 14680 this.$socialShare.on( 'click', 'a', this.socialShare.bind( this ) ); 14681 this.$window.on( 'resize', this.onResize.bind( this ) ); 14682 this.$window.on( 'keydown', function(e) { 14683 if(e.keyCode === 39) self.$gallery.slick('slickNext'); 14684 if(e.keyCode === 37) self.$gallery.slick('slickPrev'); 14685 }); 14686 $( document ).on( 'fullscreenchange mozfullscreenchange webkitfullscreenchange msfullcreenchange', this.exitFullScreen.bind( this ) ); 14687 }, 14688 14689 getViewportSizes : function() { 14690 this.screen = { 14691 w: screen.width, 14692 h: screen.height 14693 }; 14694 this.window = { 14695 w: this.$window.width(), 14696 h: this.$window.height() 14697 }; 14698 }, 14699 14700 updateSizes : function( viewport ) { 14701 this.$wrapper.width( this[ viewport ].w ); 14702 this.$wrapper.height( '100%' ); 14703 this.$imgs.height( '100%'); 14704 }, 14705 14706 createPagination : function() { 14707 var self = this; 14708 this.$dots.each( function( i ) { 14709 var img = self.$originalImgs.eq( i ).attr( 'src' ); 14710 14711 $( this ).css({ 14712 'background-image': 'url('+ img +')' 14713 }); 14714 }); 14715 }, 14716 14717 play : function(e) { 14718 e.preventDefault(); 14719 this.$playBtn.addClass( this.dom.hiddenClass ); 14720 this.$pauseBtn.removeClass( this.dom.hiddenClass ); 14721 $( this.element ).slick( 'slickPlay' ); 14722 }, 14723 14724 pause : function(e) { 14725 e.preventDefault(); 14726 this.$pauseBtn.addClass( this.dom.hiddenClass ); 14727 this.$playBtn.removeClass( this.dom.hiddenClass ); 14728 $( this.element ).slick( 'slickPause' ); 14729 }, 14730 14731 toggleShare : function(e) { 14732 e.preventDefault(); 14733 this.$socialShare.toggleClass( this.dom.hiddenClass ); 14734 }, 14735 14736 getCurentId : function() { 14737 return this.$slides.filter( this.dom.active ).data( this.dom.dataId ); 14738 }, 14739 14740 toFullScreen : function() { 14741 var self = this; 14742 14743 this.$fullScrBtn.addClass( this.dom.hiddenClass ); 14744 this.$exitFullScrBtn.removeClass( this.dom.hiddenClass ); 14745 14746 this.$slider.hide().fadeIn( 500 ); 14747 utils.launchIntoFullscreen( document.documentElement ); 14748 this.updateSizes( 'screen' ); 14749 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14750 14751 // Update state with delay so we avoid triggering exitFullScreen fn from 14752 // fullscreenchange event 14753 setTimeout( function() { 14754 self.isFullScreen = true; 14755 }, 1000); 14756 }, 14757 14758 exitFullScreen : function() { 14759 if( this.isFullScreen ) { 14760 this.$exitFullScrBtn.addClass( this.dom.hiddenClass ); 14761 this.$fullScrBtn.removeClass( this.dom.hiddenClass ); 14762 14763 utils.exitFullscreen(); 14764 this.updateSizes( 'window' ); 14765 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14766 14767 this.isFullScreen = false;
14768 } 14769 14770 }, 14771 14772 onResize : function() { 14773 this.getViewportSizes(); 14774 this.updateSizes( this.isFullScreen ? 'screen' : 'window' ); 14775 $( this.element ).slick( 'refresh' ); 14776 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14777 this.updateCacheElements(); 14778 this.createPagination(); 14779 }, 14780 14781 socialShare : function( e ) { 14782 e.preventDefault(); 14783 var $this = $( e.currentTarget ), 14784 network = $this.data( 'network' ), 14785 id = this.config.id, 14786 url = this.config.url, 14787 title = this.$wrapper.find( '.slick-title' ).text(), 14788 name; 14789 var picture = this.$slides.filter( this.dom.active ).children().first().attr( 'src' ); 14790 switch( network ) { 14791 case 'facebook': 14792 url = 'https://www.facebook.com/sharer/sharer.php?picture=' + picture+'&u=' + url + '#id=' + id; 14793 name = 'Facebook Share'; 14794 break; 14795 case 'twitter': 14796 url = 'http://twitter.com/intent/tweet?text=' + url + '#id=' + id; 14797 name = 'Twitter Share'; 14798 break; 14799 case 'pinterest': 14800 url = 'http://pinterest.com/pin/create/bookmarklet/?media=' + picture + '&url=' + url + '&is_video=false&description=' + title; 14801 // other available link paranmeters: media, description 14802 name = 'Pinterest Share'; 14803 break; 14804 14805 } 14806 14807 window.open( url, name, "height=380 ,width=660, resizable=0, toolbar=0, menubar=0, status=0, location=0, scrollbars=0" ); 14808 } 14809 }; 14810 14811})( jQuery ); 14812(function($) { 14813 'use strict'; 14814 14815 MK.component.Grid = function( el ) { 14816 var $container = $(el); 14817 var config = $container.data( 'grid-config' ); 14818 var isSlideshow = $container.closest('[data-mk-component="SwipeSlideshow"]').length; 14819 var miniGridConfig = { 14820 container: el, 14821 item: config.item + ':not(.is-hidden)', 14822 gutter: 0 14823 }; 14824 14825 var init = function init(){ 14826 // Flags for cancelling usage goes first : 14827 // Quit early if we discover that Grid is used inside SwipeSlideshow as it brings bug with crossoverriding positioning 14828 // + grid is not really needed as we have single row all handled by slider. 14829 // It happens only in woocommerce carousel as of hardcoded Grid in loop-start.php 14830 if(isSlideshow) return; 14831 MK.core.loadDependencies([ MK.core.path.plugins + 'minigrid.js' ], create); 14832 }; 14833 14834 // Remove el hidden without adding proper class 14835 var prepareForGrid = function prepareForGrid() { 14836 var $item = $(this); 14837 var isHidden = ($item.css('display') === 'none'); 14838 if(isHidden) $item.addClass('is-hidden'); 14839 else $item.removeClass('is-hidden'); 14840 }; 14841 14842 var create = function create() { 14843 var timer = null; 14844 14845 function draw() { 14846 // Prevent plugin breaking when feeding it with hidden elements 14847 $container.find(config.item).each( prepareForGrid ); 14848 minigrid(miniGridConfig); 14849 } 14850 14851 function redraw() { 14852 if (timer) clearTimeout(timer); 14853 timer = setTimeout(draw, 100); 14854 } 14855 14856 // init 14857 draw(); 14858 // If reinitializing drop existing event handler 14859 $(window).off('resize', redraw); 14860 $(window).on('resize', redraw); 14861 MK.utils.eventManager.subscribe('item-expanded', redraw); 14862 MK.utils.eventManager.subscribe('ajaxLoaded', redraw); 14863 MK.utils.eventManager.subscribe('staticFilter', redraw); 14864 }; 14865 14866 return { 14867 init : init 14868 }; 14869 }; 14870 14871})(jQuery); 14872 14873 14874 14875 14876 14877 14878 14879 14880/** 14881 * ICON FACTORY @ Maki 14882 * 14883 * Javascript part that translates css styles into link to server side manufacturer. 14884 * By passing extra args we generate icons as needed on server and send them back with ajax and attch them to DOM here. 14885 * 14886 * 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. 14887 * 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. 14888 * 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. 14889 * 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. 14890 * 14891 * Prons: 14892 * - automated process where we can mimic old behaviour 14893 * - possible bugs are easy to patch 14894 * - 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) 14895 * - icon generation moved to server side where our main templating is already happening 14896 * - we can easily switch how our Icon should be outputed - inline, img, object, iframe. Whatever our situation requires (img for responsive icons for example) 14897 * 14898 * Cons: 14899 * - possible bugs based on different nature of svgs / fonts. Need to be manually fixed
14900 * - some manual work to straighten things - litle css refactors here and there 14901 * - as the mapping the icons happens in server side, it may cause CPU bottlenecks and should be cashed in DB for repetitve usage. 14902 * 14903 * 14904 * TODO Refactor to config obj as parameter for easier passing it. Also decouple more - especially loops 14905 */ 14906(function ($) { 14907 'use strict'; 14908 14909 // As long as we can manage this manually it is better like so. We avoid delay based on waiting for ajax to finish 14910 // $.get( MK.core.path.theme + '/assets/icons/icon-families.json', createAll); 14911 var families = [ 14912 ['awesome-icons' , 'mk-icon-'], // [family, prefix] 14913 ['icomoon' , 'mk-moon-'], 14914 ['pe-line-icons' , 'mk-li-'], 14915 ['theme-icons' , 'mk-jupiter-icon-'] 14916 ]; 14917 14918 // Our css contains this extends for particular families of fonts. For now we automate the process and extend it here as well 14919 var extend = { 14920 'awesome-icons' : [], 14921 'icomoon' : [], 14922 'pe-line-icons' : [], 14923 'theme-icons' : [] 14924 }; 14925 14926 // Cache holder for quick access to repeating icons. config / svg 14927 var _cache = {}; 14928 var _cacheId = 0; 14929 // Merge configs to call it with single ajax 14930 var _config = []; 14931 // Counter for all families. We merge all our families for single ajax request and countdown when to trigger the download. 14932 var _roundCount = 0; 14933 // Mapper for icons - config / array with nodes 14934 var _iconMap = {}; 14935 14936 14937 // Closure to merge $icons together and to run the download when we counted down icon families to 0 14938 var getIconsSprite = (function() { 14939 var $icons = null; 14940 var iterator = 0; 14941 14942 function run(callback) { 14943 var config = encodeURIComponent(JSON.stringify(_config)); 14944 14945 $.ajax({ 14946 url : MK.core.path.ajaxUrl, 14947 // dataType: 'xml', 14948 method: 'POST', 14949 data: {action : 'mk_get_icon', iterator: iterator++, config: config}, 14950 success: function(sprite) { 14951 callback(sprite, $icons); 14952 _config = []; 14953 _iconMap = {}; 14954 $icons = null; 14955 }, 14956 error: function(err) { 14957 console.log('Icon load problem'); 14958 } 14959 }); 14960 } 14961 14962 return function(callback, $els, count) { 14963 if(!$icons) $icons = $els; 14964 else $icons.add($els); 14965 // console.log(count); 14966 if(!count) run(callback); 14967 }; 14968 }()); 14969 14970 // Wait for DOM manipulations and run where really needed 14971 $(window).on('load', function() { 14972 setTimeout(function() { 14973 createAll(document); 14974 if($('.mk-header').length) createAll('.mk-header'); 14975 if($('.js-flexslider, .mk-flexslider').length) createAll('.js-flexslider, .mk-flexslider'); 14976 if($('.mk-accordion').length) createAll('.mk-accordion'); 14977 }, 1000); 14978 }); 14979 14980 MK.utils.eventManager.subscribe('ajaxLoaded', function() { 14981 // Wait for actuall DOM insertion 14982 setTimeout(createAll, 100, '.js-loop'); 14983 }); 14984 14985 MK.utils.eventManager.subscribe('ajax-preview', function() { 14986 setTimeout(createAll, 100, '.ajax-container'); 14987 }); 14988 14989 MK.utils.eventManager.subscribe('photoAlbum-open', function() { 14990 setTimeout(createAll, 100, '.gallery-share'); 14991 }); 14992 14993 MK.utils.eventManager.subscribe('quickViewOpen', function() { 14994 setTimeout(createAll, 300, '.mk-modal-content'); 14995 }); 14996 14997 // Our main runner that creates all icons in given scope 14998 function createAll(scope) { 14999 // iterate through families 15000 for(var i = 0, l = families.length; i < l; i++) { 15001 var family = families[i][0]; 15002 var prefix = families[i][1]; 15003 var $icons = getIcons(family, prefix, scope); 15004 if($icons.length) { 15005 _roundCount++; 15006 // 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. 15007 // We need it to pass all $icons first so we can merge all families before ajax request. 15008 setTimeout(createIcons, 0, $icons, family, prefix); 15009 } 15010 } 15011 } 15012 15013 // Grab all els in given scope that contains our prefix, anywhere in class attribute. Extend if needed 15014 function getIcons(family, prefix, scope) { 15015 var $scope = $(scope); 15016 var $icons = $scope.find('[class*='+ prefix +']'); 15017 var extraClassNames = extend[family]; 15018 // if nothing to extend just return what we have till now 15019 if(!extraClassNames) return $icons; 15020 // Extend it with our css garbish
15021 extraClassNames.forEach(function(className) { 15022 var $icon = $scope.find(className); 15023 $icons = $icons.add($icon); 15024 }); 15025 // serve all 15026 return $icons; 15027 } 15028 15029 15030 function createIcons($icons, family, prefix, i, unicode) { 15031 var id = i || 0; 15032 var icon = $icons[id]; 15033 15034 // quick check point, if no more icons break recursion 15035 if(!icon) { 15036 _roundCount--; 15037 getIconsSprite(insertIcons, $icons, _roundCount, _config); 15038 return; 15039 } 15040 15041 var css = getComputedStyle( icon, ':before'); 15042 var classAttr = icon.getAttribute('class'); 15043 var name = (classAttr) ? matchClass(classAttr.split(' '), prefix) : false; 15044 var h = getComputedStyle(icon).fontSize; 15045 var config = createConfig(css, name, family, unicode, h); 15046 var cache = JSON.stringify(config); 15047 15048 if(!config) { 15049 // 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) 15050 // As we expect empty response from server, we just skip to next icon 15051 15052 } else if(_cache[cache]) { 15053 // Use cached icon when possible 15054 // 15055 if(typeof _iconMap[cache] === 'undefined') _iconMap[cache] = [$icons.eq(id)]; 15056 else _iconMap[cache].push($icons.eq(id)); 15057 15058 } else { 15059 if(typeof _iconMap[cache] === 'undefined') _iconMap[cache] = [$icons.eq(id)]; 15060 else _iconMap[cache].push($icons.eq(id)); 15061 15062 _cache[cache] = _cacheId.toString(); // number as string so we could do [var] key lookup after reversal 15063 config.id = _cacheId; // extend config for server only 15064 _config.push(config); 15065 15066 _cacheId++; // increment after caching 15067 } 15068 15069 createIcons($icons, family, prefix, ++id); 15070 } 15071 15072 function insertIcons(sprite, $icons) { 15073 var $sprite = $(sprite); 15074 var $svgs = $sprite.find('svg'); 15075 var idMap = invert(_cache); 15076 15077 $sprite.each(function() { 15078 var $svg = $(this); 15079 var id = $svg.attr('data-cacheid'); // read as string 15080 var configKey = idMap[id]; 15081 15082 _cache[configKey] = this; 15083 // console.log(id, this) 15084 }); 15085 15086 // console.log('sprite',sprite) 15087 // 15088 15089 Object.keys(_iconMap).forEach(function (cacheKey) { 15090 _iconMap[cacheKey].forEach(function($icons) { 15091 $icons.each(function() { 15092 var $svg = $(_cache[cacheKey]).clone(); 15093 var $icon = $(this); 15094 15095 // console.log($icon[0], _cache[cacheKey]); 15096 15097 function remove() { 15098 // exclude problematic stuff 15099 if($icon.parents('.pricing-features')) return; 15100 $icon.not('.mk-jupiter-icon-xing') 15101 .not('.mk-jupiter-icon-square-xing') 15102 .not('.mk-jupiter-icon-simple-xing') 15103 .find('.mk-svg-icon') 15104 .not('[data-name="mk-moon-zoom-in"]') 15105 .remove(); // and remove else to reapply styles with new loaded svg 15106 } 15107 15108 if($svg.length ) remove(); 15109 15110 if(!$icon.find('svg').length) { 15111 if($icon.parents('.widget ul').length) $icon.prepend($svg); 15112 else $icon.append($svg); 15113 } 15114 }); 15115 }); 15116 }); 15117 15118 // Notify rest of app 15119 MK.utils.eventManager.publish('iconsInsert'); 15120 } 15121 15122 // Prepare configuration for server side. We need to grab icons and manipulate them a little there 15123 function createConfig(css, name, family, unicode, height) { 15124 var hasGradient = checkGradient(css); 15125 var hasDirection = extractGradient('direction', css.background); 15126 15127 var config = { 15128 family: family, 15129 // this is mostly for Extends. Because we've been customizing singular elements and assigning content unicode manually, 15130 // now we don't have any proper anchor for usage with our json map 15131 unicode: (unicode) ? unicode : decodeUnicode(css.content), 15132 name: name, 15133 // fill: css.color, 15134 gradient_type: hasGradient ? extractGradient('type' , css.background) : false, 15135 gradient_start: hasGradient ? extractGradient('start', css.background) : false, 15136 gradient_stop: hasGradient ? extractGradient('stop' , css.background) : false, 15137 gradient_direction: hasDirection ? extractGradient('direction', css.background).replace(' ', '-') : false, 15138 height: height 15139 }; 15140 15141 // If there is no name or unicode the whole config is invalid 15142 if(!config.name && !config.unicode) return false;
15143 else return config; 15144 } 15145 15146 // Extract class name that represents the icon that we try to generate 15147 function matchClass(classes, prefix) { 15148 // var missing = ''; 15149 for(var i = 0, l = classes.length; i < l; i++) { 15150 if(classes[i].indexOf(prefix) !== -1) return classes[i]; // return first matched class name 15151 // else missing += ' .' + classes[i]; // collect not matching classes 15152 } 15153 // if we didn't returned anything within loop we output notification here. Keep it for testing only 15154 // console.log('Possibility of missing icons for element with classes:' + missing); 15155 } 15156 15157 // Check if background conatains gradient declaration and return bg string if so or false 15158 function checkGradient(css) { 15159 var bg = css.background; 15160 if(bg.indexOf('radial') !== -1 || bg.indexOf('linear') !== -1) return bg; 15161 else return false; 15162 } 15163 15164 // Extract background attribiutes from bg string. 15165 // Multipurpose function which returns part we ask for 15166 function extractGradient(attr, grad) { 15167 // quit if gradient is falsy 15168 // console.log(grad); 15169 if(!grad) return false; 15170 15171 var isRadial = grad.indexOf('radial') !== -1; 15172 var isLinear = grad.indexOf('linear') !== -1; 15173 var hasDirection = grad.indexOf('(to') !== -1; 15174 var f, t; // from, to 15175 15176 if(attr === 'type') { 15177 if(isRadial) return 'radial'; 15178 if(isLinear) return 'linear'; 15179 15180 } else if(attr === 'start') { 15181 f = getStrPosition(grad, 'rgb(', 1); 15182 t = getStrPosition(grad, '0%' , 1); 15183 15184 } else if(attr === 'stop') { 15185 f = getStrPosition(grad, 'rgb(', 2); 15186 t = getStrPosition(grad, '100%', 1); 15187 15188 } else if(attr === 'direction') { 15189 if(!hasDirection) return false; 15190 f = getStrPosition(grad, '(to', 1) + 4; 15191 t = getStrPosition(grad, ', rgb(', 1); 15192 15193 } else { 15194 return false; 15195 } 15196 15197 return grad.slice(f, t); 15198 } 15199 15200 // Helper for getting indexOf but with lookup for instance of the string 15201 // str - string to search 15202 // m - what we're looking for 15203 // i - instance, starting at 1 15204 function getStrPosition(str, m, i) { 15205 return str.split(m, i).join(m).length; 15206 } 15207 15208 // JS outputs unicode results rather than code representation. This helper function does the job 15209 function decodeUnicode(content) { 15210 // escape() kills \ escape signature and outputs it as %u 15211 // str replace to drop special chars 15212 // and we bring it all to lowercase to match filenames we keep on server 15213 if(content && content !== 'none') return escape(content).replace(/%22/g, '').replace('%u', '').toLowerCase(); 15214 else return false; // just in case we haven't provided content to work with 15215 } 15216 15217 function invert(obj) { 15218 var new_obj = {}; 15219 for (var prop in obj) { 15220 if(obj.hasOwnProperty(prop)) { 15221 new_obj[obj[prop]] = prop; 15222 } 15223 } 15224 return new_obj; 15225 } 15226 15227}(jQuery)); 15228/*! 15229 * imagesLoaded PACKAGED v4.1.1 15230 * JavaScript is all like "You images are done yet or what?" 15231 * MIT License 15232 */ 15233 15234!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
15234h(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}); 15235 15236(function($, window){ 15237 'use strict'; 15238 15239 var scrollY = MK.val.scroll; 15240 var dynamicHeight = MK.val.dynamicHeight; 15241 15242 var $window = $(window); 15243 var $containers = $('.js-loop'); 15244 15245 $containers.each( pagination ); 15246 15247 $window.on( 'vc_reload', function() { 15248 $('.js-loop').each( pagination ); 15249 } ); 15250 15251 function pagination() { 15252 var unique = Date.now(); 15253 var $container = $(this); 15254 var $superContainer = $container.parent(); // should contain clearing so it stretches with floating children 15255 var $loadBtn = $container.siblings('.js-loadmore-holder').find('.js-loadmore-button'); 15256 var $loadScroll = $('.js-load-more-scroll'); 15257 var style = $container.data('pagination-style'); 15258 var maxPages = $container.data('max-pages'); 15259 var id = '#' + ($container.attr('id')); 15260 var ajaxLoader = new MK.utils.ajaxLoader(id); 15261 var isLoadBtn = (style === 2); 15262 var isInfiniteScroll = (style === 3); // add flag for last container 15263 var scrollCheckPoint = null; 15264 var isHandlerBinded = false;
15265 15266 ajaxLoader.init(); 15267 15268 init(); 15269 15270 function init() { 15271 MK.utils.eventManager.subscribe('ajaxLoaded', onLoad); 15272 bindHandlers(); 15273 if( isInfiniteScroll ) scrollCheckPoint = spyScrollCheckPoint(); 15274 15275 $window.on( 'vc_reload', function() { 15276 $window.off('scroll', handleScroll); 15277 } ); 15278 } 15279 15280 function bindHandlers() { 15281 if( isLoadBtn ) $loadBtn.on('click', handleClick); 15282 if( isInfiniteScroll ) $window.on('scroll', handleScroll); 15283 isHandlerBinded = true; 15284 } 15285 15286 function unbindHandlers() { 15287 if( isLoadBtn ) $loadBtn.off('click', handleClick); 15288 if( isInfiniteScroll ) $window.off('scroll', handleScroll); 15289 isHandlerBinded = false; 15290 } 15291 15292 function handleClick(e) { 15293 e.preventDefault(); 15294 if(!ajaxLoader.isLoading) loadMore(); 15295 } 15296 15297 function handleScroll() { 15298 if((scrollY() > scrollCheckPoint()) && !ajaxLoader.isLoading) loadMore(); 15299 } 15300 15301 function loadMore() { 15302 loadingIndicatorStart(); 15303 var page = ajaxLoader.getData('paged'); 15304 ajaxLoader.setData({paged: ++page}); 15305 ajaxLoader.load(unique); 15306 } 15307 15308 function onLoad(e, response) { 15309 if( typeof response !== 'undefined' && response.id === id) { 15310 // Checking found posts helps to fix all pagination styles 15311 if( ajaxLoader.getData('found_posts') <= 0 && ajaxLoader.getData('paged') >= ajaxLoader.getData('maxPages')) loadingIndicatorHide(); 15312 else loadingIndicatorShow(); 15313 if(response.unique === unique) $container.append(response.content); 15314 loadingIndicatorStop(); 15315 } 15316 } 15317 15318 function loadingIndicatorStart() { 15319 if(isLoadBtn) $loadBtn.addClass('is-active'); 15320 else if(isInfiniteScroll) MK.ui.loader.add('.js-load-more-scroll'); 15321 15322 } 15323 15324 function loadingIndicatorStop() { 15325 if(isLoadBtn) $loadBtn.removeClass('is-active'); 15326 else if(isInfiniteScroll) MK.ui.loader.remove('.js-load-more-scroll'); 15327 } 15328 15329 function loadingIndicatorShow() { 15330 if(isHandlerBinded) return; 15331 if(isLoadBtn) $loadBtn.show(); 15332 else if(isInfiniteScroll) $loadScroll.show(); 15333 bindHandlers(); 15334 } 15335 15336 function loadingIndicatorHide() { 15337 if(!isHandlerBinded) return; 15338 if(isLoadBtn) $loadBtn.hide(); 15339 else if(isInfiniteScroll) $loadScroll.hide(); 15340 unbindHandlers(); 15341 } 15342 15343 15344 function spyScrollCheckPoint() { 15345 var containerO = 0; 15346 var containerH = dynamicHeight( $superContainer ); 15347 var winH = dynamicHeight( window ); 15348 15349 var setVals = function() { 15350 containerO = $superContainer.offset().top; 15351 }; 15352 15353 setVals(); 15354 $window.on('resize', function() { requestAnimationFrame(setVals); }); 15355 15356 return function() { 15357 return (containerH() + containerO) - (winH() * 2); 15358 }; 15359 } 15360 } 15361 15362})(jQuery, window); 15363(function($) { 15364 'use strict'; 15365 15366 // Check if it's inside hidden parent 15367 // Cannot be position: fixed 15368 function isHidden(el) { 15369 return (el.offsetParent === null); 15370 } 15371 15372 MK.component.Masonry = function(el) { 15373 var $window = $(window); 15374 var $container = $(el); 15375 var config = $container.data( 'masonry-config' ); 15376 var $masonryItems = $container.find(config.item); 15377 var cols = config.cols || 8; 15378 var $filterItems = null; // assign only when apply filter 15379 var wall = null; 15380 15381 var init = function init() { 15382 MK.core.loadDependencies([ MK.core.path.plugins + 'freewall.js' ], onDepLoad); 15383 }; 15384 15385 var onDepLoad = function onDepLoad() { 15386 masonry(); 15387 15388 // Events 15389 $window.on('resize', onResize); 15390 MK.utils.eventManager.subscribe('ajaxLoaded', onPostAddition); 15391 MK.utils.eventManager.subscribe('staticFilter', resize); 15392 }; 15393 15394 var masonry = function masonry() { 15395 // Quit for hidden elements for now. 15396 if(isHidden(el)) return; 15397 15398 var newCols; 15399 if(window.matchMedia( '(max-width:600px)' ).matches) newCols = 2; 15400 else if(window.matchMedia( '(max-width:850px)' ).matches) newCols = 4; 15401 else newCols = cols; 15402 15403 var colW = $container.width() / newCols; 15404 15405 wall = new Freewall( config.container ); 15406 15407 // We need to pass settings to a plugin via reset method. Strange but works fine. 15408 wall.reset({
15409 selector: config.item + ':not(.is-hidden)', 15410 gutterX: 0, // set default gutter to 0 and again - apply margins to item holders in css 15411 gutterY: 0, 15412 cellW: colW, 15413 cellH: colW 15414 }); 15415 15416 wall.fillHoles(); 15417 wall.fitWidth(); 15418 15419 $masonryItems.each(function() { 15420 $(this).data('loaded', true); 15421 }); 15422 }; 15423 15424 15425 // Clear attributes after the plugin. It's API method dosn't handle it properly 15426 var destroyContainer = function destroyContainer() { 15427 $container.removeAttr('style') 15428 .removeData('wall-height') 15429 .removeData('wall-width') 15430 .removeData('min-width') 15431 .removeData('total-col') 15432 .removeData('total-row') 15433 .removeAttr('data-wall-height') 15434 .removeAttr('data-wall-width') 15435 .removeAttr('data-min-width') 15436 .removeAttr('data-total-col') 15437 .removeAttr('data-total-row'); 15438 }; 15439 15440 var destroyItem = function destroyItem() { 15441 var $item = $(this); 15442 $item.removeAttr('style') 15443 .removeData('delay') 15444 .removeData('height') 15445 .removeData('width') 15446 .removeData('state') 15447 .removeAttr('data-delay') 15448 .removeAttr('data-height') 15449 .removeAttr('data-width') 15450 .removeAttr('data-state'); 15451 }; 15452 15453 var destroyAll = function destroyAll() { 15454 if( !wall ) return; 15455 wall.destroy(); // API destroy 15456 destroyContainer(); 15457 $masonryItems.each( destroyItem ); // run our deeper destroy 15458 }; 15459 15460 var onResize = function onResize() { 15461 requestAnimationFrame(resize); 15462 }; 15463 15464 var refresh = function refresh() { 15465 if( !wall ) return; 15466 setTimeout(wall.fitWidth.bind(wall), 5); 15467 }; 15468 15469 var resize = function resize() { 15470 destroyAll(); 15471 masonry(); 15472 }; 15473 15474 var onPostAddition = function onPostAddition() { 15475 $masonryItems = $container.find(config.item); 15476 15477 $masonryItems.each(function() { 15478 var $item = $(this), 15479 isLoaded = $item.data('loaded'); 15480 15481 if(!isLoaded) $item.css('visibility', 'hidden'); 15482 }); 15483 15484 15485 $container.mk_imagesLoaded().then(function() { 15486 destroyAll(); 15487 masonry(); 15488 }); 15489 }; 15490 15491 return { 15492 init : init 15493 }; 15494 }; 15495 15496}(jQuery)); 15497(function($) { 15498 'use strict'; 15499 15500 MK.component.Pagination = function(el) { 15501 this.el = el; 15502 }; 15503 15504 MK.component.Pagination.prototype = { 15505 init: function init() { 15506 this.cacheElements(); 15507 this.bindEvents(); 15508 this.onInitLoad(); 15509 }, 15510 15511 cacheElements: function cacheElements() { 15512 this.lastId = 1; 15513 this.unique = Date.now(); 15514 this.$pagination = $(this.el); 15515 this.$container = this.$pagination.prev('.js-loop'); 15516 this.$pageLinks = this.$pagination.find('.js-pagination-page'); 15517 this.$nextLink = this.$pagination.find('.js-pagination-next'); 15518 this.$prevLink = this.$pagination.find('.js-pagination-prev'); 15519 this.$current = this.$pagination.find('.js-current-page'); 15520 this.$maxPages = this.$pagination.find('.pagination-max-pages'); // TODO change in DOM and here to js class 15521 this.containerId = '#' + this.$container.attr('id'); 15522 this.pagePathname = window.location.pathname; 15523 this.pageSearch = window.location.search; 15524 this.popState = false; 15525 this.ajaxLoader = new MK.utils.ajaxLoader('#' + this.$container.attr('id')); 15526 this.ajaxLoader.init(); 15527 }, 15528 15529 bindEvents: function bindEvents() { 15530 this.$pageLinks.on('click', this.pageClick.bind(this)); 15531 this.$nextLink.on('click', this.nextClick.bind(this)); 15532 this.$prevLink.on('click', this.prevClick.bind(this)); 15533 MK.utils.eventManager.subscribe('ajaxLoaded', this.onLoad.bind(this)); 15534 }, 15535 15536 pageClick: function pageClick(e) { 15537 e.preventDefault(); 15538 var $this = $(e.currentTarget); 15539 var id = parseFloat($this.attr('data-page-id')); 15540 15541 if(id > this.ajaxLoader.getData('maxPages') || id < 1) return; 15542 this.load(id, $this); 15543 this.updatePagedNumUrl( id ); 15544 }, 15545 15546 nextClick: function nextClick(e) { 15547 e.preventDefault(); 15548 if(this.ajaxLoader.getData('paged') === this.ajaxLoader.getData('maxPages')) return; 15549 this.load(++this.lastId, $(e.currentTarget)); 15550 this.updatePagedNumUrl( this.lastId ); 15551 }, 15552 15553 prevClick: function prevClick(e) { 15554 e.preventDefault(); 15555 if(this.ajaxLoader.getData('paged') === 1) return; 15556 this.load(--this.lastId, $(e.currentTarget)); 15557 this.updatePagedNumUrl( this.lastId ); 15558 }, 15559 15560 load: function load(id, $el) { 15561 this.lastId = id; 15562 this.ajaxLoader.setData({paged: id}); 15563 this.ajaxLoader.load(this.unique); 15564 this.removeIndicator(); 15565 MK.ui.loader.add($el); 15566 }, 15567 15568 onLoad: function success(e, response) { 15569 if( typeof response !== 'undefined' && response.id === this.containerId) { 15570 this.updatePagination(); 15571 this.lastId = this.ajaxLoader.getData('paged'); 15572 15573 if(response.unique === this.unique) { 15574 this.removeIndicator(); 15575 this.scrollPage(); 15576 this.$container.html(response.content); 15577 } 15578 } 15579 }, 15580 15581 updatePagination: function updatePagination() { 15582 var self = this; 15583 15584 // Hide / show arrows 15585 var isFirst = (this.ajaxLoader.getData('paged') === 1); 15586 var isLast = (this.ajaxLoader.getData('paged') === this.ajaxLoader.getData('maxPages')); 15587 15588 if(isFirst) this.$prevLink.addClass('is-vis-hidden'); 15589 else this.$prevLink.removeClass('is-vis-hidden'); 15590 15591 if(isLast) this.$nextLink.addClass('is-vis-hidden'); 15592 else this.$nextLink.removeClass('is-vis-hidden'); 15593 15594 // X of Y 15595 this.$current.html(this.ajaxLoader.getData('paged')); 15596 this.$maxPages.html(this.ajaxLoader.getData('maxPages')); 15597 15598 // Move overfloating items 15599 var displayItems = 10; 15600 var centerAt = displayItems / 2; 15601 15602 if(this.ajaxLoader.getData('maxPages') > displayItems) { 15603 this.$pageLinks.each(function(i) { 15604 15605 var id = self.lastId - centerAt; 15606 id = Math.max(id, 1); 15607 id = Math.min(id, self.ajaxLoader.getData('maxPages') - displayItems + 1); 15608 id = id + i; 15609 15610 $(this).html( id ).attr('data-page-id', id).show(); 15611 15612 if(i === 0 && id > 1) $(this).html('...'); 15613 if(i === displayItems - 1 && id < self.ajaxLoader.getData('maxPages')) $(this).html('...'); 15614 }); 15615 } else { 15616 this.$pageLinks.each(function(i) { 15617 var $link = $(this); 15618 var id = i + 1; 15619 15620 $link.html(id).attr('data-page-id', id); 15621 15622 if( self.ajaxLoader.getData('maxPages') === 1) { 15623 self.$pageLinks.hide(); 15624 } else { 15625 if(i > self.ajaxLoader.getData('maxPages') - 1) $link.hide(); 15626 else $link.show(); 15627 } 15628 15629 }); 15630 } 15631 15632 // Highlight current only 15633 this.$pageLinks.filter('[data-page-id="' + this.ajaxLoader.getData('paged') + '"]' ).addClass('current-page') 15634 .siblings().removeClass('current-page'); 15635 15636 }, 15637 15638 scrollPage: function scrollPage() { 15639 var containerOffset = this.$container.offset().top; 15640 var offset = containerOffset - MK.val.offsetHeaderHeight( containerOffset ) - 20; 15641 15642 this.$container.find('a:first').focus() 15643 MK.utils.scrollTo( offset ); 15644 }, 15645 15646 removeIndicator: function removeIndicator() { 15647 MK.ui.loader.remove('.js-pagination-page, .js-pagination-next, .js-pagination-prev'); 15648 }, 15649 15650 /** 15651 * Set some actions when archive/category page is loaded. Actions list: 15652 * - Select the correct paged ID on pagination list. 15653 * - Set current paged ID on the label. 15654 * - Add event listener onpopstate for handling prev/next button of Browser URL. 15655 * - Set info for updatePagedNumUrl() about request comes from popstate. 15656 * 15657 * @since 5.9.8 15658 */ 15659 onInitLoad: function onInitLoad() { 15660 var initPagedID = this.$pagination.data( 'init-pagination' ); 15661 if ( initPagedID && initPagedID > 1 ) { 15662 this.$current.html( initPagedID ); 15663 this.$pageLinks.filter( '[data-page-id="' + initPagedID + '"]' ).addClass( 'current-page' ).siblings().removeClass( 'current-page' ); 15664 } 15665 15666 // Run popstate only if it's supported by the browser. 15667 if ( 'onpopstate' in window ) { 15668 var thisPop = this; 15669 window.onpopstate = function( event ) { 15670 var id = 1; 15671 15672 // At start, state is always null. So, we should check it before processing. 15673 if ( typeof event.state === 'object' && event.state ) { 15674 var state = event.state; 15675 15676 // Set paged ID for updating page. 15677 if ( state.hasOwnProperty( 'MkPagination' ) ) { 15678 var currentState = state.MkPagination; 15679 if ( currentState.hasOwnProperty( 'paged' ) ) { 15680 id = parseFloat( currentState.paged ); 15681 } 15682 } 15683 } else { 15684 id = parseFloat( thisPop.getURLPagedID() ); 15685 } 15686 15687 // Tell updatePagedNumUrl() if request come from popstate. 15688 thisPop.popState = true; 15689 thisPop.$pageLinks.filter( '[data-page-id="' + id + '"]' ).trigger( 'click' ); 15690 } 15691 } 15692 }, 15693 15694 /** 15695 * Update current pagination browser URL by adding/changing paged number. Only run if 15696 * the browser support pushState and the request not coming from popstate. 15697 * 15698 * WordPress has some ways to set paged number: 15699 * 1. page/[number], paged=[number] will be directed here. 15700 * 2. page=[number] 15701 * So, we should check which one the request is used here. 15702 * 15703 * @since 5.9.8 15704 */ 15705 updatePagedNumUrl: function updatePagedNumUrl( id ) { 15706 // Check pushState browser support and ignore if request come from popstate. 15707 if ( 'history' in window && 'pushState' in history && id && ! this.popState ) { 15708 var fullPage = this.pagePathname + this.pageSearch; 15709 var isQueryPage = false;
15710 15711 // Style 1 - /page/[number], as default value. 15712 var newPage = 'page/' + id + '/'; 15713 var expPage = /page\/\d+\/?/; 15714 var result = this.pagePathname.match( /\/page\/\d+/ ); 15715 var isPagedExist = ( result ) ? true : false; 15716 15717 // Style 2 - ?page=[number], only run if /page/ is not exist and URL query var exist. 15718 if ( ! isPagedExist && this.pageSearch ) { 15719 isQueryPage = this.pageSearch.match( /page\=\d+/ ); 15720 if ( isQueryPage ) { 15721 newPage = 'page=' + id; 15722 expPage = /page\=\d+/; 15723 } 15724 } 15725 15726 // If page number is 1, remove paged number from URL. 15727 if ( id === 1 ) { 15728 newPage = ''; 15729 if ( isQueryPage ) { 15730 expPage = ( this.pageSearch.match( /\&+/ ) ) ? /page\=\d+\&?/ : /\?page\=\d+\&?/; 15731 } 15732 } 15733 15734 // Set new pathname. Do replacement only if the new pathname contains paged number. 15735 var newURL = this.pagePathname + newPage + this.pageSearch; 15736 if ( fullPage.match( expPage ) ) { 15737 newURL = fullPage.replace( expPage, newPage ); 15738 } 15739 15740 // Set history state and return popstate back to fal
15740se. 15741 var historyState = { 15742 MkPagination: { 15743 url: newURL, 15744 paged: id 15745 } 15746 } 15747 this.popState = false; 15748 15749 // Push new pathname to display/hide the paged number. 15750 window.history.pushState( historyState, null, newURL ); 15751 } 15752 this.popState = false; 15753 }, 15754 15755 /** 15756 * Get current URL page ID. Notes: 15757 * 1. page/[number], paged=[number] will be directed here. 15758 * 2. page=[number] 15759 * 15760 * @return {integer} Current paged ID. Default 1. 15761 */ 15762 getURLPagedID: function getURLPagedID() { 15763 var pathname = window.location.pathname; 15764 var search = window.location.search; 15765 var pagedId = 1; 15766 var result = ''; 15767 var isPagedExist = false; 15768 15769 // Search based on style 1. 15770 result = pathname.match( /\/page\/(\d+)/ ); 15771 if ( result ) { 15772 isPagedExist = true; 15773 pagedId = ( result.hasOwnProperty( 1 ) ) ? result[1] : 1; 15774 } 15775 15776 // Search based on style 2. 15777 if ( ! isPagedExist && search ) { 15778 result = search.match( /page\=(\d+)/ ); 15779 if ( result ) { 15780 isPagedExist = true; 15781 pagedId = ( result.hasOwnProperty( 1 ) ) ? result[1] : 1; 15782 } 15783 } 15784 15785 return pagedId; 15786 } 15787 }; 15788 15789}(jQuery)); 15790(function( $ ) { 15791 'use strict'; 15792 15793 var val = MK.val, 15794 utils = MK.utils; 15795 15796 MK.component.Parallax = function( el ) { 15797 var self = this, 15798 $this = $( el ), 15799 obj = $this[0], 15800 $window = $( window ), 15801 container = document.getElementById( 'mk-theme-container' ), 15802 config = $this.data( 'parallax-config' ), 15803 $holder = $( config.holder ), 15804 headerHeight = null, 15805 offset = null, 15806 elHeight = null, 15807 ticking = false, 15808 isMobile = null; 15809 15810 15811 var clientRect = null; 15812 15813 var update = function() { 15814 // Clear styles to check for natural styles 15815 // then apply position and size 15816 obj.style.transform = null; 15817 obj.style.top = null; 15818 obj.style.bottom = null; 15819 15820 isMobile = MK.utils.isMobile(); 15821 15822 if( isMobile ) { 15823 $this.css( 'height', '' ); 15824 return; 15825 } 15826 15827 clientRect = $this[ 0 ].getBoundingClientRect(); 15828 offset = clientRect.top; 15829 elHeight = clientRect.height; 15830 headerHeight = val.offsetHeaderHeight( offset ); 15831 offset = offset - headerHeight + val.scroll(); 15832 15833 setPosition(); 15834 setSize( ); 15835 }; 15836 15837 15838 var h = 0, 15839 winH = 0, 15840 proportion = 0, 15841 height = 0; 15842 15843 // Position and background attachement should me moved to CSS but we repair it high specificity here as styles are not reliable currently 15844 var setSize = function() { 15845 $this.css( 'height', '' ); 15846 winH = $window.height() - headerHeight; 15847 h = obj.getBoundingClientRect().height; 15848 15849 if( config.speed <= 1 && config.speed > 0 ) { 15850 if( offset === 0 ) { 15851 $this.css({ 15852 backgroundAttachment: 'scroll', 15853 'will-change': 'transform' 15854 }); 15855 } else { 15856 $this.css({ 15857 height : h + ( (winH - h) * config.speed ), 15858 backgroundAttachment: 'scroll', 15859 'will-change': 'transform' 15860 }); 15861 } 15862 15863 } else if ( config.speed > 1 && h <= winH ) { 15864 $this.css({ 15865 // good for full heights - 2 because it's viewable by 2 screen heights 15866 height: ( winH + ( ( winH * config.speed ) - winH ) * 2 ), 15867 top: -( ( winH * config.speed ) - winH ), 15868 backgroundAttachment: 'scroll', 15869 'will-change': 'transform' 15870 }); 15871 15872 } else if ( config.speed > 1 && h > winH ) { 15873 proportion = h / winH; 15874 height = ( winH + ( ( winH * config.speed ) - winH ) * (1 + proportion) ); 15875 15876 $this.css({ 15877 height: height, 15878 top: -( height - (winH * config.speed) ), 15879 backgroundAttachment: 'scroll', 15880 'will-change': 'transform' 15881 }); 15882 15883 } else if ( config.speed < 0 && h >= winH ) { 15884 height = h * (1 - config.speed); 15885 $this.css({ 15886 height: height + (height - h), 15887 top: h - height, 15888 backgroundAttachment: 'scroll', 15889 'will-change': 'transform' 15890 }); 15891 15892 } else if ( config.speed < 0 && h < winH ) { 15893 // candidate to change 15894 var display = (winH + h) / winH; 15895 height = h * -config.speed * display; 15896 $this.css({ 15897 height: h + (height * 2), 15898 top: -height, 15899 backgroundAttachment: 'scroll', 15900 'will-change': 'transform' 15901 }); 15902 } 15903 }; 15904 15905 15906 var currentPoint = null, 15907 progressVal = null, 15908 startPoint = null, 15909 endPoint = null, 15910 $opacityLayer = config.opacity ? $this.find( config.opacity ) : null, 15911 scrollY = null; 15912 15913 var setPosition = function() { 15914 startPoint = offset - winH; 15915 endPoint = offset + elHeight + winH - headerHeight; 15916 scrollY = val.scroll(); 15917 15918 if( scrollY < startPoint || scrollY > endPoint ) { 15919 ticking = false;
15920 return; 15921 } 15922 15923 currentPoint = (( -offset + scrollY ) * config.speed); 15924 15925 $this.css({ 15926 '-webkit-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15927 '-moz-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15928 '-ms-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15929 '-o-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 15930 'transform': 'translateY(' + currentPoint + 'px) translateZ(0)' 15931 }); 15932 15933 ticking = false; 15934 }; 15935 15936 15937 var requestTick = function() { 15938 if( !ticking && !isMobile ) { 15939 ticking = true; 15940 window.requestAnimationFrame( setPosition ); 15941 } 15942 }; 15943 15944 15945 var init = function() { 15946 // Disable scroll effects when smooth scroll is disabled 15947 if( !MK.utils.isSmoothScroll ) { return; } 15948 15949 update(); 15950 setTimeout(update, 100); 15951 $window.on( 'load', update ); 15952 $window.on( 'resize', update ); 15953 window.addResizeListener( container, update ); 15954 15955 $window.on( 'scroll', requestTick ); 15956 }; 15957 15958 15959 return { 15960 init : init 15961 }; 15962 }; 15963 15964})( jQuery ); 15965(function($) { 15966 'use strict'; 15967 15968 MK.component.Preloader = function(el) { 15969 this.el = el; 15970 }; 15971 15972 MK.component.Preloader.prototype = { 15973 init: function init() { 15974 this.cacheElements(); 15975 this.bindEvents(); 15976 }, 15977 15978 cacheElements: function cacheElements() { 15979 this.$preloader = $(this.el); 15980 }, 15981 15982 bindEvents: function bindEvents() { 15983 this.onLoad(); // all components inited on page load 15984 }, 15985 15986 onLoad: function onLoad() { 15987 setTimeout(this.hidePreloader.bind(this), 300); 15988 }, 15989 15990 hidePreloader: function hidePreloader() { 15991 this.$preloader.hide(); 15992 } 15993 }; 15994 15995}(jQuery)); 15996 15997(function($) { 15998 'use strict'; 15999 16000 // Image added for proportional scaling 16001 MK.ui.loader = { 16002 tpl : function() { 16003 return '<div class="mk-loading-indicator">' + 16004 '<div class="mk-loading-indicator__inner">' + 16005 '<div class="mk-loading-indicator__icon"></div>' + 16006 '<img style="height:100%; width:auto;" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">' + 16007 '</div>' + 16008 '</div>'; 16009 }, 16010 16011 add : function(item) { 16012 $(item).append(this.tpl); 16013 }, 16014 16015 remove : function(item) { 16016 if(item) $(item).find('.mk-loading-indicator').remove(); 16017 else $('.mk-loading-indicator').remove(); 16018 } 16019 }; 16020 16021}(jQuery)); 16022(function( $ ) { 16023 // IE / Edge fix for fixed positioned elements 16024 // MS clip path doesnt redraw properly so we expirience similar bug like with background attachement fixed on Chrome 16025 if (MK.utils.browser.name === 'Edge' || MK.utils.browser.name === 'IE') { 16026 var val = 1; 16027 var $edgeClipper = $('.mk-slider-slide'); // edge slider 16028 var $sectionClipper = $('.clipper-true'); // edge slider 16029 var $bgLayer = $('.background-layer'); // page section 16030 16031 var onScroll = function onScroll() { 16032 val *= -1; 16033 if( $edgeClipper.length ) $edgeClipper.each( redraw ); 16034 if( $sectionClipper.length ) $sectionClipper.each( redraw ); 16035 if( $bgLayer.length ) $bgLayer.each( redraw ); 16036 }; 16037 16038 var redraw = function redraw() { 16039 $(this).css('margin-top', val / 100); 16040 }; 16041 16042 $(window).on("scroll", function () { 16043 window.requestAnimationFrame(onScroll); 16044 }); 16045 } 16046}(jQuery)); 16047MK.component.ResponsiveImageSetter = (function ($) { 16048 16049 'use strict'; 16050 16051 var module = {}; 16052 16053 16054 /*---------------------------------------------------------------------------------*/ 16055 /* Private Variables 16056 /*---------------------------------------------------------------------------------*/ 16057 16058 var viewportClass = getViewportClass(); 16059 var isRetina = window.devicePixelRatio >= 2; 16060 16061 16062 /*---------------------------------------------------------------------------------*/ 16063 /* Private Methods 16064 /*---------------------------------------------------------------------------------*/ 16065 16066 function run($imgs) { 16067 $imgs.filter( function() { 16068 return !this.hasAttribute("mk-img-src-setted"); 16069 }).each(setSrcAttr); 16070 } 16071 16072 function setSrcAttr() { 16073 var $img = $(this); 16074 var set = $img.data('mk-image-src-set'); 16075 // Set src attribute to img link suitable for our logic. It will load the image. 16076 if(set['responsive'] === 'false' && isRetina && set['2x']) $img.attr('src', set['2x']); 16077 else if(set['responsive'] === 'false') $img.attr('src', set.default); 16078 else if(viewportClass === 1 && isRetina && set['2x']) $img.attr('src', set['2x']); // default x2 for retina 16079 else if(viewportClass === 0 && set.mobile) $img.attr('src', set.mobile); 16080 else $img.attr('src', set.default); 16081 16082 $img.load(function() { 16083 $(window).trigger('mk-image-loaded') 16084 }) 16085 } 16086 16087 function getViewportClass() { 16088 if(window.matchMedia('(max-width: 736px)').matches) return 0; 16089 else return 1; 16090 } 16091 16092 function handleResize($imgs) { 16093 if(!$imgs.length) return;
16093 // Do not run if empty collection 16094 var currentViewportClass = getViewportClass(); 16095 // We don't need to reload bigger images when screen size is decreasing as browser already performs resize operation. 16096 // Run update on for increasing screen size 16097 if( currentViewportClass > viewportClass) { 16098 viewportClass = currentViewportClass; // update for further reference 16099 run($imgs); 16100 } 16101 } 16102 16103 16104 /*---------------------------------------------------------------------------------*/ 16105 /* Public Methods 16106 /*---------------------------------------------------------------------------------*/ 16107 16108 module.init = function ($imgs) { 16109 16110 // Do not run if empty collection 16111 if(!$imgs.length) return; 16112 16113 // Run and bind to events 16114 run($imgs); 16115 $imgs.attr('mk-img-src-setted', ''); 16116 16117 }; 16118 16119 module.onResize = function ($imgs) { 16120 16121 $(window).on( 'resize', MK.utils.throttle( 500, function() { 16122 handleResize($imgs); 16123 })); 16124 16125 16126 }; 16127 16128 16129 module.handleAjax = function () { 16130 setTimeout(function ajaxDelayedCallback() { // give it a chance to insert content first 16131 var $newImgs = $('img[data-mk-image-src-set]').filter( function() { 16132 return !this.hasAttribute("mk-lazyload"); 16133 }); 16134 if(!$newImgs.length) return; 16135 run($newImgs); 16136 }, 100); 16137 } 16138 16139 16140 return module; 16141 16142}(jQuery)); 16143 16144 16145jQuery(function($) { 16146 16147 var init = function init() { 16148 // Get All Responsive Images 16149 $allImages = $('img[data-mk-image-src-set]').filter(function(index) { 16150 var isNotPortfolioImage = !$(this).hasClass('portfolio-image'), 16151 isNotBlogImage = $(this).closest('.mk-blog-container').length == 0, 16152 isNotSwiperImage = !$(this).hasClass('swiper-slide-image'), 16153 isNotGalleryImage = !$(this).hasClass('mk-gallery-image'); 16154 return isNotPortfolioImage && isNotBlogImage && isNotSwiperImage && isNotGalleryImage; 16155 });; 16156 16157 // Handle the resize 16158 MK.component.ResponsiveImageSetter.onResize($allImages); 16159 16160 MK.component.ResponsiveImageSetter.init($allImages); 16161 16162 MK.utils.eventManager.subscribe('ajaxLoaded', MK.component.ResponsiveImageSetter.handleAjax ); // ajax loops 16163 MK.utils.eventManager.subscribe('ajax-preview', MK.component.ResponsiveImageSetter.handleAjax ); // ajax portfolio 16164 MK.utils.eventManager.subscribe('quickViewOpen', MK.component.ResponsiveImageSetter.handleAjax ); 16165 }
16166 init(); 16167 $(window).on('vc_reload', init); 16168 16169}); 16170 16171 16172 16173(function( $ ) { 16174 'use strict'; 16175 16176 var utils = MK.utils; 16177 var val = MK.val; 16178 16179 /** 16180 * Keep track of top Level sections so we can easly skip to next one. 16181 * We must be explicit about DOM level to nested sections. 16182 * 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). 16183 * We keep track for the same sections in Footer for mutating window location with '!loading' to prevent native anchor behaviour. 16184 */ 16185 var $topLevelSections = $('#theme-page > .vc_row, #theme-page > .mk-main-wrapper-holder, #theme-page > .mk-page-section'); 16186 16187 $( document ).on( 'click', '.mk-skip-to-next', function() { 16188 var $this = $( this ), 16189 /** 16190 * Static height of button + the space to the bottom of the container. 16191 * 16192 * @TODO Possible to calculate dynamically. 16193 */ 16194 btnHeight = $this.hasClass( 'edge-skip-slider' ) ? 150 : 76, 16195 offset = $this.offset().top + btnHeight, 16196 nextOffset = utils.nextHigherVal( utils.offsets( $topLevelSections ), [offset] ); 16197 16198 utils.scrollTo( nextOffset - val.offsetHeaderHeight( nextOffset ) ); 16199 }); 16200 16201})( jQuery ); 16202 16203(function( $ ) { 16204 'use strict'; 16205 16206 /** 16207 * A fork from original VC function. 16208 * The original one is adding some padding which causes extra padding 16209 * on our templates so we customized the codes. 16210 * 16211 * @since 5.9.8 16212 * @since 6.0.1 Improve logic to handle LTR responsive state. 16213 * @since 6.0.1 Fix missing padding-left if row has its own padding-left when 16214 * Vertical Header is active. 16215 */ 16216 function MkfullWidthRow() { 16217 var $windowWidth = $(document).width(); 16218 var $elements = $('[data-mk-full-width="true"]'); 16219 var direction = $('body.rtl').length ? 'right' : 'left'; 16220 var verticalHeader = $('body.vertical-header-enabled').length ? true : false; 16221 var verticalHeaderWidth = ( $( '.header-style-4 .mk-header-inner' ).outerWidth() > 270 ) ? 0 : 270; 16222 var verticalHeaderRtl = $('body.rtl').length ? -1 : 1; 16223 var verticalHeaderRtlWidth = $('body.rtl.vertical-header-enabled').length ? verticalHeaderWidth : 0; 16224 var verticalHeaderRight = $('body.vertical-header-right').length ? -1 : 1; 16225 var verticalHeaderWidthBoxed = 0; 16226 var boxed = $('body.mk-boxed-enabled').length; 16227 var boxedOffset = ( boxed ) ? ( $(window).width() - $('#theme-page').outerWidth() ) / 2 : 0; 16228 var boxedMaxWidth = ( boxed ) ? $('#theme-page').outerWidth() : 'auto'; 16229 16230 if ( verticalHeader && boxed ) { 16231 verticalHeaderWidthBoxed = ( $( '.header-style-4 .mk-header-inner' ).outerWidth() > 270 ) ? 0 : verticalHeaderRtl * verticalHeaderRight * 135; 16232 } 16233 16234 var transparentHeader = $('.transparent-header').length; 16235 if ( transparentHeader > 0 ) { 16236 verticalHeaderWidthBoxed = 0; 16237 } 16238 16239 $.each($elements, function(key, item) { 16240 var $el = $(this); 16241 var css; 16242 $el.addClass("vc_hidden"); 16243 var $el_full = $el.next(".vc_row-full-width"); 16244 if ($el_full.length || ($el_full = $el.parent().next(".vc_row-full-width")), $el_full.length) { 16245 var el_margin_left = parseInt($el.css("margin-left"), 10), 16246 el_margin_right = parseInt($el.css("margin-right"), 10), 16247 offset = 0 - $el_full.offset().left - el_margin_left, 16248 width = $(window).width(); 16249 if (css = { 16250 position: "relative", 16251 "box-sizing": "border-box", 16252 width: $(window).width(), 16253 maxWidth: boxedMaxWidth 16254 }, 16255 css[direction] = offset + boxedOffset + verticalHeaderWidthBoxed + ( verticalHeaderRight * verticalHeaderRtlWidth ), 16256 $el.css(css), !$el.data("mkStretchContent")) { 16257 var padding = -1 * offset; 16258 0 > padding && (padding = 0); 16259 var paddingRight = width - padding - $el_full.width() + el_margin_left + el_margin_right; 16260 padding = padding - paddingRight; 16261 if ( 0 > paddingRight && (paddingRight = 0) ) { 16262 css = {}; 16263 if ( 'right' === direction ) { 16264 css["padding-left"] = padding + "px"; 16265 css["padding-right"] = 0; 16266 } else { 16267 css["padding-right"] = padding + "px"; 16268 css["padding-left"] = 0; 16269 } 16270 16271 $el.css(css) 16272 } 16273 } 16274 16275 /** 16276 * Fix AM-2974 16277 * 16278 * When user set padding-left or padding-right in the row, both of them 16279 * will be prioritized as important styles. It breaks padding-left set 16280 * by Vertical Header to make the row content fit with the content 16281 * container. We need to repopulate the padding-{side - based on Vertical 16282 * Header position} value. 16283 * 16284 * Works when: 16285 * - Vertical Header is active 16286 * - Main layout is not boxed 16287 * - Page Section is full layout 16288 * - Row is full width layout and content 16289 * - Row has its own padding-left and padding-right 16290 * - There is no additional padding added 16291 * 16292 * How it works: 16293 * Get current row padding-left and vertical header width. If both of them 16294 * are equal, it means row position is correct related to Vertical Header. 16295 * If not, we should sum both of them, then declare new padding-{side} 16296 * style attribute. 16297 * 16298 * Tested with: 16299 * - Main Layout: Full & Boxed Layout 16300 * - Page Section: Full Layout and Non Full Layout 16301 * - Row: Full Width, Full Width Content, and Non Full Width 16302 * - Header: Non vertical headers 16303 * - Transparent Header 16304 * - RTL and LTR 16305 */ 16306 if ( verticalHeader && ! boxed && ! transparentHeader && ! css.hasOwnProperty( 'padding-left' ) && ! css.hasOwnProperty( 'padding-right' )) { 16307 // Get row padding-{side} and Vertical Header width. 16308 var side = 'left'; 16309 if ( verticalHeaderRight === -1 ) { 16310 side = 'right'; 16311 } 16312 var el_padding_dir = parseInt( $el.css( 'padding-' + side ), 10 ); 16313 var header_padding_dir = $( '.header-style-4 .mk-header-inner' ).outerWidth(); 16314 // Compare both of row and vertical header padding-{side}. 16315 if ( el_padding_dir != header_padding_dir) { 16316 if($windowWidth > mk_responsive_nav_width) { 16317 $el[0].style.setProperty( 'padding-' + side, header_padding_dir + 'px', 'important' ); 16318 } else { 16319 $el[0].style.removeProperty( 'padding-' + side); 16320 } 16321 16322 16323 // Reset padding-{side} for page section. 16324 var $el_page_section = $el.find( '.mk-page-section.full_layout' ); 16325 if ( $el_page_section.length > 0 ) { 16326 $el_page_section[0].style.setProperty( 'padding-' + side, 'unset', 'important' ); 16327 } 16328 } 16329 } 16330 16331 $el.attr("data-mk-full-width-init", "true"), $el.removeClass("vc_hidden"), $(document).trigger("vc-full-width-row-single", { 16332 el: $el, 16333 offset: offset, 16334 marginLeft: el_margin_left, 16335 marginRight: el_margin_right, 16336 elFull: $el_full, 16337 width: width 16338 }) 16339 } 16340 }
16340), $(document).trigger("mk-full-width-row", $elements); 16341 } 16342 16343 MkfullWidthRow(); 16344 var debounceResize = null; 16345 $(window).on("resize", function() { 16346 if( debounceResize !== null ) { clearTimeout( debounceResize ); } 16347 debounceResize = setTimeout( MkfullWidthRow, 100 ); 16348 }); 16349 16350})( jQuery ); 16351 16352(function($) { 16353 'use strict'; 16354 16355 // 16356 // Constructor 16357 // 16358 // ///////////////////////////////////////////////////////// 16359 16360 MK.ui.Slider = function( container, config ) { 16361 16362 var defaults = { 16363 slide : '.mk-slider-slide', 16364 nav : '.mk-slider-nav', 16365 effect : 'roulete', 16366 ease : 'easeOutQuart', // should not be changed, remove 16367 slidesPerView : 1, 16368 slidesToView : 1, 16369 transitionTime : 700, 16370 displayTime : 3000, 16371 autoplay : true, 16372 hasNav : true, 16373 hasPagination : true, 16374 paginationTpl : '<span></span>', 16375 paginationEl : '#pagination', 16376 draggable : true, 16377 fluidHeight : false, 16378 pauseOnHover : false, 16379 lazyload : false, 16380 activeClass : 'is-active', 16381 edgeSlider : false, 16382 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>', 16383 onInitialize : function() {}, 16384 onAfterSlide : function( id ) {}, 16385 onBeforeSlide : function( id ) {} 16386 }; 16387 16388 this.state = { 16389 id : 0, 16390 moveForward : true, 16391 running : false, 16392 zIFlow : null, 16393 stop : false, 16394 }; 16395 16396 this.config = $.extend( defaults, config ); 16397 this.container = container; 16398 16399 this.initPerView = this.config.slidesPerView; 16400 16401 // Timer holder 16402 this.activeTimer = null; 16403 this.autoplay = null; 16404 this.timer = null; 16405 this.timerRemaining = parseInt(this.config.displayTime); 16406 16407 // Boolean 'Em All, Making sure it's not string 16408 this.config.lazyload = JSON.parse(this.config.lazyload); 16409 this.config.edgeSlider = JSON.parse(this.config.edgeSlider); 16410 16411 // Image Loader Instance 16412 this.imageLoader = null; 16413 16414 // Add abort command to imagesLoaded, Placing it inside script makes it to work with different versions 16415 // of imagesLoaded if loaded by other Plugins 16416 imagesLoaded.prototype.abort = function() { 16417 this.progress = this.complete = function() { }; 16418 }; 16419 }; 16420 16421 16422 16423 // 16424 // Shared methods 16425 // 16426 // ///////////////////////////////////////////////////////// 16427 16428 MK.ui.Slider.prototype = { 16429 16430 init : function() { 16431 this.setPerViewItems(); 16432 this.cacheElements(); 16433 this.getSlideSize(); 16434 this.bindEvents(); 16435 this.setSize(); 16436 this.setPos(); 16437 16438 // Hack for preparing 'prev' on first click if needed 16439 this.updateId( -1 ); 16440 this.updateId( 1 ); 16441 16442 this.val = this.dynamicVal(); 16443 this.timeline = this.prepareTimeline( this.config.transitionTime ); 16444 16445 this.timeline.build(); 16446 16447 if( this.config.hasPagination ) { this.buildPagination(); } 16448 16449 if( this.config.autoplay && document.hasFocus() ) { this.setTimer(); } 16450 16451 if( typeof this.config.onInitialize === 'function' ) { 16452 this.config.onInitialize( this.slides ); 16453 } 16454 16455 if( this.config.fluidHeight === true ) { 16456 $( this.slides ).height( 'auto' ); 16457 $( this.container ).css( 'transition', 'height ' + 200 + 'ms ease-out' ); 16458 this.setHeight( 0 ); 16459 } 16460 16461 16462 if( this.config.fluidHeight === 'toHighest' ) { 16463 this.setHeightToHighest(); 16464 } 16465 16466 // Create timer per slide if required 16467 $(this.slides).each(this.createTimer); 16468 16469 // If it's Edge Slider and Lazy Load is enabled 16470 if ( this.config.lazyload && this.config.edgeSlider ) { 16471 16472 // If It's not a Video Slide 16473 if ( $(this.slides[this.state.id]).find('video').length === 0 ) { 16474 // Set the first slide's BG image 16475 var $slideImg = $(this.slides[this.state.id]).children('[data-mk-img-set]');
16476 MK.component.BackgroundImageSetter.init( $slideImg ); 16477 } 16478 $(this.config.spinnerTpl).prependTo( this.$slides ); 16479 16480 } else { 16481 16482 // Set all slides's BG images 16483 MK.component.BackgroundImageSetter.init( $(this.slides).children('[data-mk-img-set]') ); 16484 16485 } 16486 16487 16488 16489 }, 16490 16491 16492 cacheElements : function () { 16493 this.container = this.isNode( this.container ) ? this.container 16494 : document.querySelectorAll( this.container )[0]; 16495 this.slides = this.container.querySelectorAll( this.config.slide ); 16496 this.$slides = $(this.slides); 16497 16498 if( this.config.hasNav ) { this.$nav = $( this.config.nav ); } 16499 if( this.config.hasPagination ) { this.$pagination = $( this.config.paginationEl ); } 16500 }, 16501 16502 16503 bindEvents : function() { 16504 var $window = $( window ); 16505 16506 if( this.config.slidesPerView > 1 ) { $window.on( 'resize', this.setPerViewItems.bind( this ) ); } 16507 if( this.config.hasNav ) { this.eventsNav(); } 16508 if( this.config.hasPagination ) { this.eventsPag(); } 16509 if( this.config.draggable ) { this.dragHandler(); } 16510 if( this.config.autoplay ) { 16511 $window.on( 'focus', this.windowActive.bind( this ) ); 16512 $window.on( 'blur', this.windowInactive.bind( this ) ); 16513 } 16514 if( this.config.pauseOnHover ) { 16515 $(this.container).on( 'mouseleave', this.setTimer.bind( this ) ); 16516 $(this.container).on( 'mouseenter', this.unsetTimer.bind( this ) ); 16517 } 16518 if( this.config.fluidHeight === 'toHighest' ) { 16519 $window.on( 'resize', this.setHeightToHighest.bind( this ) ); 16520 } 16521 }, 16522 16523 16524 setPerViewItems: function() { 16525 if(window.matchMedia( '(max-width: 500px)' ).matches) { this.config.slidesPerView = 1; } 16526 else if(window.matchMedia( '(max-width: 767px)' ).matches && this.initPerView >= 2 ) { this.config.slidesPerView = 2; } 16527 else if(window.matchMedia( '(max-width: 1024px)' ).matches && this.initPerView >= 3 ) { this.config.slidesPerView = 3; } 16528 else { this.config.slidesPerView = this.initPerView; } 16529 16530 if( typeof this.slides === 'undefined' ) return; 16531 this.getSlideSize(); 16532 this.setSize(); 16533 this.setPos(); 16534 this.timeline = this.prepareTimeline( this.config.transitionTime ); 16535 this.timeline.build(); 16536 }, 16537 16538 16539 eventsNav : function() { 16540 this.$nav.on( 'click', 'a', this.handleNav.bind( this ) ); 16541 }, 16542 16543 16544 eventsPag : function() { 16545 this.$pagination.on( 'click', 'a', this.handlePagination.bind( this ) ); 16546 }, 16547 16548 16549 handleNav : function( e ) { 16550 e.preventDefault(); 16551 16552 if( this.state.running ) { return; } 16553 this.state.running = true; 16554 16555 var $this = $( e.currentTarget ), 16556 moveForward = $this.data( 'direction' ) === 'next'; 16557 16558 16559 if( this.config.autoplay ) { 16560 this.unsetTimer(); 16561 setTimeout( this.setTimer.bind( this ), this.config.transitionTime ); 16562 } 16563 16564 this.state.moveForward = moveForward; 16565 this.timeline.build(); 16566 this.timeline.play(); 16567 16568 this.setActive( this.nextId( moveForward ? 1 : -1 ) ); 16569 if( this.config.fluidHeight ) { this.setHeight( this.nextId( moveForward ? 1 : -1 ) ); } 16570 }, 16571 16572 16573 handlePagination : function( e ) { 16574 e.preventDefault(); 16575 16576 var $this = $( e.currentTarget ), 16577 id = $this.index(); 16578 16579 this.goTo( id ); 16580 }, 16581 16582 16583 reset: function() { 16584 this.state.stop = true; 16585 this.state.id = 0; 16586 this.setPos(); 16587 this.unsetTimer(); 16588 this.setTimer(); 16589 }, 16590 16591 16592 goTo : function(id) { 16593 if( this.state.running ) { return; } 16594 this.state.running = true; 16595 16596 var lastId = this.state.id; 16597 16598 if( lastId === id ) { 16599 return; 16600 } else if( lastId < id ) { 16601 this.state.moveForward = true; 16602 } else { 16603 this.state.moveForward = false; 16604 } 16605 16606 if( this.config.autoplay ) { 16607 this.unsetTimer(); 16608 setTimeout( this.setTimer.bind( this ), this.config.transitionTime ); 16609 } 16610 16611 this.timeline.build( Math.abs( lastId - id ) ); 16612 this.timeline.play(); 16613 16614 this.setActive( id ); 16615 if( this.config.fluidHeight ) { this.setHeight( id ); } 16616 }, 16617 16618 16619 windowActive : function() { 16620 this.setTimer(false, true); 16621 $(this.container).removeClass('is-paused'); 16622 }, 16623 16624 16625 windowInactive : function() { 16626 this.unsetTimer(); 16627 $(this.container).addClass('is-paused'); 16628 }, 16629 16630 16631 updateId : function( val ) { 16632 this.state.id = this.nextId(val); 16633 }, 16634 16635 nextId : function( val ) { 16636 var len = this.slides.length, 16637 insertVal = this.state.id + val; 16638 insertVal = ( insertVal >= 0 ) ? insertVal : len + val; 16639 insertVal = ( insertVal >= len ) ? 0 : insertVal; 16640 16641 return insertVal; 16642 }, 16643 16644 16645 setStyle : function( obj, style ) { 16646 var hasT = style.transform, 16647 t = { 16648 x : ( hasT ) ? style.transform.translateX : null, 16649 y : ( hasT ) ? style.transform.translateY : null, 16650 scale : ( hasT ) ? style.transform.scale : null, 16651 rotate : ( hasT ) ? style.transform.rotate : null, 16652 rotateX : ( hasT ) ? style.transform.rotateX : null, 16653 rotateY : ( hasT ) ? style.transform.rotateY : null 16654 }, 16655 z = 'translateZ(0)', 16656 x = (t.x) ? 'translateX(' + t.x + '%)' : 'translateX(0)', 16657 y = (t.y) ? 'translateY(' + t.y + '%)' : 'translateY(0)', 16658 s = (t.scale) ? 'scale(' + t.scale + ')' : 'scale(1)', 16659 r = (t.rotate) ? 'rotate(' + t.rotate + 'deg)' : 'rotate(0)', 16660 rX = (t.rotateX) ? 'rotateX(' + t.rotateX + 'deg)' : '', 16661 rY = (t.rotateY) ? 'rotateY(' + t.rotateY + 'deg)' : '', 16662 16663 o = style.opacity, 16664 h = style.height, 16665 w = style.width; 16666 16667 var c = z + x + y + s + r + rX + rY; 16668 16669 if( c.length ) { 16670 obj.style.webkitTransform = c; 16671 obj.style.msTransform = c; 16672 obj.style.transform = c; 16673 } 16674 16675 if( typeof o === 'number' ) { obj.style.opacity = o; } 16676 if( h ) { obj.style.height = h + '%'; } 16677 if( w ) { obj.style.width = w + '%'; } 16678 }, 16679 16680 16681 setPos : function() { 16682 if( typeof this.slides === 'undefined' ) return; 16683 var id = this.state.id, 16684 i = 0, 16685 len = this.slides.length, 16686 animation = this.animation[ this.config.effect ], 16687 axis = animation.axis, 16688 animNext = animation.next, 16689 animActi = animation.active, 16690 animPrev = animation.prev, 16691 perView = this.config.slidesPerView, 16692 slideId = null, 16693 zIFlow = null, 16694 style = {}; 16695 16696 style.transform = {}; 16697 16698 16699 for( ; i < len; i += 1 ) { 16700 if(i < perView) { 16701 // Position for visible slides. Apply active styles 16702 style = animActi; 16703 style.transform[ 'translate' + axis ] = i * 100; 16704 } else { 16705 // Rest slides move after edge based on axis and moveForward. Apply Next / Prev styles 16706 style = this.state.moveForward ? animNext : animPrev; 16707 style.transform[ 'translate' + axis ] = this.state.moveForward ? perView * 100 : -100; 16708 } 16709 16710 this.slides[ i ].style.zIndex = 0; 16711 16712 slideId = ( i + id ) % len; 16713 this.setStyle( this.slides[ slideId ], style ); 16714 } 16715 }, 16716 16717 16718 // When we're setting animation along Y axis we're going to set up height 16719 // otherwise width. It is shared amongst all slides 16720 setSize : function() { 16721 if( typeof this.slides === 'undefined' ) return; 16722 var i = 0, 16723 len = this.slides.length, 16724 axis = this.animation[ this.config.effect ].axis, 16725 slideSize = this.slideSize, 16726 style = {}; 16727 16728 if( axis === 'Y' ) { 16729 style.height = slideSize[ axis ]; 16730 } else { 16731 style.width = slideSize[ axis ]; 16732 } 16733 16734 for( ; i < len; i += 1 ) { 16735 this.setStyle( this.slides[ i ], style ); 16736 } 16737 }, 16738 16739 16740 setHeight : function( id ) { 16741 var $slides = $( this.slides ), 16742 $activeSlide = $slides.eq( id ); 16743 16744 var currentHeight = $activeSlide.height(); 16745 $( this.container ).height( currentHeight ); 16746 }, 16747 16748 16749 setHeightToHighest : function() { 16750 // this is becouse of alliginig woocommrece carousel. Too much DOM 16751 // Refactor someday 16752 var $slides = $( this.slides ), 16753 height = 0; 16754 16755 $slides.each(function() { 16756 height = Math.max(height, $(this).find('> div').outerHeight()); 16757 }); 16758 16759 $( this.container ).height( height ); 16760 }, 16761 16762 16763 // Little utility inspired by GreenSock. 16764 // We export this to this.timeline on init. 16765 prepareTimeline : function( time ) { 16766 var self = this, 16767 iteration = 0, 16768 totalIter = time / (1000 / 60), 16769 animLoop = [], 16770 aL = 0, // animation length 16771 loops = 1, 16772 ease = this.config.ease, 16773 currentStyle, timeProg, 16774 build, move, add, play, reverse, progress, kill; 16775 16776 16777 // Build constants, run them only once 16778 // take out possibly 16779 var len = this.slides.length, 16780 perView = this.config.slidesPerView, 16781 animation = this.animation[ this.config.effect ], 16782 animAxis = animation.axis, 16783 animNext = animation.next, 16784 animActi = animation.active, 16785 animPrev = animation.prev, 16786 style = {}, 16787 slideId = null, 16788 zIFlow = null; 16789 16790 style.transform = {}; 16791 16792 16793 build = function( repeats ) { 16794 var currentEase = ease; 16795 loops = repeats || loops; 16796 16797 // console.log('build', loops); 16798 16799 if( !loops ) { return; } 16800 if( loops > 1 ) { 16801 currentEase = 'linearEase'; 16802 } 16803 16804 // clean before running new build 16805 kill(); 16806 // set new positions 16807 self.setPos(); 16808 16809 var id = self.state.id, 16810 moveForward = self.state.moveForward, 16811 i = 0, 16812 axisMove = (moveForward) ? -100 : 100; 16813
16814 for( ; i <= perView; i += 1 ) { 16815 slideId = ( (moveForward) ? i + id : i + id - 1 ) % len; 16816 slideId = ( slideId < 0 ) ? len + slideId : slideId; 16817 16818 if( i === 0 ) { 16819 style = moveForward ? animPrev : animActi; 16820 } else if( i === perView ) { 16821 style = moveForward ? animActi : animNext; 16822 } else { 16823 style = animActi; 16824 } 16825 16826 zIFlow = (self.state.moveForward) ? animNext.zIndex : animPrev.zIndex; 16827 if( zIFlow ) { 16828 // console.log( zIFlow ); 16829 self.slides[ slideId ].style.zIndex = (zIFlow === '+') ? i + 1 : len - i; 16830 } 16831 16832 style.transform[ 'translate' + animAxis ] = axisMove; 16833 add( self.slides[ slideId ], style, currentEase ); 16834 } 16835 }; 16836 16837 add = function( slide, toStyles, ease ) { 16838 if( typeof slide === 'undefined' ) { 16839 throw 'Add at least one slide'; 16840 } 16841 16842 var fromStyles = slide.style, 16843 style = self.refStyle( toStyles, fromStyles ); 16844 16845 animLoop.push( [slide, style, ease] ); 16846 aL += 1; 16847 }; 16848 16849 move = function( startProg, mode ) { 16850 if (isTest) return; 16851 var currentTotalIter = totalIter; 16852 16853 if( loops > 1 ) { 16854 currentTotalIter = totalIter / 5; 16855 } 16856 16857 if( !self.state.running ) { self.state.running = true; } 16858 16859 if( startProg ) { 16860 // update iteration val to cached outside var 16861 // ceil to handle properly play after mouse up / touch end 16862 iteration = Math.ceil(startProg * currentTotalIter); 16863 } 16864 16865 timeProg = iteration / currentTotalIter; 16866 progress( timeProg ); 16867 16868 // Break loop 16869 if( iteration >= currentTotalIter && mode === 'play' || 16870 iteration <= 0 && mode === 'reverse' ) { 16871 16872 self.state.running = false; 16873 iteration = 0; 16874 kill(); 16875 self.updateId( self.state.moveForward ? 1 : -1 ); 16876 // If we're creating multiple animation loop we trigger outside only first pass to start all game. 16877 // the rest are triggered as callback 16878 loops -= 1; 16879 if( loops > 0 ) { 16880 build(); 16881 play(); 16882 } 16883 16884 // if we run all loops reset back the default value 16885 if( !loops ) { 16886 loops = 1; 16887 self.timerRemaining = parseInt(self.config.displayTime); 16888 self.config.onAfterSlide( self.state.id ); 16889 } 16890 16891 return; 16892 } 16893 16894 // Run in given mode 16895 if( mode === 'play') { 16896 iteration += 1; 16897 } else { 16898 iteration -= 1; 16899 } 16900 16901 requestAnimationFrame( function() { 16902 if(self.state.stop) return; 16903 move( 0, mode ); 16904 }); 16905 }; 16906 16907 play = function( startProg ) { 16908 16909 var $nextSlide = $(self.slides[ self.nextId(self.state.moveForward ? 1 : -1) ] ); 16910 16911 // If it's Edge Slider and Lazy Load is enabled and It's not a Video Slide 16912 if ( self.config.lazyload && self.config.edgeSlider ) { 16913 16914 // Set the next slide's BG Image 16915 var $slideImg = $nextSlide.find('[data-mk-img-set]'); 16916 if ( $slideImg.length ) { 16917 MK.component.BackgroundImageSetter.init( $slideImg ); 16918 } 16919 16920 } 16921 16922 self.config.onBeforeSlide( self.nextId(self.state.moveForward ? 1 : -1) ); 16923 var start = startProg || 0; 16924 iteration = 0; 16925 self.state.stop = false; 16926 move( start, 'play' ); 16927 16928 }; 16929 16930 reverse = function( startProg ) { 16931 var start = startProg || 1; 16932 move( start, 'reverse' ); 16933 }; 16934 16935 progress = function( progVal ) { 16936 var aI = 0, 16937 currentStyle; 16938 16939 for( aI; aI < aL; aI++ ) { 16940 if( progVal !== 1 && progVal !== 0 ) { 16941 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], animLoop[ aI ][ 2 ] ); 16942 } else if( progVal === 1) { 16943 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], 'linearEase' ); 16944 } else if ( progVal === 0 ) { 16945 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], 'linearEase' ); 16946 } 16947 self.setStyle( animLoop[ aI ][ 0 ], currentStyle ); 16948 } 16949 }; 16950 16951 // Clear previous loop 16952 kill = function() { 16953 animLoop = []; 16954 aL = 0; 16955 }; 16956 16957 16958 return { 16959 build : build, 16960 add : add, 16961 play : play, 16962 reverse : reverse, 16963 progress : progress 16964 }; 16965 }, 16966 16967 16968 // Build reference styles. 16969 // Return object with array containig initial style and change of its value 16970 // as required for easing functions 16971 refStyle : function( toStyles, fromStyles ) { 16972 var axis = this.animation[ this.config.effect ].axis, 16973 style = {}, 16974 initVal, changeVal, endVal, dynamicEnd, styleProp, transProp, transform; 16975 16976 for( styleProp in toStyles ) { 16977 16978 if( styleProp === 'transform' ) { 16979 transform = this.getTransforms( fromStyles ); 16980 style.transform = {}; 16981 16982 for( transProp in toStyles.transform ) { 16983 // don't care about z 16984 if( transProp === 'translateZ' ) { continue; } 16985 16986 initVal = transform[ transProp ] || 0; // if it is undefined it means it's 0 16987 dynamicEnd = ( transProp === 'translate' + axis ) ? initVal : 0; 16988 endVal = toStyles.transform[ transProp ] + dynamicEnd; // it is dynamic, based on slide position in current set 16989 changeVal = endVal - initVal; 16990 style.transform[ transProp ] = [ initVal, changeVal ]; 16991 } 16992 } else if( styleProp === 'zIndex' ) { 16993 // console.log( 'z' ); 16994 continue; 16995 } else { 16996 initVal = parseFloat( fromStyles[ styleProp ] ) || 0; // if it is undefined it means it's 0 16997 endVal = toStyles[ styleProp ]; 16998 changeVal = endVal - initVal; 16999 style[ styleProp ] = [ initVal, changeVal ]; 17000 } 17001 } 17002 17003 return style; 17004 }, 17005 17006 17007 currentStyle : function( progress, style, ease ) { 17008 var self = this, 17009 currentStyle = {}, 17010 currentVals, styleProp, transProp, prog; 17011 17012 // Redo same loop but construct currentStyle object out of cached values 17013 for( styleProp in style ) { 17014 17015 if( styleProp === 'transform' ) { 17016 currentStyle.transform = {}; 17017 17018 for( transProp in style.transform ) { 17019 // remove this line. double check first if needed by logging 17020 if( transProp === 'translateZ' ) { continue; } 17021 17022 currentVals = style.transform[ transProp ]; 17023 currentStyle.transform[ transProp ] = 17024 // (currentIteration, startValue, changeInValue, totalIterations) 17025 self.ease[ ease ]( progress, currentVals[ 0 ], currentVals[ 1 ], 1 ); 17026 } 17027 } else { 17028 currentVals = style[ styleProp ]; 17029 currentStyle[ styleProp ] = 17030 self.ease[ ease ]( progress, currentVals[ 0 ], currentVals[ 1 ], 1 ); 17031 } 17032 } 17033 17034 return currentStyle; 17035 }, 17036 17037 17038 setActive : function( id ) { 17039 var $slides = $( this.slides ), 17040 className = this.config.activeClass; 17041 17042 $slides.removeClass( className ); 17043 17044 if( this.config.hasPagination ) { 17045 var $pagination = this.$pagination.find( 'a' ); 17046 $pagination.removeClass( className ); 17047 $pagination.eq( id ).addClass( className ); 17048 } 17049 17050 if( this.activeTimer ) { 17051 clearTimeout( this.activeTimer ); 17052 if ( this.imageLoader ) { 17053 this.imageLoader.abort(); 17054 } 17055 } 17056 17057 17058 var self = this; 17059 17060 this.activeTimer = setTimeout( function() { 17061 17062 var $currentSlide = $slides.eq( id ); 17063 17064 if ( self.config.lazyload && self.config.edgeSlider ) { // If it's Edge Slider and Lazy Load is enabled 17065
17066 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 17067 17068 var imgSet = $currentSlide.children('.mk-video-section-touch').data('mk-img-set'); 17069 var exactImg = MK.component.BackgroundImageSetter.getImage( imgSet ); 17070 var $bgImage = $('<img>').attr('src', exactImg ); 17071 17072 self.imageLoader = imagesLoaded( $bgImage[0], function( instance ) { 17073 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17074 setTimeout( function() { 17075 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17076 }, 200); 17077 $currentSlide.addClass( className ); 17078 }); 17079 17080 } 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 17081 17082 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17083 setTimeout( function() { 17084 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17085 }, 200); 17086 $currentSlide.addClass( className ); 17087 17088 } else { // If it's a Image Slide 17089 17090 if ( $currentSlide.children('[data-mk-img-set]').length ) { 17091 // Get the matching Image URL to start lazy loading 17092 var imgSet = $currentSlide.children('[data-mk-img-set]').data('mk-img-set'); 17093 var exactImg = MK.component.BackgroundImageSetter.getImage( imgSet ); 17094 var $bgImage = $('<img>').attr('src', exactImg ); 17095 17096 // Prevent counting time on slide until the image loads 17097 self.unsetTimer(); 17098 self.imageLoader = imagesLoaded( $bgImage[0], function( instance ) { 17099 // Hide spinner, Continue counting time on slide and show the content 17100 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17101 setTimeout( function() { 17102 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17103 }, 200); 17104 self.setTimer(false, false, $currentSlide.data('timer') || Number(self.config.displayTime) ); 17105 $currentSlide.addClass( className ); 17106 }); 17107 } else { 17108 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17109 setTimeout( function() { 17110 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17111 }, 200); 17112 self.setTimer(false, false, $currentSlide.data('timer') || Number(self.config.displayTime) ); 17113 $currentSlide.addClass( className ); 17114 } 17115 17116 } 17117 17118 } else { 17119 17120 $currentSlide.addClass( className ); 17121 17122 } 17123 17124 17125 }, this.config.transitionTime ); 17126 }, 17127 17128 createTimer : function() { 17129 17130 var $slide = $(this), 17131 video = $slide.find('video').get(0); 17132 17133 if(video) { 17134 // A hacky but reliable way to ge the video duration 17135 var interval = setInterval( function() { 17136 // If the metadata is ready 17137 if ( video.readyState > 0 ) { 17138 $slide.data('timer', (video.duration * 1000)); 17139 $slide.attr('data-timer', (video.duration * 1000)); 17140 clearInterval(interval); 17141 } 17142 }, 100); 17143 } 17144 17145 }, 17146 17147 setTimer : function( isFirst, isPaused, fixed_time ) { 17148 // check for custom timer 17149 var customTimer = this.$slides.eq(this.nextId(this.state.moveForward ? 1 : -1)).data('timer'), 17150 trans = parseInt( this.config.transitionTime ), 17151 interval = customTimer ? customTimer : parseInt( this.config.displayTime ), 17152 timer = interval + trans; 17153 17154 var self = this, 17155 first = isFirst || true, 17156 fixed_time = fixed_time || 0, 17157 create, run; 17158 17159 this.timer = true; 17160 this.lastSetTimer = Date.now(); 17161 17162 create = function() { 17163 17164 if( self.autoplay ) { clearTimeout( self.autoplay ); } 17165 if( !self.timer ) { 17166 return; 17167 } 17168 self.state.moveForward = true; 17169 self.timeline.build(); 17170 self.timeline.play(); 17171 self.setActive( self.nextId( 1 ) ); 17172 if( self.config.fluidHeight ) { self.setHeight( self.nextId( 1 ) ); } 17173 first = false; 17174 self.lastSetTimer = Date.now(); 17175 17176 run(); 17177 }; 17178 17179 run = function(newInterval) { 17180 // check for custom timer 17181 customTimer = self.$slides.eq(self.nextId(self.state.moveForward ? 1 : -1)).data('timer'); 17182 interval = customTimer ? customTimer : parseInt( self.config.displayTime ); 17183 timer = interval + trans; // update timer with current val 17184 17185 var time = newInterval || timer; 17186 self.autoplay = setTimeout( create, time ); 17187 }; 17188 17189 if ( fixed_time ) { 17190 run( fixed_time ); 17191 } else if (isPaused) { 17192 run( this.timerRemaining ); 17193 } else { 17194 run(); 17195 } 17196 }, 17197 17198 17199 unsetTimer : function() { 17200 this.timer = false;
17201 this.lastUnsetTimer = Date.now(); 17202 this.timerRemaining -= this.lastUnsetTimer - this.lastSetTimer; 17203 if( this.autoplay ) { clearTimeout( this.autoplay ); } 17204 }, 17205 17206 17207 buildPagination : function() { 17208 var i = 0, 17209 len = this.slides.length, 17210 tpl = ''; 17211 17212 for( ; i < len; i += 1 ) { 17213 tpl += '<a href="javascript:;">' + this.config.paginationTpl + '</a>'; 17214 } 17215 17216 this.$pagination.html( tpl ); 17217 this.setActive( 0 ); 17218 }, 17219 17220 17221 getSlideSize : function() { 17222 this.slideSize = { 17223 X: 100 / this.config.slidesPerView, 17224 Y: 100 / this.config.slidesPerView 17225 }; 17226 }, 17227 17228 17229 getTransforms : function( style ) { 17230 // console.log( style ); 17231 var transform = style.transform || style.webkitTransform || style.mozTransform, 17232 regex = /(\w+)\(([^)]*)\)/g, 17233 match, 17234 T = {}; 17235 17236 if( typeof transform !== 'string' ) { 17237 throw 'Transform prop is not a string.'; 17238 } 17239 17240 if( !transform ) { return; } 17241 17242 // Run regex assignment 17243 while( match = regex.exec( transform ) ) { 17244 T[ match[ 1 ] ] = parseFloat( match[ 2 ] ); 17245 } 17246 17247 return T; 17248 }, 17249 17250 isNode : function( o ) { 17251 return ( 17252 typeof Node === "object" ? o instanceof Node : 17253 o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string" 17254 ); 17255 }, 17256 17257 17258 dragHandler : function() { 17259 var self = this, 17260 $container = $( this.container ), 17261 prevBuild = false, 17262 nextBuild = false, 17263 dragging = false, 17264 buffor = 5, // helpful for decoupling with click events 17265 dragStart, dragMove, dragEnd, progress; 17266 17267 progress = function( moveX ) { 17268 return moveX / self.val.viewportW(); 17269 }; 17270 17271 dragStart = function( moveX, startX ) { 17272 // console.log( 'start', moveX, startX ); 17273 }; 17274 17275 dragMove = function( moveX ) { 17276 // console.log('move'); 17277 if( self.state.running ) return; 17278 17279 // Don't need to check for existance here 17280 17281 if( moveX < -buffor ) { 17282 17283 if( !nextBuild ) { 17284 self.state.moveForward = true; 17285 self.timeline.build(); 17286 nextBuild = true; 17287 prevBuild = false; 17288 self.unsetTimer(); 17289 } else { 17290 // turn progress into positive val 17291 self.timeline.progress( -progress( moveX ) ); 17292 } 17293 dragging = true; 17294 } else if( moveX > buffor ) { 17295 17296 if( !prevBuild ) { 17297 self.state.moveForward = false; 17298 self.timeline.build(); 17299 prevBuild = true; 17300 nextBuild = false; 17301 self.unsetTimer(); 17302 } else { 17303 self.timeline.progress( progress( moveX ) ); 17304 } 17305 dragging = true; 17306 } 17307 }; 17308 17309 dragEnd = function( moveX ) { 17310 if( dragging ) { 17311 var prog = progress( moveX ), 17312 absProg = prog < 0 ? -prog : prog; 17313 17314 if( absProg > 0.1 ) { 17315 self.timeline.play( absProg ); 17316 self.setActive( self.nextId( prog < 0 ? 1 : -1 ) ); 17317 if( self.config.fluidHeight ) { self.setHeight( self.nextId( prog < 0 ? 1 : -1 ) ); } 17318 } else { 17319 self.timeline.reverse( absProg ); 17320 // eventually move this to reverse callbacks 17321 if(prog < 0) { 17322 self.updateId( -1 ); 17323 } else { 17324 self.updateId( 1 ); 17325 } 17326 } 17327 17328 prevBuild = false; 17329 nextBuild = false; 17330 dragging = false; 17331 if( self.config.autoplay ) { self.setTimer( false ); } 17332 } 17333 }; 17334 17335 this.drag( $container, dragStart, dragMove, dragEnd ); 17336 }, 17337 17338 17339 drag : function( $el, startFn, moveFn, stopFn ) { 17340 17341 var touchX, touchY, movX, movY, go, evt, 17342 prevent, start, move, stop; 17343 17344 prevent = function( e ) { 17345 e.preventDefault(); 17346 }; 17347 17348 start = function( e ) { 17349 // $el.on("touchmove", prevent); 17350 $el.on("mousemove", prevent); 17351 $el.on("touchmove", move); 17352 $el.on("mousemove", move); 17353 17354 evt = (e.type === 'touchstart') ? e.originalEvent.touches[0] : e; 17355 touchX = evt.pageX; 17356 17357 if(typeof startFn === 'function') { 17358 startFn(movX, touchX); 17359 } 17360 }; 17361 17362 move = function( e ) { 17363 evt = (e.type === 'touchmove') ? e.originalEvent.touches[0] : e; 17364 movX = evt.pageX - touchX; 17365 17366 if(typeof moveFn === 'function') { 17367 moveFn(movX); 17368 } 17369 }; 17370 17371 stop = function( e ) { 17372 // $el.off("touchmove", prevent); 17373 $el.off("mousemove", prevent); 17374 $el.off("touchmove", move); 17375 $el.off("mousemove", move); 17376 17377 if(typeof stopFn === 'function') { 17378 stopFn(movX); 17379 } 17380 }; 17381 17382 $el.on("touchstart", start); 17383 $el.on("mousedown", start); 17384 $el.on("touchend", stop); 17385 $el.on("touchleave", stop); 17386 $el.on("touchcancel", stop);
17387 $el.on("mouseup", stop); 17388 $el.on("mouseleave", stop); 17389 }, 17390 17391 17392 dynamicVal : function() { 17393 var $window = $( window ), 17394 update, 17395 getViewportW, viewportW; 17396 17397 update = function() { 17398 viewportW = $window.width(); 17399 }; 17400 17401 getViewportW = function() { 17402 return viewportW; 17403 }; 17404 17405 update(); 17406 $window.on( 'load', update ); 17407 $window.on( 'resize', update ); 17408 17409 return { 17410 viewportW : getViewportW 17411 }; 17412 } 17413 }; 17414 17415 17416 17417 // 17418 // Set of default animations 17419 // 17420 // ///////////////////////////////////////////////////////// 17421 17422 MK.ui.Slider.prototype.animation = { 17423 17424 slide : { 17425 axis : 'X', 17426 next : { transform: {} }, 17427 active : { transform: {} }, 17428 prev : { transform: {} } 17429 }, 17430 17431 vertical_slide : { 17432 axis : 'Y', 17433 next : { transform: {} }, 17434 active : { transform: {} }, 17435 prev : { transform: {} } 17436 }, 17437 17438 perspective_flip : { 17439 axis : 'Y', 17440 next : { 17441 transform: { 17442 rotateX : 80 17443 } 17444 }, 17445 active : { 17446 transform: { 17447 rotateX : 0 17448 } 17449 }, 17450 prev : { 17451 transform: { 17452 rotateX : 0 17453 } 17454 } 17455 }, 17456 17457 zoom : { 17458 axis : 'Z', 17459 next: { 17460 opacity : 0, 17461 transform : { 17462 scale : 0.9 17463 } 17464 }, 17465 active: { 17466 opacity : 1, 17467 transform : { 17468 scale : 1 17469 } 17470 }, 17471 prev: { 17472 opacity : 0, 17473 transform : { 17474 scale : 1.1 17475 } 17476 } 17477 }, 17478 17479 fade : { 17480 axis : 'Z', 17481 next: { 17482 opacity : 0, 17483 transform : {} 17484 }, 17485 active: { 17486 opacity : 1, 17487 transform : {} 17488 }, 17489 prev: { 17490 opacity : 0, 17491 transform : {} 17492 } 17493 }, 17494 17495 kenburned : { 17496 axis : 'Z', 17497 next: { 17498 opacity : 0, 17499 transform : {} 17500 }, 17501 active: { 17502 opacity : 1, 17503 transform : {} 17504 }, 17505 prev: { 17506 opacity : 0, 17507 transform : {} 17508 } 17509 }, 17510 17511 zoom_out : { 17512 axis : 'Z', 17513 next: { 17514 zIndex : '+', 17515 opacity : 1, 17516 transform : { 17517 translateY : 100, 17518 scale : 1 17519 } 17520 }, 17521 active: { 17522 opacity : 1, 17523 transform : { 17524 translateY : 0, 17525 scale : 1 17526 } 17527 }, 17528 prev: { 17529 zIndex : '+', 17530 opacity : 0, 17531 transform : { 17532 translateY : 0, 17533 scale : 0.5 17534 } 17535 } 17536 }, 17537 17538 // Problem with Z-Flow 17539 horizontal_curtain : { 17540 axis : 'Z', 17541 next: { 17542 zIndex : '+', 17543 transform : { 17544 translateX : 100, 17545 } 17546 }, 17547 active: { 17548 transform : { 17549 translateX : 0, 17550 } 17551 }, 17552 prev: { 17553 zIndex : '+', 17554 transform : { 17555 translateX : -70, 17556 } 17557 } 17558 }, 17559 17560 roulete : { 17561 axis : 'X', 17562 next: { 17563 opacity : 0.5, 17564 transform : { 17565 scale : 0.5, 17566 rotate : 10, 17567 translateY : 20 17568 } 17569 }, 17570 active: { 17571 opacity : 1, 17572 transform : { 17573 scale : 1, 17574 rotate : 0, 17575 translateY : 0 17576 } 17577 }, 17578 prev: { 17579 opacity : 0.3, 17580 transform : { 17581 scale : 0.5, 17582 rotate : -10, 17583 translateY : 20 17584 } 17585 } 17586 } 17587 }; 17588 17589 17590 17591 // 17592 // Penner's easing library 17593 // 17594 // ///////////////////////////////////////////////////////// 17595 17596 MK.ui.Slider.prototype.ease = { 17597 /* 17598 *
17599 * TERMS OF USE - EASING EQUATIONS 17600 * 17601 * Open source under the BSD License. 17602 * 17603 * Copyright © 2001 Robert Penner 17604 * All rights reserved. 17605 * 17606 * Redistribution and use in source and binary forms, with or without modification, 17607 * are permitted provided that the following conditions are met: 17608 * 17609 * Redistributions of source code must retain the above copyright notice, this list of 17610 * conditions and the following disclaimer. 17611 * Redistributions in binary form must reproduce the above copyright notice, this list 17612 * of conditions and the following disclaimer in the documentation and/or other materials 17613 * provided with the distribution. 17614 * 17615 * Neither the name of the author nor the names of contributors may be used to endorse 17616 * or promote products derived from this software without specific prior written permission. 17617 * 17618 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17619 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17620 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 17621 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17622 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 17623 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 17624 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17625 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 17626 * OF THE POSSIBILITY OF SUCH DAMAGE. 17627 * 17628 */ 17629 linearEase : function(currentIteration, startValue, changeInValue, totalIterations) { 17630 return changeInValue * currentIteration / totalIterations + startValue; 17631 }, 17632 17633 easeInQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17634 return changeInValue * (currentIteration /= totalIterations) * currentIteration + startValue; 17635 }, 17636 17637 easeOutQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17638 return -changeInValue * (currentIteration /= totalIterations) * (currentIteration - 2) + startValue; 17639 }, 17640 17641 easeInOutQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17642 if ((currentIteration /= totalIterations / 2) < 1) { 17643 return changeInValue / 2 * currentIteration * currentIteration + startValue; 17644 } 17645 return -changeInValue / 2 * ((--currentIteration) * (currentIteration - 2) - 1) + startValue; 17646 }, 17647 17648 easeInCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17649 return changeInValue * Math.pow(currentIteration / totalIterations, 3) + startValue; 17650 }, 17651 17652 easeOutCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17653 return changeInValue * (Math.pow(currentIteration / totalIterations - 1, 3) + 1) + startValue; 17654 }, 17655 17656 easeInOutCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17657 if ((currentIteration /= totalIterations / 2) < 1) { 17658 return changeInValue / 2 * Math.pow(currentIteration, 3) + startValue; 17659 } 17660 return changeInValue / 2 * (Math.pow(currentIteration - 2, 3) + 2) + startValue; 17661 }, 17662 17663 easeInQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17664 return changeInValue * Math.pow (currentIteration / totalIterations, 4) + startValue; 17665 }, 17666 17667 easeOutQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17668 return -changeInValue * (Math.pow(currentIteration / totalIterations - 1, 4) - 1) + startValue; 17669 }, 17670 17671 easeInOutQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17672 if ((currentIteration /= totalIterations / 2) < 1) { 17673 return changeInValue / 2 * Math.pow(currentIteration, 4) + startValue; 17674 } 17675 return -changeInValue/2 * (Math.pow(currentIteration - 2, 4) - 2) + startValue; 17676 }, 17677 17678 easeInQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17679 return changeInValue * Math.pow (currentIteration / totalIterations, 5) + startValue; 17680 }, 17681 17682 easeOutQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17683 return changeInValue * (Math.pow(currentIteration / totalIterations - 1, 5) + 1) + startValue; 17684 }, 17685 17686 easeInOutQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17687 if ((currentIteration /= totalIterations / 2) < 1) { 17688 return changeInValue / 2 * Math.pow(currentIteration, 5) + startValue; 17689 } 17690 return changeInValue / 2 * (Math.pow(currentIteration - 2, 5) + 2) + startValue; 17691 }, 17692 17693 easeInSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17694 return changeInValue * (1 - Math.cos(currentIteration / totalIterations * (Math.PI / 2))) + startValue; 17695 }, 17696 17697 easeOutSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17698 return changeInValue * Math.sin(currentIteration / totalIterations * (Math.PI / 2)) + startValue; 17699 }, 17700 17701 easeInOutSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17702 return changeInValue / 2 * (1 - Math.cos(Math.PI * currentIteration / totalIterations)) + startValue; 17703 }, 17704 17705 easeInExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17706 return changeInValue * Math.pow(2, 10 * (currentIteration / totalIterations - 1)) + startValue; 17707 }, 17708 17709 easeOutExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17710 return changeInValue * (-Math.pow(2, -10 * currentIteration / totalIterations) + 1) + startValue; 17711 }, 17712 17713 easeInOutExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17714 if ((currentIteration /= totalIterations / 2) < 1) { 17715 return changeInValue / 2 * Math.pow(2, 10 * (currentIteration - 1)) + startValue; 17716 } 17717 return changeInValue / 2 * (-Math.pow(2, -10 * --currentIteration) + 2) + startValue; 17718 }, 17719 17720 easeInCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17721 return changeInValue * (1 - Math.sqrt(1 - (currentIteration /= totalIterations) * currentIteration)) + startValue; 17722 }, 17723 17724 easeOutCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17725 return changeInValue * Math.sqrt(1 - (currentIteration = currentIteration / totalIterations - 1) * currentIteration) + startValue; 17726 }, 17727 17728 easeInOutCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17729 if ((currentIteration /= totalIterations / 2) < 1) { 17730 return changeInValue / 2 * (1 - Math.sqrt(1 - currentIteration * currentIteration)) + startValue; 17731 } 17732 return changeInValue / 2 * (Math.sqrt(1 - (currentIteration -= 2) * currentIteration) + 1) + startValue; 17733 } 17734 }; 17735 17736})(jQuery); 17737/* Social Share */ 17738/* -------------------------------------------------------------------- */ 17739(function($) { 17740 'use strict'; 17741 17742 MK.component.SocialShare = function( el ) { 17743 var networks = { 17744 twitter : 'http://twitter.com/intent/tweet?text={title}&url={url}', 17745 pinterest : 'http://pinterest.com/pin/create/button/?url={url}&media={image}&description={title}', 17746 facebook : 'https://www.facebook.com/sharer/sharer.php?u={url}', 17747 googleplus : 'https://plus.google.com/share?url={url}', 17748 linkedin : 'http://www.linkedin.com/shareArticle?mini=true&url={url}&title={title}&summary={desc}', 17749 digg : 'http://digg.com/submit?url={url}&title={title}', 17750 reddit : 'http://reddit.com/submit?url={url}&title={title}', 17751 }; 17752 17753 this.networks = networks; 17754 this.el = el; 17755 }; 17756 17757
17758 MK.component.SocialShare.prototype = { 17759 17760 init : function() { 17761 this.cacheElements(); 17762 this.bindEvents(); 17763 }, 17764 17765 cacheElements : function() { 17766 this.$this = $( this.el ); 17767 17768 17769 }, 17770 17771 bindEvents : function() { 17772 var thisObject = this; 17773 var tempClass = ""; 17774 $.each(this.networks, function( key, value ) { 17775 thisObject.$tempClass = $('.' + key + '-share'); 17776 thisObject.$tempClass.click(thisObject.openSharingDialog.bind(self, this, key)); 17777 17778 }); 17779 }, 17780 17781 openSharingDialog : function(url, site, args) { 17782 var urlWrapper = url; 17783 var rx = new RegExp('\{[a-z]*\}','g'), res; 17784 var match = rx.exec(url); 17785 while (match != null) { 17786 var pureAttr = match[0].replace("{", "").replace("}" , ""); 17787 var attValue = $(args.currentTarget).attr('data-' + pureAttr); 17788 if (attValue === undefined || attValue === null) { 17789 attValue = ""; 17790 } 17791 attValue = attValue.replace('#', '%23'); // fix for twitter description 17792 urlWrapper = urlWrapper.replace(match, attValue); 17793 match = rx.exec(url); 17794 } 17795 17796 window.open(urlWrapper, site + "Window", "height=320,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17797 17798 }, 17799 17800 17801 }; 17802 17803 17804 ////////////////////////////////////////// 17805 // 17806 // Apply to: 17807 // 17808 // /////////////////////////////////////// 17809 17810 var $body = $('body'); 17811 17812 if(!$body.length) return; 17813 17814 $body.each(function() { 17815 var socialShare = new MK.component.SocialShare(this); 17816 socialShare.init(); 17817 }); 17818 17819})(jQuery); 17820 17821 17822 17823//function mk_social_share() { 17824// 17825// "use strict";
17826// 17827// $('.twitter-share').on('click', function () { 17828// var $url = $(this).attr('data-url'), 17829// $title = $(this).attr('data-title'); 17830// 17831// 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"); 17832// return false; 17833// }); 17834// 17835// $('.pinterest-share').on('click', function () { 17836// var $url = $(this).attr('data-url'), 17837// $title = $(this).attr('data-title'), 17838// $image = $(this).attr('data-image'); 17839// 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"); 17840// return false; 17841// }); 17842// 17843// $('.facebook-share').on('click', function () { 17844// var $url = $(this).attr('data-url'); 17845// 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"); 17846// return false; 17847// }); 17848// 17849// $('.googleplus-share').on('click', function () { 17850// var $url = $(this).attr('data-url'); 17851// 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"); 17852// return false; 17853// }); 17854// 17855// $('.linkedin-share').on('click', function () { 17856// var $url = $(this).attr('data-url'), 17857// $title = $(this).attr('data-title'), 17858// $desc = $(this).attr('data-desc'); 17859// 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"); 17860// return false; 17861// }); 17862//} 17863 17864 17865 17866 17867(function($) { 17868 'use strict'; 17869 17870 MK.component.Sortable = function(el) { 17871 this.el = el; 17872 }; 17873 17874 MK.component.Sortable.prototype = { 17875 init: function init() { 17876 this.cacheElements(); 17877 this.bindEvents(); 17878 }, 17879 17880 cacheElements: function cacheElements() { 17881 this.unique = Date.now(); 17882 this.$filter = $(this.el); 17883 this.config = this.$filter.data('sortable-config'); 17884 17885 this.ajaxLoader = new MK.utils.ajaxLoader(this.config.container); 17886 this.ajaxLoader.init(); 17887 17888 this.$container = $( this.config.container ); 17889 this.$navItems = this.$filter.find('a'); 17890 this.$filterItems = this.$container.find(this.config.item); 17891 }, 17892 17893 bindEvents: function bindEvents() { 17894 this.$navItems.on('click', this.handleClick.bind(this)); 17895 MK.utils.eventManager.subscribe('ajaxLoaded', this.onLoad.bind(this)); 17896 }, 17897 17898 handleClick: function handleClick(e) { 17899 e.preventDefault(); 17900 17901 var $item = $(e.currentTarget); 17902 var term = $item.data('filter'); 17903 17904 this.$navItems.removeClass('current'); 17905 $item.addClass('current'); 17906 17907 if(this.config.mode === 'ajax') this.inDB(term, $item); 17908 else this.inPage(term); 17909 }, 17910 17911 inDB: function inDB(term, $item) { 17912 // Add load indicator only for long requests 17913 MK.ui.loader.remove(this.$filter); 17914 MK.ui.loader.add($item); 17915 17916 // If mk-ajax-loaded-posts span exists and one of the filter is clicked, 17917 // clear post ids 17918 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 17919 this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts', ''); 17920 } 17921 17922 this.ajaxLoader.setData({ 17923 paged: 1, 17924 term: term 17925 }); 17926 this.ajaxLoader.load(this.unique); 17927 }, 17928 17929 inPage: function inPage(term) { 17930 var $filterItems = this.$container.find(this.config.item); 17931 $filterItems.removeClass('is-hidden'); // show all first 17932 // Replace all ', ' with ', .'. It's used to add '.' as class selector of each category. 17933 var className = term.replace( /, /g, ", ." ); 17934 if(term !== '*') $filterItems.not( '.' + className ).addClass('is-hidden'); // hide filtered 17935 MK.utils.eventManager.publish('staticFilter'); 17936 }, 17937 17938 onLoad: function onLoad(e, response) { 17939 if(this.config.mode === 'static') { 17940 this.$navItems.removeClass('current').first().addClass('current'); 17941 } 17942 if( typeof response !== 'undefined' && response.id === this.config.container) { 17943 MK.ui.loader.remove(this.$filter); 17944 if(response.unique === this.unique) { 17945 this.$container.html(response.content); 17946 this.ajaxLoader.setData({paged: 1}); 17947 } 17948 } 17949 } 17950 }; 17951 17952})(jQuery); 17953(function($) { 17954 'use strict'; 17955 17956 var $iframes = $('iframe'); 17957 17958 $iframes.each(function() { 17959 var $iframe = $(this); 17960 var parent = $iframe.parent().get(0); 17961 var tagName = parent.tagName; 17962 17963 if(tagName === 'P') $iframe.wrap('<div class="mk-video-container"></div>'); 17964 }); 17965 17966}(jQuery)); 17967(function($) { 17968 'use strict'; 17969 17970 $(document).on('click', function(e) { 17971 $('.mk-toggle-trigger').removeClass('mk-toggle-active'); 17972 }); 17973 17974 function toggle(e) { 17975 e.preventDefault(); 17976 e.stopPropagation(); 17977 var $this = $(e.currentTarget); 17978 17979 if (!$this.hasClass('mk-toggle-active')) { 17980 17981 $('.mk-box-to-trigger').fadeOut(200); 17982 $this.parent().find('.mk-box-to-trigger').fadeIn(250); 17983 $('.mk-toggle-trigger').removeClass('mk-toggle-active'); 17984 $this.addClass('mk-toggle-active'); 17985 17986 } else { 17987 17988 $('.mk-box-to-trigger').fadeOut(200); 17989 $this.removeClass('mk-toggle-active'); 17990 17991 } 17992 } 17993 17994 function assignToggle() { 17995 // wait for ajax response propagation and insertion 17996 setTimeout(function() { 17997 $('.mk-toggle-trigger').off('click', toggle); 17998 $('.mk-toggle-trigger').on('click', toggle); 17999 }, 100); 18000 } 18001 18002 assignToggle(); 18003 MK.utils.eventManager.subscribe('ajaxLoaded', assignToggle); 18004 MK.utils.eventManager.subscribe('ajax-preview', assignToggle); 18005 18006 $(window).on('vc_reload', function(){ 18007 assignToggle(); 18008 MK.utils.eventManager.subscribe('ajaxLoaded', assignToggle); 18009 MK.utils.eventManager.subscribe('ajax-preview', assignToggle); 18010 }); 18011 18012}(jQuery)); 18013(function( $ ) { 18014 'use strict'; 18015 if( MK.utils.isMobile() ) { 18016 $('.mk-animate-element').removeClass('mk-animate-element'); 18017 return; 18018 }
18019 var init = function init() { 18020 var $rootLevelEls = $('.js-master-row, .widget'); 18021 $rootLevelEls.each( spyViewport ); 18022 $rootLevelEls.each( function rootLevelEl() { 18023 var $animateEl = $(this).find( '.mk-animate-element' ); 18024 $animateEl.each( spyViewport ); 18025 /** 18026 * Firefox has known issue where horizontal scrollbar will appear if an 18027 * element uses animation CSS. The solution should be set the element 18028 * position as fixed or overflow-x as hidden. Position fixed is not possible 18029 * to use because it's only cause other big problems. The best way is 18030 * set overflow-x as hidden in the page content container #theme-page. 18031 * 18032 * NOTE: The problem is spotted on Right To Left viewport only. So, it's 18033 * limited to '.right-to-left' selector only for now to avoid other 18034 * problems. Please extend the functionallity if it's happen in 18035 * other viewport animation effect. 18036 */ 18037 var browserName = MK.utils.browser.name; 18038 if ( browserName === 'Firefox' ) { 18039 var $rightToLeft = $( this ).find( '.right-to-left' ); 18040 if ( $rightToLeft.length > 0 ) { 18041 $( '#theme-page' ).css( 'overflow-x', 'hidden' ); 18042 } 18043 } 18044 }); 18045 }; 18046 var spyViewport = function spyViewport(i) { 18047 var self = this; 18048 MK.utils.scrollSpy( this, { 18049 position : 'bottom', 18050 threshold : 200, 18051 after : function() { 18052 animate.call(self, i); 18053 } 18054 }); 18055 }; 18056 var animate = function animate(i) { 18057 var $this = $(this); 18058 setTimeout(function() { 18059 $this.addClass( 'mk-in-viewport' ); 18060 }, 100 * i); 18061 }; 18062 $(window).on('load vc_reload', init); 18063}(jQuery)); 18064 18065function product_loop_add_cart() { 18066 var $body = $('body'); 18067 $body.on('click', '.add_to_cart_button', function() { 18068 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>'; 18069 var $holder = $(this).parents('.product:eq(0)'); 18070 var $i = $holder.find('.product-loading-icon'); 18071 $holder.addClass('adding-to-cart').removeClass('added-to-cart'); 18072 $i.html(icon); 18073 }); 18074 $body.bind('added_to_cart', function() { 18075 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>'; 18076 var $holder = $('.adding-to-cart'); 18077 var $i = $holder.find('.product-loading-icon'); 18078 $holder.removeClass('adding-to-cart').addClass('added-to-cart'); 18079 $i.html(icon); 18080 }); 18081} 18082(function($) { 18083 'use strict'; 18084 18085 MK.component.Tabs = function( el ) { 18086 var defaults = { 18087 activeClass : 'is-active' 18088 }; 18089 18090 this.config = defaults; 18091 this.el = el; 18092 }; 18093 18094 MK.component.Tabs.prototype = { 18095 18096 init : function() { 18097 this.cacheElements(); 18098 this.bindEvents(); 18099 }, 18100 18101 cacheElements : function() { 18102 this.$this = $( this.el ); 18103 this.$tabs = this.$this.find( '.mk-tabs-tab' ); 18104 this.$panes = this.$this.find( '.mk-tabs-pane' ); 18105 this.currentId = 0; 18106 }, 18107 18108 bindEvents : function() { 18109 var self = this; 18110 18111 this.$tabs.on( 'click', this.switchPane.bind( this ) ); 18112 }, 18113 18114 switchPane : function( evt ) { 18115 evt.preventDefault(); 18116 18117 var clickedId = $( evt.currentTarget ).index(); 18118 18119 this.hide( this.currentId ); 18120 this.show( clickedId ); 18121 18122 // Update current id 18123 this.currentId = clickedId; 18124 18125 // Notify rest of the app 18126 MK.utils.eventManager.publish('item-expanded'); 18127 }, 18128 18129 show : function( id ) { 18130 this.$tabs.eq( id ).addClass( this.config.activeClass ); 18131 this.$panes.eq( id ).addClass( this.config.activeClass ); 18132 }, 18133 18134 hide : function( id ) { 18135 this.$tabs.eq( id ).removeClass( this.config.activeClass ); 18136 this.$panes.eq( id ).removeClass( this.config.activeClass ); 18137 } 18138 }; 18139 18140})(jQuery); 18141 18142 18143/* Tabs */ 18144/* -------------------------------------------------------------------- */ 18145 18146function mk_tabs() { 18147 18148 // "use strict"; 18149 18150 // if ($.exists('.mk-tabs, .mk-news-tab, .mk-woo-tabs')) { 18151 // $(".mk-tabs, .mk-news-tab, .mk-woo-tabs").tabs(); 18152 18153 // $('.mk-tabs').on('click', function () { 18154 // $('.mk-theme-loop').isotope('layout'); 18155 // }); 18156 18157 // $('.mk-tabs.vertical-style').each(function () { 18158 // $(this).find('.mk-tabs-pane').css('minHeight', $(this).find('.mk-tabs-tabs').height() - 1); 18159 // }); 18160 18161 // } 18162} 18163 18164function mk_tabs_responsive(){ 18165 // $('.mk-tabs, .mk-news-tab').each(function () { 18166 // $this = $(this); 18167 // if ($this.hasClass('mobile-true')) { 18168 // if (window.matchMedia('(max-width: 767px)').matches) 18169 // { 18170 // $this.tabs("destroy"); 18171 // } else { 18172 // $this.tabs(); 18173 // } 18174 // } 18175 // }); 18176 18177} 18178 18179 18180(function($) { 18181 'use strict'; 18182 18183 /** 18184 * Entry point of application. Runs all components 18185 */ 18186 $( window ).on( 'load', function() { 18187 MK.core.initAll( document ); 18188 MK.utils.scrollToURLHash(); 18189 // TODO move preloader to components and manage it state from within 18190 setTimeout( function() { 18191 MK.ui.preloader.hide(); // site wide 18192 $('.mk-preloader').hide(); // components 18193 $('body').removeClass('loading'); 18194 }, 150 ); 18195 }); 18196 18197 /** 18198 * VC frontend editor. Init all components. 18199 */ 18200 $( window ).on( 'vc_reload', function() { 18201 setTimeout(function(){ 18202 MK.core.initAll( document ); 18203 }, 100); 18204 }); 18205 18206 /** 18207 * Assign global click handlers 18208 */ 18209 $( document ).on( 'click', '.js-smooth-scroll, .js-main-nav a', smoothScrollToAnchor); 18210 $( '.side_dashboard_menu a' ).on( 'click', smoothScrollToAnchor); 18211 18212 function smoothScrollToAnchor( evt ) { 18213 var anchor = MK.utils.detectAnchor( this ); 18214 var $this = $(evt.currentTarget); 18215 var loc = window.location; 18216 var currentPage = loc.origin + loc.pathname; 18217 var href = $this.attr( 'href' ); 18218 var linkSplit = (href) ? href.split( '#' ) : ''; 18219 var hrefPage = linkSplit[0] ? linkSplit[0] : ''; 18220 var hrefHash = linkSplit[1] ? linkSplit[1] : ''; 18221 18222 if( anchor.length ) { 18223 if(hrefPage === currentPage || hrefPage === '') evt.preventDefault(); 18224 MK.utils.scrollToAnchor( anchor ); 18225 18226 } else if( $this.attr( 'href' ) === '#' ) { 18227 evt.preventDefault(); 18228 } 18229 } 18230 18231}(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.