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.
vendor: 1,418 bytes, lines 1093-1140
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) {
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 window.jupiterCaptchaContactFormCaptchaToken = function( token ) { 12692 window.jupiterContactFormNewCreatedCaptchaToken = token; 12693 }; 12694 12695 /** 12696 * Initialize mk forms. e.g add activeClassName for inputs. 12697 * 12698 * @param form 12699 * @param activeClassName 12700 * @param invalidClassName 12701 */ 12702 function initializeForm(form, activeClassName, invalidClassName) { 12703 var inputs = getFormInputs(form); 12704 for (var i = 0; i < inputs.length; i++) { 12705 markActiveClass(inputs[i]); 12706 } 12707 form.addEventListener('submit', function(e) { 12708 validateForm(e, invalidClassName); 12709 }); 12710 /** 12711 * Set activeClassName for the parent node of the inout 12712 */ 12713 function setActiveClass() { 12714 addClass(this.parentNode, activeClassName); 12715 } 12716 /** 12717 * Unset activeClassName from the parent node of the input. 12718 * We need to unset activeClassName only if the data was empty. 12719 * e.g. in the line style of the mk-contact-form, we set labels position based on activeClassName. 12720 */ 12721 function unsetActiveClass() { 12722 if (this.value === '') { 12723 removeClass(this.parentNode, activeClassName); 12724 } 12725 } 12726 /** 12727 * Add event listeners (focus,blur) for input to set and unset activeClassName. 12728 * 12729 * @param input 12730 */ 12731 function markActiveClass(input) { 12732 input.addEventListener('focus', setActiveClass); 12733 input.addEventListener('blur', unsetActiveClass); 12734 } 12735 } 12736 /** 12737 * Validate form when it's submitted. If everything was valid, we with post form in ajax request. 12738 * 12739 * @param e 12740 * @param invalidClassName 12741 */ 12742 function validateForm(e, invalidClassName) { 12743 e.preventDefault(); 12744 var form = e.target || e.srcElement; 12745 var inputs = getFormInputs(form); 12746 var isValidForm = true; 12747 var hasCaptchaField = false; 12748 for (var i = 0; i < inputs.length; i++) { 12749 var input = inputs[i]; 12750 input.value = String(input.value).trim(); 12751 switch (input.type) { 12752 case 'hidden': 12753 break; 12754 case 'checkbox': 12755 isValidForm = validateCheckBox(input, invalidClassName) && isValidForm; 12756 break; 12757 case 'email': 12758 isValidForm = validateEmail(input, invalidClassName) && isValidForm; 12759 break; 12760 case 'textarea': 12761 isValidForm = validateText(input, invalidClassName) && isValidForm; 12762 break; 12763 case 'text': 12764 /** 12765 * Some old browsers such as IE 8 and 9 detect email type as text. 12766 * So, we need to extra check for data-type attribute 12767 */ 12768 if (input.dataset.type === 'captcha') { 12769 isValidForm = validateText(input, invalidClassName) && isValidForm; 12770 hasCaptchaField = true; 12771 } else if (input.dataset.type === 'email') { 12772 isValidForm = validateEmail(input, invalidClassName) && isValidForm; 12773 } else { 12774 isValidForm = validateText(input, invalidClassName) && isValidForm; 12775 } 12776 break; 12777 default: 12778 /** 12779 * e.g. validating for radiobox, selectbox and etc. 12780 */ 12781 console.warn('Implement validation for ' + input.name + ':' + input.type); 12782 break; 12783 } 12784 } 12785 if (isValidForm) { 12786 if (hasCaptchaField) { 12787 validateCaptcha(form, invalidClassName, sendForm); 12788 } else { 12789 sendForm(form); 12790 } 12791 } 12792 } 12793 /** 12794 * Validate captcha of form. If everything was, we will execute captchaIsValidCallback which as sendForm(). 12795 * 12796 * @param form 12797 * @param invalidClassName 12798 * @param captchaIsValidCallback 12799 * @returns boolean 12800 */ 12801 function validateCaptcha(form, invalidClassName, captchaIsValidCallback) { 12802 var input = form.querySelectorAll('[data-type="captcha"]')[0]; 12803 if (input.value.length === 0) { 12804 addClass(input, invalidClassName); 12805 return false;
12806 } else { 12807 window.get.captcha(input.value).done(function(data) { 12808 loadCaptcha(); 12809 input.value = ''; 12810 if (data !== 'ok') { 12811 addClass(input, invalidClassName); 12812 addClass(input, 'contact-captcha-invalid'); 12813 removeClass(input, 'contact-captcha-valid'); 12814 input.placeholder = mk_captcha_invalid_txt; 12815 } else { 12816 removeClass(input, invalidClassName); 12817 removeClass(input, 'contact-captcha-invalid'); 12818 addClass(input, 'contact-captcha-valid'); 12819 input.placeholder = mk_captcha_correct_txt; 12820 captchaIsValidCallback(form); 12821 } 12822 }); 12823 } 12824 } 12825 12826 function callTheAjax( data, $form, progressButton ) { 12827 $.post(ajaxurl, data, function(response) { 12828 var res = JSON.parse(response); 12829 if (res.action_Status) { 12830 progressButton.success($form); 12831 $form.find('.text-input').val(''); 12832 $form.find('textarea').val(''); 12833 $form.find('input[type=checkbox]').attr("checked", false); 12834 $form.find('.contact-form-message').slideDown().addClass('state-success').html(res.message); 12835 setTimeout(function() { 12836 $form.find('.contact-form-message').slideUp(); 12837 }, 5000); 12838 } else { 12839 progressButton.error($form); 12840 $form.find('.contact-form-message').removeClass('state-success').html(res.message); 12841 } 12842 }); 12843 } 12844 12845 /** 12846 * Send submitted form. 12847 * 12848 * @param form 12849 */ 12850 function sendForm(form) { 12851 var $form = $(form); 12852 var data = getFormData(form); 12853 progressButton.loader($form); 12854 12855 // No captcha enabled. 12856 if ( $form.find( '.g-recaptcha' ).length < 1 ) { 12857 callTheAjax( data, $form, progressButton ); 12858 return; 12859 } 12860 12861 const siteKey = $form.find( '.g-recaptcha' ).attr( 'data-sitekey' ); 12862 const captchaVersion = $form.find( '.g-recaptcha' ).attr( 'data-type' ); 12863 12864 // Captcha v3 enabled. 12865 if ( 'v3' === captchaVersion ) { 12866 grecaptcha.execute( siteKey, { action: 'submit' } ).then( function( token ) { 12867 data['g-recaptcha-response'] = token; 12868 data['g-recaptcha-version'] = captchaVersion; 12869 12870 callTheAjax( data, $form, progressButton ) 12871 } ); 12872 } 12873 12874 // Captcha v2 enabled 12875 if ( 'v2' === captchaVersion ) { 12876 grecaptcha.execute().then( function() { 12877 setTimeout( function() { 12878 data['g-recaptcha-response'] = jupiterContactFormNewCreatedCaptchaToken; 12879 data['g-recaptcha-version'] = captchaVersion; 12880 12881 callTheAjax( data, $form, progressButton ) 12882 }, 2000 ); 12883 } ); 12884 } 12885 } 12886 /** 12887 * Initialize all captcha images for first time. All captcha images is always same. e.g. if we have multiple form, 12888 * all of them will have the same image. 12889 * It will also add event listener for '.captcha-change-image' objects to reload the captcha. 12890 */ 12891 function initializeCaptchas() { 12892 var captchaChangeImageButtons = document.getElementsByClassName('captcha-change-image'); 12893 for (var i = 0; i < captchaChangeImageButtons.length; i++) { 12894 captchaChangeImageButtons[i].addEventListener('click', loadCaptcha); 12895 } 12896 } 12897 /** 12898 * Load captcha text and append the image to captcha container. 12899 * If it used as a callback, it will prevent default behave of the event. 12900 * e.g. loading new captcha by click on <a> without changing url. 12901 */ 12902 function loadCaptcha(e) { 12903 if (e) { 12904 e.preventDefault(); 12905 } 12906 $.post(ajaxurl, { 12907 action: 'mk_create_captcha_image' 12908 }, appendImage); 12909 /** 12910 * The callback function for append or change old image src based on response. T 12911 * The captchaImageURL is the url of the captcha which is provided in ajax response of mk_create_captcha_image. 12912 * @param captchaImageURL 12913 */ 12914 function appendImage(captchaImageURL) { 12915 if (captchaImageHolder.find('.captcha-image').length === 0) { 12916 captchaImageHolder.html('<img src="' + captchaImageURL + '" class="captcha-image" alt="captcha txt">'); 12917 } else { 12918 captchaImageHolder.find('.captcha-image').attr("src", captchaImageURL + '?' + new Date().getTime()); 12919 } 12920 } 12921 } 12922 /** 12923 * Get form inputs using querySelectorAll(). 12924 * It returns <input> and <textarea> tags. If you need any other tags such as <select>, please update this function. 12925 * 12926 * @param form 12927 * @returns {NodeList} 12928 */ 12929 function getFormInputs(form) { 12930 return form.querySelectorAll('input,textarea'); 12931 } 12932 /** 12933 * Get data of the form inputs and textareas as a object. 12934 * 12935 * @param form 12936 * @returns {{action: string}} 12937 */ 12938 function getFormData(form) { 12939 var data = { 12940 action: 'mk_contact_form' 12941 }; 12942 var inputs = getFormInputs(form); 12943 for (var i = 0; i < inputs.length; i++) {
12944 data[inputs[i].name] = inputs[i].value; 12945 } 12946 return data; 12947 } 12948} 12949/* Ajax Login Form */ 12950/* -------------------------------------------------------------------- */ 12951function mk_login_form() { 12952 $('form.mk-login-form').each(function() { 12953 var $this = $(this); 12954 $this.on('submit', function(e) { 12955 $('p.mk-login-status', $this).show().text(ajax_login_object.loadingmessage); 12956 $.ajax({ 12957 type: 'POST', 12958 dataType: 'json', 12959 url: ajax_login_object.ajaxurl, 12960 data: { 12961 'action': 'ajaxlogin', 12962 'username': $('#username', $this).val(), 12963 'password': $('#password', $this).val(), 12964 'security': $('#security', $this).val() 12965 }, 12966 success: function(data) { 12967 $('p.mk-login-status', $this).text(data.message); 12968 if (data.loggedin === true) { 12969 document.location.href = ajax_login_object.redirecturl; 12970 } 12971 } 12972 }); 12973 e.preventDefault(); 12974 }); 12975 }); 12976} 12977/* Progress Button */ 12978/* -------------------------------------------------------------------- */ 12979var progressButton = { 12980 loader: function(form) { 12981 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 12982 var $form = form, 12983 progressBar = $form.find(".mk-progress-button .mk-progress-inner"), 12984 buttonText = $form.find(".mk-progress-button .mk-progress-button-content"), 12985 progressButton = new TimelineLite(); 12986 progressButton.to(progressBar, 0, { 12987 width: "100%", 12988 scaleX: 0, 12989 scaleY: 1 12990 }).to(buttonText, .3, { 12991 y: -5 12992 }).to(progressBar, 1.5, { 12993 scaleX: 1, 12994 ease: Power2.easeInOut 12995 }, "-=.1").to(buttonText, .3, { 12996 y: 0 12997 }).to(progressBar, .3, { 12998 scaleY: 0 12999 }); 13000 }); 13001 }, 13002 success: function(form) { 13003 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 13004 var $form = form, 13005 buttonText = $form.find(".mk-button .mk-progress-button-content, .mk-contact-button .mk-progress-button-content"), 13006 successIcon = $form.find(".mk-progress-button .state-success"), 13007 progressButtonSuccess = new TimelineLite({ 13008 onComplete: hideSuccessMessage 13009 }); 13010 progressButtonSuccess.to(buttonText, .3, { 13011 paddingRight: 20, 13012 ease: Power2.easeInOut 13013 }, "+=1").to(successIcon, .3, { 13014 opacity: 1 13015 }).to(successIcon, 2, { 13016 opacity: 1 13017 }); 13018 13019 function hideSuccessMessage() { 13020 progressButtonSuccess.reverse() 13021 } 13022 }); 13023 }, 13024 error: function(form) { 13025 MK.core.loadDependencies([MK.core.path.plugins + 'tweenmax.js'], function() { 13026 var $form = form, 13027 buttonText = $form.find(".mk-button .mk-progress-button-content, .mk-contact-button .mk-progress-button-content"), 13028 errorIcon = $form.find(".mk-progress-button .state-error"), 13029 progressButtonError = new TimelineLite({ 13030 onComplete: hideErrorMessage 13031 }); 13032 progressButtonError.to(buttonText, .3, { 13033 paddingRight: 20 13034 }, "+=1").to(errorIcon, .3, { 13035 opacity: 1 13036 }).to(errorIcon, 2, { 13037 opacity: 1 13038 }); 13039 13040 function hideErrorMessage() { 13041 progressButtonError.reverse() 13042 } 13043 }); 13044 } 13045}; 13046 13047function mk_click_events() { 13048 "use strict"; 13049 13050 var eventtype = 'click'; 13051 13052 $(".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) { 13053 if (event.stopPropagation) { 13054 event.stopPropagation(); 13055 } else if (window.event) { 13056 window.event.cancelBubble = true; 13057 } 13058 }); 13059 $("html").on(eventtype, function() {
13060 $(".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); 13061 $('.mk-quick-contact-link').removeClass('quick-contact-active'); 13062 // Removed By Maki for repairing fullnav scroll issue. Hope it odesnt break anything 13063 // $('body').css('overflow', 'visible'); 13064 }); 13065 13066 $('.mk-fullscreen-search-overlay').on(eventtype,function(){ 13067 $(this).removeClass('mk-fullscreen-search-overlay-show'); 13068 }); 13069 13070 $('.mk-forget-password').on(eventtype, function() { 13071 $('.mk-forget-panel').siblings().hide().end().show(); 13072 }); 13073 13074 $('.mk-create-account').on(eventtype, function() { 13075 $('#mk-register-panel').siblings().hide().end().show(); 13076 }); 13077 13078 $('.mk-return-login').on(eventtype, function() { 13079 $('#mk-login-panel').siblings().hide().end().show(); 13080 }); 13081 13082 13083 $('.mk-quick-contact-link').on(eventtype, function() { 13084 var $this = $(this), 13085 $quickContact = $('#mk-quick-contact'); 13086 if (!$this.hasClass('quick-contact-active')) { 13087 $quickContact.addClass('quick-contact-anim').fadeIn(250); 13088 $this.addClass('quick-contact-active'); 13089 } else { 13090 $quickContact.removeClass('quick-contact-anim').fadeOut(100); 13091 $this.removeClass('quick-contact-active'); 13092 } 13093 return false; 13094 }); 13095 13096} 13097 13098function mk_social_share_global() { 13099 13100 "use strict"; 13101 13102 var eventtype = 'click'; 13103 13104 $('.twitter-share').on(eventtype, function() { 13105 var $this = $(this), 13106 $url = $this.attr('data-url'), 13107 $title = $this.attr('data-title'); 13108 13109 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"); 13110 return false; 13111 }); 13112 13113 $('.pinterest-share').on(eventtype, function() { 13114 var $this = $(this), 13115 $url = $this.attr('data-url'), 13116 $title = $this.attr('data-title'), 13117 $image = $this.attr('data-image'); 13118 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"); 13119 return false; 13120 }); 13121 13122 $('.facebook-share').on(eventtype, function() { 13123 var $url = $(this).attr('data-url'); 13124 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"); 13125 return false; 13126 }); 13127 13128 $('.googleplus-share').on(eventtype, function() { 13129 var $url = $(this).attr('data-url'); 13130 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"); 13131 return false; 13132 }); 13133 13134 $('.linkedin-share').on(eventtype, function() { 13135 var $this = $(this), 13136 $url = $this.attr('data-url'), 13137 $title = $this.attr('data-title'), 13138 $desc = $this.attr('data-desc'); 13139 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"); 13140 return false; 13141 }); 13142} 13143 13144 13145/* Event Count Down */ 13146/* -------------------------------------------------------------------- */ 13147 13148function mk_event_countdown() { 13149 if ($.exists('.mk-event-countdown')) { 13150 13151 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.countdown.js' ], function() { 13152 13153 $('.mk-event-countdown').each(function () { 13154 if (isTest) return; 13155 var $this = $(this), 13156 $date = $this.attr('data-date'), 13157 $offset = $this.attr('data-offset'); 13158 13159 $this.downCount({ 13160 date: $date, 13161 offset: $offset 13162 }); 13163 }); 13164 13165 }); 13166 } 13167} 13168/* Flexslider init */ 13169/* -------------------------------------------------------------------- */ 13170 13171function mk_flexslider_init() { 13172 13173 var $lcd = $('.mk-lcd-slideshow'), 13174 $laptop = $('.mk-laptop-slideshow-shortcode'); 13175 13176 if($lcd.length) $lcd.find('.mk-lcd-image').fadeIn(); 13177 if($laptop.length) $laptop.find(".mk-laptop-image").fadeIn(); 13178 13179 $('.js-flexslider').each(function () { 13180 13181 if ($(this).parents('.mk-tabs').length || $(this).parents('.mk-accordion').length) { 13182 $(this).removeData("flexslider"); 13183 } 13184 13185 var $this = $(this), 13186 $selector = $this.attr('data-selector'), 13187 $animation = $this.attr('data-animation'), 13188 $easing = $this.attr('data-easing'), 13189 $direction = $this.attr('data-direction'), 13190 $smoothHeight = $this.attr('data-smoothHeight') == "true" ? true : false,
13191 $slideshowSpeed = $this.attr('data-slideshowSpeed'), 13192 $animationSpeed = $this.attr('data-animationSpeed'), 13193 $controlNav = $this.attr('data-controlNav') == "true" ? true : false, 13194 $directionNav = $this.attr('data-directionNav') == "true" ? true : false, 13195 $pauseOnHover = $this.attr('data-pauseOnHover') == "true" ? true : false, 13196 $isCarousel = $this.attr('data-isCarousel') == "true" ? true : false; 13197 13198 13199 if ($selector !== undefined) { 13200 var $selector_class = $selector; 13201 } else { 13202 var $selector_class = ".mk-flex-slides > li"; 13203 } 13204 13205 if ($isCarousel === true) { 13206 var $itemWidth = parseInt($this.attr('data-itemWidth')), 13207 $itemMargin = parseInt($this.attr('data-itemMargin')), 13208 $minItems = parseInt($this.attr('data-minItems')), 13209 $maxItems = parseInt($this.attr('data-maxItems')), 13210 $move = parseInt($this.attr('data-move')); 13211 } else { 13212 var $itemWidth = $itemMargin = $minItems = $maxItems = $move = 0; 13213 } 13214 13215 MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.flexslider.js' ], function() { 13216 $this.flexslider({ 13217 selector: $selector_class, 13218 animation: $animation, 13219 easing: $easing, 13220 direction: $direction, 13221 smoothHeight: $smoothHeight, 13222 slideshow: !isTest, // autoplay 13223 slideshowSpeed: $slideshowSpeed, 13224 animationSpeed: $animationSpeed, 13225 controlNav: $controlNav, 13226 directionNav: $directionNav, 13227 pauseOnHover: $pauseOnHover, 13228 prevText: "", 13229 nextText: "", 13230 itemWidth: $itemWidth, 13231 itemMargin: $itemMargin, 13232 minItems: $minItems, 13233 maxItems: $maxItems, 13234 move: $move 13235 }); 13236 }); 13237 13238 }); 13239 13240} 13241 13242/* Header Search Form */ 13243/* -------------------------------------------------------------------- */ 13244 13245function mk_header_searchform() { 13246 13247 $('.mk-search-trigger').on('click',function(){ 13248 setTimeout(function() { 13249 $('#mk-ajax-search-input').focus(); 13250 },500); 13251 }); 13252 13253 "use strict"; 13254 13255 $('.mk-header-toolbar .mk-header-searchform .text-input').on('focus', function () { 13256 13257 if ($('.mk-header-toolbar .mk-header-searchform .text-input').hasClass('on-close-state')) { 13258 $('.mk-header-toolbar .mk-header-searchform .text-input').removeClass('on-close-state').animate({ 13259 'width': '200px' 13260 }, 200); 13261 return false; 13262 } 13263 }); 13264 13265 $(".mk-header-toolbar .mk-header-searchform").on('click',function (event) { 13266 if (event.stopPropagation) { 13267 event.stopPropagation(); 13268 } else if (window.event) { 13269 window.event.cancelBubble = true; 13270 } 13271 }); 13272 13273 13274 $("html").on('click',function () { 13275 $(this).find(".mk-header-toolbar .mk-header-searchform .text-input").addClass('on-close-state').animate({ 13276 'width': 90 13277 }, 300); 13278 }); 13279 13280 // Fix search for edge browser 13281 if(MK.utils.browser.name === 'Edge') { 13282 $('#mk-fullscreen-search-input').on('keydown', function(e) { 13283 if (e.which == 13) { 13284 e.preventDefault(); 13285 $('#mk-fullscreen-searchform').submit(); 13286 } 13287 }); 13288 } 13289 13290} 13291 13292 13293 13294 13295/* Hover Events */ 13296/* -------------------------------------------------------------------- */ 13297 13298function mk_hover_events() { 13299 13300 "use strict"; 13301 13302 $('.shopping-cart-header').hover( 13303 function() { 13304 $(this).find('.mk-shopping-cart-box').stop(true, true).fadeIn(250); 13305 }, 13306 function() { 13307 $(this).find('.mk-shopping-cart-box').stop(true, true).delay(500).fadeOut(250); 13308 } 13309 ); 13310 13311 13312 $('.widget-sub-navigation > ul > li, .widget_nav_menu ul.menu > li, .widget_product_categories ul > .cat-item').each(function() { 13313 13314 var $this = $(this), 13315 $subLevel = $this.find('ul').first(); 13316 13317 if ($this.hasClass('page_item_has_children') || $this.hasClass('menu-item-has-children') || $this.hasClass('cat-parent')) { 13318 $this.on('click', function() { 13319 if($this.hasClass('toggle-active')) { 13320 $subLevel.stop(true, true).slideUp(700); 13321 $this.removeClass('toggle-active'); 13322 } else { 13323 $subLevel.stop(true, true).slideDown(700); 13324 $this.addClass('toggle-active'); 13325 } 13326 }); 13327 13328 $subLevel.on('click', function(e){ 13329 e.stopPropagation(); 13330 }); 13331 } 13332 13333 }); 13334 13335 // var eventtype = mobilecheck() ? 'touchstart' : 'click'; 13336 var eventtype = 'click'; 13337 13338 $('.mk-fullscreen-trigger').on(eventtype, function(e) { 13339 $('.mk-fullscreen-search-overlay').addClass('mk-fullscreen-search-overlay-show'); 13340 setTimeout(function(){ 13341 $("#mk-fullscreen-search-input").focus(); 13342 }, 300); 13343 e.preventDefault(); 13344 }); 13345 13346 $('.mk-fullscreen-close').on(eventtype, function(e) { 13347 $('.mk-fullscreen-search-overlay').removeClass('mk-fullscreen-search-overlay-show'); 13348 e.preventDefault(); 13349 }); 13350 13351} 13352 13353function mk_unfold_footer() { 13354 var $this = $('#mk-footer'), 13355 $spacer = $('#mk-footer-unfold-spacer'), 13356 $footerHeight = $this.outerHeight(); 13357 13358 // Stick with CSS media query breakpoint to target exact screen width 13359 if( !window.matchMedia("(max-width: 767px)").matches ) { 13360 if ($this.hasClass('mk-footer-unfold')) { 13361 $spacer.css('height', $footerHeight); 13362 } 13363 } else { 13364 $spacer.css('height', 0); 13365 } 13366} 13367 13368/* jQuery fancybox lightbox */ 13369/* -------------------------------------------------------------------- */ 13370function mk_lightbox_init() { 13371 13372 $(".mk-lightbox").fancybox({ 13373 loop: true, 13374 }); 13375 13376 $.fancybox.defaults.hash = false;
13377 13378} 13379 13380 13381/* Milestone Number Shortcode */ 13382/* -------------------------------------------------------------------- */ 13383 13384function mk_milestone() { 13385 13386 "use strict"; 13387 13388 if( isTest || !$.exists('.mk-milestone') ) return; 13389 13390 $('.mk-milestone').each(function () { 13391 var $this = $(this), 13392 stop_number = $this.find('.milestone-number').attr('data-stop'), 13393 animation_speed = parseInt($this.find('.milestone-number').attr('data-speed')); 13394 13395 var build = function() { 13396 if (!$this.hasClass('scroll-animated')) { 13397 $this.addClass('scroll-animated'); 13398 13399 $({ 13400 countNum: $this.find('.milestone-number').text() 13401 }).animate({ 13402 countNum: stop_number 13403 }, { 13404 duration: animation_speed, 13405 easing: 'linear', 13406 step: function () { 13407 $this.find('.milestone-number').text(Math.floor(this.countNum)); 13408 }, 13409 complete: function () { 13410 $this.find('.milestone-number').text(this.countNum); 13411 } 13412 }); 13413 } 13414 }; 13415 13416 if ( !MK.utils.isMobile() ) { 13417 // refactored only :in-viewport logic. rest is to-do 13418 MK.utils.scrollSpy( this, { 13419 position: 'bottom', 13420 after: build 13421 }); 13422 } else { 13423 build(); 13424 } 13425 13426 }); 13427 13428} 13429 13430 13431 13432 13433/* Recent Works Widget */ 13434/* -------------------------------------------------------------------- */ 13435 13436function mk_portfolio_widget() { 13437 13438 "use strict"; 13439 13440 $('.widget_recent_portfolio li').each(function () { 13441 13442 $(this).find('.portfolio-widget-thumb').on('hover',function () { 13443 13444 $(this).siblings('.portfolio-widget-info').animate({ 13445 'opacity': 1 13446 }, 200); 13447 }, function () { 13448 13449 $(this).siblings('.portfolio-widget-info').animate({ 13450 'opacity': 0 13451 }, 200); 13452 }); 13453 13454 }); 13455} 13456 13457 13458 13459/* Skill Meter and Charts */ 13460/* -------------------------------------------------------------------- */ 13461function mk_skill_meter() { 13462 "use strict"; 13463 if ($.exists('.mk-skill-meter')) { 13464 if (!MK.utils.isMobile()) { 13465 $(".mk-skill-meter .progress-outer").each(function() { 13466 var $this = $(this); 13467 13468 var build = function() { 13469 if (!$this.hasClass('scroll-animated')) { 13470 $this.addClass('scroll-animated'); 13471 $this.animate({ 13472 width: $this.attr("data-width") + '%' 13473 }, 2000); 13474 } 13475 }; 13476 13477 MK.utils.scrollSpy( this, { 13478 position: 'bottom', 13479 after: build 13480 }); 13481 }); 13482 } else { 13483 $(".mk-skill-meter .progress-outer").each(function() { 13484 var $this = $(this); 13485 if (!$this.hasClass('scroll-animated')) { 13486 $this.addClass('scroll-animated'); 13487 $this.css({ 13488 width: $(this).attr("data-width") + '%' 13489 }); 13490 } 13491 }); 13492 } 13493 } 13494} 13495 13496// function mk_charts() { 13497// "use strict"; 13498 13499// if( !$.exists('.mk-chart') ) return; 13500 13501// MK.core.loadDependencies([ MK.core.path.plugins + 'jquery.easyPieChart.js' ], function() { 13502 13503// $('.mk-chart').each(function() { 13504 13505// var $this = $(this), 13506// $parent_width = $(this).parent().width(), 13507// $chart_size = parseInt($this.attr('data-barSize')); 13508 13509// if ($parent_width < $chart_size) { 13510// $chart_size = $parent_width; 13511// $this.css('line-height', $chart_size); 13512// $this.find('i').css({ 13513// 'line-height': $chart_size + 'px' 13514// }); 13515// $this.css({ 13516// 'line-height': $chart_size + 'px' 13517// }); 13518// } 13519 13520// var build = function() { 13521// $this.easyPieChart({ 13522// animate: 1300, 13523// lineCap: 'butt', 13524// lineWidth: $this.attr('data-lineWidth'), 13525// size: $chart_size, 13526// barColor: $this.attr('data-barColor'), 13527// trackColor: $this.attr('data-trackColor'), 13528// scaleColor: 'transparent', 13529// onStep: function(value) {
13530// this.$el.find('.chart-percent span').text(Math.ceil(value)); 13531// } 13532// }); 13533// }; 13534 13535// // refactored only :in-viewport logic. rest is to-do 13536// MK.utils.scrollSpy( this, { 13537// position: 'bottom', 13538// after: build 13539// }); 13540 13541 13542// }); 13543// }); 13544// } 13545/* Toolbar subscribe form */ 13546/* -------------------------------------------------------------------- */ 13547 13548$( "#mc-embedded-subscribe-form" ).on('submit',function( e ){ 13549 var $this = $(this); 13550 13551 e.preventDefault(); 13552 $.ajax({ 13553 url: MK.core.path.ajaxUrl, 13554 type: "POST", 13555 data: { 13556 action: "mk_ajax_subscribe", 13557 email: $this.find( ".mk-subscribe--email" ).val(), 13558 list_id: $this.find( ".mk-subscribe--list-id" ).val(), 13559 optin: $this.find( ".mk-subscribe--optin" ).val() 13560 }, 13561 success: function ( res ) { 13562 $this.parent().find( ".mk-subscribe--message" ).html( $.parseJSON( res ).message ); 13563 } 13564 }); 13565}); 13566 13567/** 13568 * Add class to tag. It's vanilla js instead of jQuery. 13569 * @param tag 13570 * @param className 13571 */ 13572function addClass(tag, className) { 13573 tag.className += ' ' + className; 13574} 13575 13576/** 13577 * Remove class from tag. It's vanilla js instead of jQuery. 13578 * Replacing should be with g for replacing all occurrence. 13579 * 13580 * @param tag 13581 * @param className 13582 */ 13583function removeClass(tag, className) { 13584 tag.className = tag.className.replace(new RegExp(className, 'g'), ''); 13585} 13586 13587 13588/** 13589 * If we're running under Node for testing purpose. 13590 */ 13591if(typeof exports !== 'undefined') { 13592 exports.addClass = addClass; 13593 exports.removeClass = removeClass; 13594} 13595 13596/*! 13597* javascript-debounce 1.0.0 13598* 13599* A lightweight, dependency-free JavaScript module for debouncing functions based on David Walsh's debounce function. 13600* 13601* Source code available at: https://github.com/jgarber623/javascript-debounce 13602* 13603* (c) 2015-present Jason Garber (http://sixtwothree.org) 13604* 13605* javascript-debounce may be freely distributed under the MIT license. 13606*/ 13607 13608(function(root, factory) { 13609 if (typeof define === "function" && define.amd) { 13610 define([], factory); 13611 } else if (typeof exports === "object") { 13612 module.exports = factory(); 13613 } else { 13614 root.debounce = factory(); 13615 } 13616})(this, function() { 13617 "use strict"; 13618 return function(callback, delay) { 13619 var timeout; 13620 return function() { 13621 var context = this, args = arguments; 13622 clearTimeout(timeout); 13623 timeout = setTimeout(function() { 13624 callback.apply(context, args); 13625 }, delay); 13626 }; 13627 }; 13628}); 13629 13630/** 13631 * Validate email address. 13632 * 13633 * @param input 13634 * @param invalidClassName 13635 * @returns boolean 13636 */ 13637function validateEmail(input, invalidClassName) { 13638 var value = input.value.trim(); 13639 if ((input.required || value.length > 0) && !/^([a-z0-9_\.\-\+]+)@([\da-z\.\-]+)\.([a-z\.]{2,63})$/i.test(value)) { 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 * Validate text entry. 13653 * 13654 * @param input 13655 * @param invalidClassName 13656 * @returns boolean 13657 */ 13658function validateText(input, invalidClassName) { 13659 var value = input.value.trim(); 13660 if (input.required && value.length === 0) { 13661 if (invalidClassName) { 13662 addClass(input, invalidClassName); 13663 } 13664 return false; 13665 } else { 13666 if (invalidClassName) { 13667 removeClass(input, invalidClassName); 13668 } 13669 return true; 13670 } 13671} 13672/** 13673 * Validate Checkbox. 13674 * 13675 * @param input 13676 * @param invalidClassName 13677 * @returns boolean 13678 */ 13679function validateCheckBox(input, invalidClassName) { 13680 if (input.required && input.checked == false) { 13681 if (invalidClassName) { 13682 addClass(input, invalidClassName); 13683 } 13684 return false; 13685 } else { 13686 if (invalidClassName) { 13687 removeClass(input, invalidClassName); 13688 } 13689 return true; 13690 } 13691} 13692/** 13693 * If we're running under Node for testing purpose. 13694 */ 13695if (typeof exports !== 'undefined') { 13696 exports.validateEmail = validateEmail; 13697 exports.validateText = validateText; 13698} 13699(function( $ ) { 13700 'use strict'; 13701 13702 var $wrapper = $('.js-bottom-corner-btns'); 13703 var $contactBtn = $wrapper.find('.js-bottom-corner-btn--contact'); 13704 var $backBtn = $wrapper.find('.js-bottom-corner-btn--back'); 13705 var hasContactBtn = $contactBtn.length; 13706 var hasBackBtn = $backBtn.length; 13707 13708 if(!hasBackBtn) return; 13709 13710 function deactivate() { 13711 $contactBtn.removeClass('is-active'); 13712 $backBtn.removeClass('is-active'); 13713 } 13714 13715 function activate() { 13716 $contactBtn.addClass('is-active'); 13717 $backBtn.addClass('is-active'); 13718 } 13719 13720 MK.utils.scrollSpy( 400, {
13721 before: deactivate, 13722 after: activate 13723 }); 13724 13725})( jQuery ); 13726(function($) { 13727 'use strict'; 13728 13729 $('.mk-fullscreen-nav-close, .mk-fullscreen-nav-wrapper, #fullscreen-navigation a').on('click', function(e) { 13730 13731 // Close nav with removing classes 13732 $('.mk-fullscreen-nav').removeClass('opened'); 13733 $('.mk-dashboard-trigger').removeClass('fullscreen-active'); 13734 $('body').removeClass('fullscreen-nav-opened'); 13735 13736 var anchor = MK.utils.detectAnchor( this ), 13737 $this = $( this ); 13738 13739 // Scroll to anchor if exists 13740 if( anchor.length ) { 13741 // Moved 4 these lines after anchor check because console error. If anchor 13742 // return false, split part will throw an error because $this is not a link. 13743 // Fix AM-2027 13744 var href = $this.attr( 'href' ).split( '#' )[0]; 13745 var url = window.location.href; 13746 var isSamePage = url.indexOf( href ) !== -1; 13747 13748 if ( isSamePage ) { 13749 e.preventDefault(); 13750 } 13751 MK.utils.scrollToAnchor( anchor ); 13752 13753 // Or do nothing if pointless # as href 13754 // BAD PRACTICE: it is very popular to use "#" for click elements that we listen to with js. 13755 // GOOD PRACTICE: prefer "javascript:;" as easier to handle and more readable version 13756 } else if( $this.attr( 'href' ) === '#' ) { 13757 e.preventDefault(); 13758 } 13759 }); 13760 13761 $('.fullscreen-navigation-ul .menu-sub-level-arrow').on('click', function(){ 13762 $(this).siblings('.sub-menu').slideToggle(); 13763 }); 13764 13765}(jQuery)); 13766(function($) { 13767 'use strict'; 13768 13769 /** 13770 * Mega menu 13771 */ 13772 var $navList = $(".main-navigation-ul"); 13773 var megaMenu = function megaMenu() { 13774 13775 $navList.MegaMenu({ 13776 type: "vertical", 13777 delay: 200 13778 }); 13779 }; 13780 13781 $(window).on('load', megaMenu); 13782 13783}(jQuery)); 13784(function($) { 13785 'use strict'; 13786 13787 /** 13788 * One pager menu hash update. 13789 * Smooth scroll is appended globally whenever the click element has corresponding #el 13790 */ 13791 var onePageNavItem = function onePageNavItem() { 13792 var $this = $( this ), 13793 link = $this.find( 'a' ), 13794 anchor = MK.utils.detectAnchor( link ); // anchor on current page 13795 13796 if( !anchor.length ) return; 13797 13798 $this.removeClass( 'current-menu-item current-menu-ancestor current-menu-parent' ); 13799 13800 var activeNav = function( state ) { 13801 return function() { 13802 $this[ state ? 'addClass' : 'removeClass' ]( 'current-menu-item' ); 13803 window.history.replaceState( undefined, undefined, [ state ? anchor : ' ' ] ); 13804 }; 13805 }; 13806 13807 MK.utils.scrollSpy( $( anchor )[0], { 13808 before : activeNav( false ), 13809 active : activeNav( true ), 13810 after : activeNav( false ), 13811 }); 13812 }; 13813 13814 var $navItems = $('.js-main-nav').find( 'li' ); 13815 13816 $(window).on('load', function() { 13817 // Wait with spying anchors so we do not assign anchor that is browser scroll to after page load 13818 // Especially when there are anchors on top - refreshing page can cause grabbing one of them into url 13819 // and force unwanted scroll 13820 setTimeout(function() { 13821 $navItems.each( onePageNavItem ); 13822 }, 1000); 13823 }); 13824 13825}(jQuery)); 13826(function($) { 13827 'use strict'; 13828 13829 var $window = $(window); 13830 var $body = $('body'); 13831 var $resMenuWrap = $('.mk-responsive-wrap'); 13832 var $post_nav = $('.mk-post-nav'); 13833 var $toolbar = $('.mk-header-toolbar'); 13834 var $resMenuLink = $('.mk-nav-responsive-link'); 13835 13836 // Flags 13837 var hasResMenu = ($resMenuWrap.length > 0); 13838 13839 //initial window and screen height 13840 var windowHeight = $window.height(); 13841 var screenHeight = screen.height; 13842 13843 // We keep this handler above hasResMenu flag. 13844 // Even if our header doesn't contain droppable responsive menu (in favor of fullscreen or side menu) 13845 // we still transform tollbar into collapsible menu part in responsive state 13846 $('.mk-toolbar-resposnive-icon').on('click', function(e) { 13847 e.preventDefault(); 13848 console.log('clicked'); 13849 if ($body.hasClass('toolbar-opened')) { 13850 $body.removeClass('toolbar-opened').addClass('toolbar-closed'); 13851 $toolbar.hide(); 13852 } else { 13853 $body.removeClass('toolbar-closed').addClass('toolbar-opened'); 13854 $toolbar.show(); 13855 } 13856 }); 13857 13858 13859 if ( ! hasResMenu && ! $('.vc_mk_header') ) return; 13860 13861 function toggleResMenu(e) { 13862 e.preventDefault(); 13863 var $this = $(this); 13864 var $headerInner = $this.parents('header'); 13865 var $resMenu = $headerInner.find('.mk-responsive-wrap'); 13866 var searchBox = $('.responsive-searchform .text-input'); 13867 var adminBarHeight = $('#wpadminbar').height(); /* Fix AM-1918 */ 13868 13869 if ($body.hasClass('mk-opened-nav')) { 13870 $this.removeClass('is-active'); 13871 $body.removeClass('mk-opened-nav').addClass('mk-closed-nav').trigger('mk-closed-nav'); 13872 $resMenu.hide(); 13873 $post_nav.removeClass('post-nav-backward'); 13874 } else { 13875 $this.addClass('is-active'); 13876 $body.removeClass('mk-closed-nav').addClass('mk-opened-nav').trigger('mk-opened-nav'); 13877 $resMenu.show(); 13878 $post_nav.addClass('post-nav-backward'); 13879 13880 } 13881 13882 //for iPhone 5 focus bug , remove search box focused class 13883 if(searchBox.hasClass('input-focused')){ 13884 searchBox.removeClass('input-focused'); 13885 } 13886 13887 } 13888 13889 $resMenuLink.each(function() { 13890 $(this).on('click', toggleResMenu); 13891 }); 13892 13893 $( window ).on( 'vc_reload', function () { 13894 $('.vc_mk_header .mk-nav-responsive-link').each(function() { 13895 $(this).on('click', toggleResMenu); 13896 }); 13897 } ) 13898 13899 var setResMenuHeight = function() { 13900 var height = $window.height() - MK.val.offsetHeaderHeight(0); 13901 $resMenuWrap.css('max-height', height); 13902 }; 13903 13904 // check if device virtual keyboard is active 13905 var isVirtualKeyboard = function() { 13906 var currentWindowHeight = $window.height(); 13907 var currentScreenHeight = screen.height; 13908 var searchBox = $('.responsive-searchform .text-input'); 13909 var searchBoxIsFocused = false; 13910 13911 //for iPhone 5 focus bug , add class for detect focus state 13912 searchBox.on('touchstart touchend', function(e) { 13913 searchBox.addClass('input-focused'); 13914 }); 13915 13916 searchBoxIsFocused = (searchBox.is(':focus') || searchBox.hasClass("input-focused")); 13917 13918 if ($body.hasClass('mk-opened-nav') && searchBoxIsFocused && currentScreenHeight == screenHeight && currentWindowHeight != windowHeight) { 13919 return true; 13920 } else { 13921 return false;
13922 } 13923 }; 13924 13925 var hideResMenu = function hideResMenu() { 13926 if (MK.utils.isResponsiveMenuState()) { 13927 13928 //when search box in responsive menu is focused , window resize fired but at this time responsive menu should be open 13929 if (!isVirtualKeyboard()) { 13930 // hide toggled menu and its states 13931 if ($body.hasClass('mk-opened-nav')) { 13932 $resMenuLink.filter('.is-active').trigger('click'); 13933 } 13934 // hide menu wrapper 13935 $resMenuWrap.hide(); 13936 13937 } 13938 } 13939 }; 13940 13941 $resMenuWrap.on('click', 'a', hideResMenu); 13942 13943}(jQuery)); 13944 13945(function($) { 13946 'use strict'; 13947 13948 var $header = $('.mk-header'); 13949 var hasHeader = ($header.length > 0); 13950 13951 if(!hasHeader && ! $('.vc_mk_header') ) return; 13952 13953 var $sticky_style = $header.attr('data-header-style'); 13954 13955 //if ($sticky_style !== 3) return; 13956 13957 $('.sidedash-navigation-ul > li').each(function() { 13958 var $this = $(this); 13959 13960 $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>'); 13961 }); 13962 13963 function toggleSubMenu(e) { 13964 e.preventDefault(); 13965 var $this = $(this); 13966 13967 if ($this.hasClass('mk-nav-sub-closed')) { 13968 $this.siblings('ul').slideDown(450).end().removeClass('mk-nav-sub-closed').addClass('mk-nav-sub-opened'); 13969 } else { 13970 $this.siblings('ul').slideUp(450).end().removeClass('mk-nav-sub-opened').addClass('mk-nav-sub-closed'); 13971 } 13972 }; 13973 13974 function toggleFullMenu(e) { 13975 e.preventDefault(); 13976 var $this = $(this), 13977 $body = $('body'), 13978 $fullscreen_box = $('.mk-fullscreen-nav'); 13979 13980 if ($this.hasClass('dashboard-style')){ 13981 if (!$this.hasClass('dashboard-active')) { 13982 $this.addClass('dashboard-active'); 13983 $body.addClass('dashboard-opened'); 13984 } else { 13985 $this.removeClass('dashboard-active'); 13986 $body.removeClass('dashboard-opened'); 13987 } 13988 }else if($this.hasClass('fullscreen-style')){ 13989 if (!$this.hasClass('fullscreen-active')) { 13990 $this.addClass('fullscreen-active'); 13991 $body.addClass('fullscreen-nav-opened'); 13992 $fullscreen_box.addClass('opened'); 13993 // MK.utils.scroll.disable(); 13994 } else { 13995 $this.removeClass('fullscreen-active'); 13996 $body.removeClass('fullscreen-nav-opened'); 13997 $fullscreen_box.removeClass('opened'); 13998 } 13999 } 14000 } 14001 14002 $('.mk-nav-arrow').each(function() { 14003 $(this).stop(true).on('click', toggleSubMenu); 14004 }); 14005 14006 $('.mk-dashboard-trigger').each(function() { 14007 $(this).on('click', toggleFullMenu); 14008 }); 14009 14010 $( window ).on( 'vc_reload', function () { 14011 $('.vc_mk_header .mk-dashboard-trigger').each(function() { 14012 $(this).on('click', toggleFullMenu); 14013 }); 14014 14015 $('.vc_mk_header .mk-nav-arrow').each(function() { 14016 $(this).stop(true).on('click', toggleSubMenu); 14017 }); 14018 } ); 14019 14020 $('html').on('click', function() { 14021 $('body').removeClass('dashboard-opened'); 14022 $('.mk-dashboard-trigger').removeClass('dashboard-active'); 14023 }); 14024 14025}(jQuery)); 14026 14027(function($) { 14028 'use strict'; 14029 14030 /** 14031 * Vertical menu 14032 */ 14033 var $verticalMenu = $('#mk-vm-menu'); 14034 var verticalMenu = function verticalMenu() { 14035 if(!$verticalMenu.data('vertical-menu') && !MK.utils.isResponsiveMenuState()) { 14036 $verticalMenu.dlmenu(); 14037 $verticalMenu.data('vertical-menu', true); 14038 } 14039 }; 14040 14041 verticalMenu(); 14042 $(window).on('resize', verticalMenu); 14043 14044}(jQuery)); 14045(function($) { 14046 'use strict'; 14047 14048 // WPML 14049 var $lang_item = $('.mk-main-navigation > .main-navigation-ul > .menu-item-language'); 14050 $lang_item.addClass('no-mega-menu').css('visibility', 'visible'); 14051 $('.mk-main-navigation .menu-item-language > a').addClass('menu-item-link'); 14052 14053})(jQuery); 14054(function($) { 14055 'use strict'; 14056 14057 var $header = $( '.mk-header' ).first(); // Theme header is always first, do not apply to shortcode headers. 14058 var hasHeader = ($header.length > 0); 14059 14060 if(!hasHeader) return; 14061 14062 var $window = $( window ); 14063 var $document = $( document ); 14064 var $headerHolder = $header.find( '.mk-header-holder' ); 14065 var $paddingWrapper = $header.find('.mk-header-padding-wrapper'); 14066 var config = $header.data(); 14067 14068 /** 14069 * Flags 14070 * @type {Boolean} 14071 */ 14072 var isStickyLazy = (config.stickyStyle === 'lazy'); 14073 var isStickyFixed = (config.stickyStyle === 'fixed'); 14074 var isStickySlide = (config.stickyStyle === 'slide'); 14075 14076 function isSticky() { // Check for sticky compatibility 14077 //return config.headerStyle !== 4; header style 4 needs sticky state for transparent header. 14078 return true; 14079 } 14080
14081 function isColorable() { // Check for coloring compatibility 14082 // TODO make it explicit in DOM that header is transparent as this is only scenario for colorable header 14083 // Monkey patch - exclude header styles that don't fit colorable 14084 return config.headerStyle !== 4; 14085 } 14086 14087 14088 /** 14089 * Change header skin 14090 */ 14091 function changeSkin( e, skin ) { 14092 $header.attr( 'data-transparent-skin', skin ); 14093 // Fix for class based skining 14094 var contrast = skin === 'light' ? 'dark' : 'light'; 14095 $header.addClass(skin + '-skin'); 14096 $header.removeClass(contrast + '-skin'); 14097 } 14098 14099 if(isColorable()) MK.utils.eventManager.subscribe( 'firstElSkinChange', changeSkin ); 14100 14101 14102 // Assign sticky scenarions 14103 if (isSticky() && isStickyLazy) { 14104 if(config.headerStyle !== 2) { 14105 lazySticky(); 14106 } 14107 } 14108 else if (isSticky() && isStickyFixed) fixedSticky(); 14109 else if (isSticky() && isStickySlide) slideSticky(); 14110 14111 /** 14112 * Sticky header behavior: Lazy 14113 */ 14114 function lazySticky() { 14115 var elClassHidden = 'header--hidden'; 14116 var elClassSticky = 'a-sticky'; 14117 var wScrollCurrent = 0; 14118 var wScrollBefore = 0; 14119 var wScrollDiff = 0; 14120 var wHeight = 0; 14121 var dHeight = 0; 14122 14123 var setSizes = function setSizes() { 14124 dHeight = $document.height(); 14125 wHeight = $window.height(); 14126 }; 14127 14128 var onScroll = function onScroll() { 14129 wScrollCurrent = MK.val.scroll(); 14130 wScrollDiff = wScrollBefore - wScrollCurrent; 14131 14132 if( wScrollCurrent <= 0 ) { // scrolled to the very top; element sticks to the top 14133 $headerHolder.removeClass( elClassHidden ); 14134 $header.removeClass( elClassSticky ); 14135 $('body').trigger('mk:header-unsticky'); 14136 14137 } else if( wScrollDiff > 0 && $headerHolder.hasClass( elClassHidden ) ) { // scrolled up; element slides in 14138 $headerHolder.removeClass( elClassHidden ); 14139 $header.addClass( elClassSticky ); 14140 $('body').trigger('mk:header-sticky'); 14141 14142 } else if( wScrollDiff < 0 ) { // scrolled down 14143 14144 if( wScrollCurrent + wHeight >= dHeight && $headerHolder.hasClass( elClassHidden ) ) { // scrolled to the very bottom; element slides in 14145 $headerHolder.removeClass( elClassHidden ); 14146 $header.addClass( elClassSticky ); 14147 $('body').trigger('mk:header-sticky'); 14148 } else { // scrolled down; element slides out 14149 $headerHolder.addClass( elClassHidden ); 14150 $header.removeClass( elClassSticky ); 14151 $('body').trigger('mk:header-unsticky'); 14152 } 14153 } 14154 14155 wScrollBefore = wScrollCurrent; 14156 }; 14157 14158 setSizes(); 14159 onScroll(); 14160 $window.on( 'resize', MK.utils.throttle( 100, setSizes ) ); 14161 $window.on( 'scroll', MK.utils.throttle( 500, onScroll ) ); 14162 } 14163 14164 14165 /** 14166 * Sticky header behavior: Fixed 14167 */ 14168 function fixedSticky() { 14169 var sticked = false; 14170 var scrollPos; 14171 14172 var toggleState = function toggleState() { 14173 scrollPos = MK.val.scroll() + MK.val.adminbarHeight(); 14174 14175 if( (scrollPos > MK.val.stickyOffset() ) && ! MK.utils.isResponsiveMenuState() ) { 14176 if(sticked) return; // stop if already sticked 14177 $header.addClass('a-sticky'); 14178 sticked = true; 14179 $('body').trigger('mk:header-sticky'); 14180 } else { 14181 if(!sticked) return; // stop if already unsticked 14182 $header.removeClass('a-sticky'); 14183 sticked = false; 14184 $('body').trigger('mk:header-unsticky'); 14185 } 14186 }; 14187 14188 toggleState(); 14189 $window.on( 'scroll', MK.utils.throttle( 100, toggleState) ); 14190 $window.on( 'resize', MK.utils.throttle( 100, toggleState) ); 14191 } 14192 14193 14194 /** 14195 * Sticky header behavior: Slide 14196 */ 14197 function slideSticky() { 14198 var sticked = false;
14199 var onScroll = function onScroll() { 14200 if (MK.val.scroll() > MK.val.stickyOffset()) { 14201 if(sticked) return; // stop if already sticked 14202 $header.addClass('pre-sticky'); 14203 $paddingWrapper.addClass('enable-padding'); 14204 setTimeout(function() { 14205 $header.addClass('a-sticky'); 14206 $('body').trigger('mk:header-sticky'); 14207 }, 1); 14208 sticked = true; 14209 } else { 14210 if(!sticked) return; // stop if already unsticked 14211 $header.removeClass('a-sticky'); 14212 $header.removeClass('pre-sticky'); 14213 $paddingWrapper.removeClass('enable-padding'); 14214 sticked = false; 14215 $('body').trigger('mk:header-unsticky'); 14216 } 14217 }; 14218 14219 onScroll(); 14220 $window.on( 'scroll', MK.utils.throttle( 100, onScroll) ); 14221 } 14222 14223 // TODO 14224 // var pageIntro = $('body').data('intro'); // block scrolling on page intro 14225 14226 14227})( jQuery ); 14228 14229(function($){ 14230 'use strict'; 14231 14232 // Do not assign at all if no touch events 14233 var hasTouchscreen = ('ontouchstart' in document.documentElement); 14234 if(!hasTouchscreen) return; 14235 14236 $('.mk-main-navigation .menu-item-has-children').each( normalizeClick ); 14237 14238 function normalizeClick() { 14239 $(this).on('click', handleClick); 14240 } 14241 14242 // 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. 14243 // 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 14244 function handleClick(e) { 14245 var $this = $(e.currentTarget); 14246 var $child = $this.find('> ul'); 14247 var isVisible = $child.css('display') !== 'none'; 14248 14249 if(!isVisible) { 14250 e.preventDefault(); 14251 e.stopPropagation(); 14252 } 14253 } 14254 14255}(jQuery)); 14256(function( $ ) { 14257 'use strict'; 14258 14259 MK.ui.preloader = { 14260 dom : { 14261 overlay: '.mk-body-loader-overlay' 14262 }, 14263 14264 hide : function hide() { 14265 $( this.dom.overlay ).fadeOut(600, "easeInOutExpo", function() { 14266 $('body').removeClass('loading'); 14267 //$( this ).remove(); 14268 }); 14269 } 14270 }; 14271 14272})( jQuery ); 14273(function($) { 14274 'use strict'; 14275 14276 var _ajaxUrl = MK.core.path.ajaxUrl; 14277 var _instances = {}; 14278 14279 MK.utils.ajaxLoader = function ajaxLoader(el) { 14280 // retrun a cached instance to have control over state from within multiple places 14281 // we may need for example to reset pageId when do filtering. It is really one instance that controls both filtering and pagination / load more 14282 var id = '#' + ($(el).attr('id')); 14283 if(typeof _instances[id] !== 'undefined') return _instances[id]; 14284 14285 // else lets start new instance 14286 this.id = id; 14287 this.el = el; 14288 this.isLoading = false; 14289 this.xhrCounter = 0; 14290 }; 14291 14292 MK.utils.ajaxLoader.prototype = { 14293 init: function init() { 14294 // prevent double initialization of we return an instance 14295 if ( this.initialized && typeof window.vc_iframe === 'undefined' ) { 14296 return; 14297 } 14298 14299 this.createInstance(); 14300 this.cacheElements(); 14301 14302 this.initialized = true; 14303 }, 14304 14305 cacheElements: function cacheElements() { 14306 this.$container = $(this.el); 14307 this.id = '#' + (this.$container.attr('id')); 14308 this.categories = this.$container.data('loop-categories'); 14309 14310 this.data = {}; 14311 this.data.action = 'mk_load_more'; 14312 this.data.query = this.$container.data('query'); 14313 this.data.atts = this.$container.data('loop-atts'); 14314 this.data.loop_iterator = this.$container.data('loop-iterator'); 14315 this.data.author = this.$container.data('loop-author'); 14316 this.data.posts = this.$container.data('loop-posts'); 14317 this.data.safe_load_more = this.$container.siblings('#safe_load_more').val(); 14318 this.data._wp_http_referer = this.$container.siblings('input[name="_wp_http_referer"]').val(); 14319 this.data.paged = 1; 14320 this.data.maxPages = this.$container.data('max-pages'); 14321 this.data.term = this.categories; 14322 }, 14323 14324 createInstance: function() { 14325 _instances[this.id] = this; 14326 }, 14327 14328 load: function load(unique) { 14329 var self = this; 14330 var seq = ++this.xhrCounter; 14331 this.isLoading = true; 14332 14333 // If mk-ajax-loaded-posts span exists, get the post ids 14334 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 14335 var loaded_posts = this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts'); 14336
14337 // Do not send looaded posts for Classic Pagination Navigation 14338 if ( this.$container.attr('data-pagination-style') != 1 ) { 14339 self.data.loaded_posts = loaded_posts.split(','); 14340 } 14341 } 14342 14343 return $.when( 14344 $.ajax({ 14345 url : _ajaxUrl, 14346 type : "POST", 14347 data : self.data 14348 }) 14349 ).done(function(response) { 14350 self.onDone(response, unique, seq); 14351 }); 14352 }, 14353 14354 onDone: function(response, unique, seq) { 14355 if(seq === this.xhrCounter) { 14356 var self = this; 14357 14358 response = $.parseJSON(response); 14359 response.unique = unique; 14360 response.id = this.id; 14361 14362 // If mk-ajax-loaded-posts span exists, update current post ids 14363 // with new post ids from server's response 14364 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 14365 this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts', response.loaded_posts); 14366 } 14367 14368 this.setData({ 14369 maxPages: response.maxPages, 14370 found_posts: response.found_posts, 14371 loop_iterator: response.i 14372 }); 14373 14374 // Preload images first by creating object from returned content. 14375 // mk_imagesLoaded() method will create a promise that gets resolved when all images inside are loaded. 14376 // Our ajaxLoad is somehow more similar to window.onload event now. 14377 $(response.content).mk_imagesLoaded().then(function() { 14378 MK.utils.eventManager.publish('ajaxLoaded', response); 14379 self.isLoading = false; 14380 self.initNewComponents(); 14381 }); 14382 14383 } else console.log('XHR request nr '+ seq +' aborted'); 14384 14385 }, 14386 14387 setData: function setData(atts) { 14388 for(var att in atts) { 14389 if(att === 'term' && atts[att] === '*') this.data.term = ''; 14390 else this.data[att] = atts[att]; 14391 } 14392 }, 14393 14394 getData: function getData(att) { 14395 return this.data[att]; 14396 }, 14397 14398 initNewComponents: function initNewComponents() { 14399 // Legacy scripts reinit 14400 window.ajaxInit(); 14401 setTimeout(window.ajaxDelayedInit, 1000); 14402 // New way to init apended things 14403 MK.core.initAll(this.el); 14404 } 14405 }; 14406 14407}(jQuery)); 14408MK.component.BackgroundImageSetter = (function ($) { 14409 14410 'use strict'; 14411 14412 var module = {}; 14413 14414 14415 /*---------------------------------------------------------------------------------*/ 14416 /* Private Variables 14417 /*---------------------------------------------------------------------------------*/ 14418 14419 /** 14420 * Take all elements with data-mk-img-set attribute and evaluate best image according to given device orientation and resolution, 14421 * sets style for backround-image on the same node element. * 14422 */ 14423 14424 var $win = $(window), 14425 // $layers = $('[data-mk-img-set]'), 14426 screen = getScreenSize(), 14427 orientation = getOrientation(), 14428 device = getDevice(), 14429 lastOrientation = orientation, 14430 lastDevice = device; 14431 14432 14433 /*---------------------------------------------------------------------------------*/ 14434 /* Private Methods 14435 /*---------------------------------------------------------------------------------*/ 14436 14437 function run($layers) { 14438 $layers.filter( function() { 14439 return !this.hasAttribute("mk-img-loaded"); 14440 }).each(applyBg); 14441 } 14442 14443 // Keep our main side effect out of calculations so they can be run once before loop of applying bg as a result 14444 function applyBg() { 14445 var $this = $(this), 14446 imgs = $this.data('mk-img-set'); 14447 14448 $this.css('background-image', 'url('+ module.getImage(imgs) +')'); 14449 $this.find('.mk-adaptive-image').attr('src', module.getImage(imgs)); 14450 } 14451 14452 // Keep track of current screen size while resizing but update device reference 14453 // and reapply backgrounds only when we discover switch point 14454 function handleResize($layers) { 14455 updateScreenSize(); 14456 if(hasSwitched()) { 14457 updateDevice(); 14458 run($layers); 14459 } 14460 } 14461 14462 function getScreenSize() { 14463 return { 14464 w: $win.width(), 14465 h: $win.height() 14466 }; 14467 } 14468 14469 // Name our device classes and add them id which simply means which one is wider 14470 function getDevice() { 14471 if (screen.w > 1024) return {class: 'desktop', id: 2}; 14472 else if(screen.w > 736) return {class: 'tablet', id: 1}; 14473 else return {class: 'mobile', id: 0}; 14474 } 14475 14476 function getOrientation() { 14477 if(screen.w > screen.h) return 'landscape'; 14478 else return 'portrait'; 14479 } 14480 14481 function updateScreenSize() { 14482 screen = getScreenSize(); 14483 } 14484 14485 function updateDevice() { 14486 if(lastOrientation !== orientation) orientation = lastOrientation; 14487 // Switch device only if going from smaller size to bigger. 14488 // Bigger to smaller is perfectly handled by browsers and doesn't require change and reupload 14489 if(lastDevice.id > device.id) device = lastDevice; 14490 } 14491 14492 function hasSwitched() { 14493 lastOrientation = getOrientation(); 14494 lastDevice = getDevice(); 14495 14496 if(lastOrientation !== orientation || lastDevice.class !== device.class) return true; 14497 else return false;
14498 } 14499 14500 14501 /*---------------------------------------------------------------------------------*/ 14502 /* Public Methods 14503 /*---------------------------------------------------------------------------------*/ 14504 14505 // As desired image might be not available we have to evaluate the best match. 14506 module.getImage = function (imgs) { 14507 if (imgs['responsive'] === 'false') { 14508 return (imgs['landscape']['desktop']) ? imgs['landscape']['desktop'] : (imgs['landscape']['external'] ? imgs['landscape']['external'] : ''); 14509 14510 } 14511 var hasOrientation = !!imgs[orientation]; 14512 // there are only two orientations now and we may get them by string name if both are there 14513 // 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. 14514 // 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 14515 var imgOriented = imgs[ (hasOrientation ? orientation : Object.keys(imgs)[0]) ], 14516 imgExact = (imgOriented[device.class]) ? imgOriented[device.class] : (imgOriented['external'] ? imgOriented['external'] : ''); 14517 return imgExact; 14518 } 14519 14520 module.init = function ($layers) { 14521 14522 // Run and bind 14523 run($layers); 14524 $layers.attr('mk-img-loaded', ''); 14525 }; 14526 14527 module.onResize = function ($layers) { 14528 $win.on('resize', MK.utils.throttle( 500, function() { 14529 handleResize($layers); 14530 })); 14531 }; 14532 14533 return module; 14534 14535}(jQuery)); 14536 14537 14538jQuery(function($) { 14539 14540 var init = function init() { 14541 // Get All Layers, Excluding Edge Slider and Page Section 14542 $allLayers = $('[data-mk-img-set]').filter(function(index) { 14543 return !$(this).hasClass('mk-section-image') && !$(this).hasClass('background-layer') && !$(this).hasClass('mk-video-section-touch'); 14544 });; 14545 14546 // Handle the resize 14547 MK.component.BackgroundImageSetter.onResize($allLayers); 14548 14549 // Set all the BG Layers 14550 MK.component.BackgroundImageSetter.init($allLayers); 14551 } 14552 init(); 14553 14554 $(window).on('vc_reload', init); 14555 14556}); 14557 14558(function( $ ) { 14559 'use strict'; 14560 14561 var val = MK.val; 14562 14563 MK.component.FullHeight = function( el ) { 14564 var $window = $( window ), 14565 $this = $( el ), 14566 config = $this.data( 'fullheight-config' ), 14567 container = document.getElementById( 'mk-theme-container' ), 14568 minH = (config && config.min) ? config.min : 0, 14569 winH = null, 14570 height = null, 14571 update_count = 0, 14572 testing = MK.utils.getUrlParameter('testing'), 14573 offset = null; 14574 14575 // We need to provide height on the same specificity level for workaround to IE bug 14576 // connect.microsoft.com/IE/feedback/details/802625/min-height-and-flexbox-flex-direction-column-dont-work-together-in-ie-10-11-preview 14577 // stackoverflow.com/questions/19371626/flexbox-not-centering-vertically-in-ie 14578 if(MK.utils.browser.name === ('IE' || 'Edge')) $this.css( 'height', '1px' ); 14579 14580 var update = function() { 14581 14582 if(update_count === 0) { 14583 winH = $window.height(); 14584 // for correct calculate 14585 offset = $this.offset().top - 1; 14586 height = Math.max(minH, winH - val.offsetHeaderHeight( offset )); 14587 $this.css( 'min-height', height ); 14588 if(testing !== undefined ) 14589 update_count++; 14590 } 14591 14592 }; 14593 14594 // TODO remove scroll listener by dynamic offset reader 14595 var init = function() { 14596 update(); 14597 $window.on( 'resize', update ); 14598 $window.on( 'scroll', update ); 14599 window.addResizeListener( container, update ); 14600 }; 14601 14602 return { 14603 init : init 14604 }; 14605 }; 14606 14607})( jQuery ); 14608 14609 14610(function( $ ) { 14611 'use strict'; 14612 14613 var core = MK.core, 14614 utils = MK.utils, 14615 path = MK.core.path; 14616 14617 14618 MK.ui.FullScreenGallery = function( element, settings ) { 14619 this.element = element; 14620 this.config = settings; 14621 14622 this.isFullScreen = false; 14623 }; 14624 14625 14626 // preload slick PLUGIN TO USE THIS 14627 MK.ui.FullScreenGallery.prototype = { 14628 dom : { 14629 fullScrBtn : '.slick-full-screen', 14630 exitFullScrBtn : '.slick-minimize', 14631 playBtn : '.slick-play', 14632 pauseBtn : '.slick-pause', 14633 shareBtn : '.slick-share', 14634 socialShare : '.slick-social-share', 14635 wrapper : '.slick-slider-wrapper', 14636 slider : '.slick-slides', 14637 slides : '.slick-slide', 14638 dots : '.slick-dot', 14639 active : '.slick-active', 14640 hiddenClass : 'is-hidden', 14641 dataId : 'slick-index' 14642 }, 14643 14644 tpl: { 14645 dot : '<div class="slick-dot"></div>', 14646 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>', 14647 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>' 14648 }, 14649 14650 init : function() { 14651 var self = this; 14652 14653 // core.loadDependencies([ path.plugins + 'slick.js' ], function() { 14654 self.cacheElements(); 14655 self.getViewportSizes(); 14656 self.updateSizes( 'window' ); 14657 self.create(); 14658 // update cache with elements propagated by plugin 14659 self.updateCacheElements(); 14660 self.createPagination(); 14661 self.bindEvents(); 14662 // }); 14663 }, 14664 14665 create : function() { 14666 var self = this; 14667 14668 this.slick = this.$gallery.slick({ 14669 dots: true, 14670 arrows: true, 14671 infinite: true, 14672 speed: 300, 14673 slidesToShow: 1, 14674 centerMode: true, 14675 centerPadding: '0px', 14676 variableWidth: true, 14677 autoplay: false, 14678 autoplaySpeed: 3000, 14679 useTransform: true, 14680 prevArrow: self.tpl.prev, 14681 nextArrow: self.tpl.next, 14682 customPaging: function(slider, i) { 14683 return self.tpl.dot; 14684 }, 14685 }); 14686 }, 14687 14688 cacheElements : function() { 14689 this.$window = $( window ); 14690 this.$gallery = $( this.element ); 14691 14692 this.$fullScrBtn = $( this.dom.fullScrBtn ); 14693 this.$exitFullScrBtn = $( this.dom.exitFullScrBtn ); 14694 this.$playBtn = $( this.dom.playBtn ); 14695 this.$pauseBtn = $( this.dom.pauseBtn ); 14696 this.$shareBtn = $( this.dom.shareBtn ); 14697 this.$socialShare = $( this.dom.socialShare ); 14698 14699 this.$wrapper = $( this.dom.wrapper ); 14700 this.$slider = $( this.dom.slider ); 14701 this.$slides = $( this.dom.slides ); 14702 this.$imgs = this.$slides.find( 'img' ); 14703 // store reference to initial images without slides appended by pugin 14704 // - needed for creating of pagination 14705 this.$originalImgs = this.$imgs; 14706 }, 14707 14708 updateCacheElements : function() { 14709 this.$slides = $( this.dom.slides ); 14710 this.$imgs = this.$slides.find( 'img' ); 14711 this.$dots = $( this.dom.dots ); 14712 }, 14713 14714 bindEvents : function() { 14715 var self = this; 14716 this.$fullScrBtn.on( 'click', this.toFullScreen.bind( this ) ); 14717 this.$exitFullScrBtn.on( 'click', this.exitFullScreen.bind( this ) ); 14718 this.$playBtn.on( 'click', this.play.bind( this ) ); 14719 this.$pauseBtn.on( 'click', this.pause.bind( this ) ); 14720 this.$shareBtn.on( 'click', this.toggleShare.bind( this ) ); 14721 this.$socialShare.on( 'click', 'a', this.socialShare.bind( this ) ); 14722 this.$window.on( 'resize', this.onResize.bind( this ) ); 14723 this.$window.on( 'keydown', function(e) { 14724 if(e.keyCode === 39) self.$gallery.slick('slickNext'); 14725 if(e.keyCode === 37) self.$gallery.slick('slickPrev'); 14726 }); 14727 $( document ).on( 'fullscreenchange mozfullscreenchange webkitfullscreenchange msfullcreenchange', this.exitFullScreen.bind( this ) ); 14728 }, 14729 14730 getViewportSizes : function() { 14731 this.screen = { 14732 w: screen.width, 14733 h: screen.height 14734 }; 14735 this.window = { 14736 w: this.$window.width(), 14737 h: this.$window.height() 14738 }; 14739 }, 14740 14741 updateSizes : function( viewport ) { 14742 this.$wrapper.width( this[ viewport ].w ); 14743 this.$wrapper.height( '100%' ); 14744 this.$imgs.height( '100%'); 14745 }, 14746 14747 createPagination : function() { 14748 var self = this; 14749 this.$dots.each( function( i ) { 14750 var img = self.$originalImgs.eq( i ).attr( 'src' ); 14751 14752 $( this ).css({ 14753 'background-image': 'url('+ img +')' 14754 }); 14755 }); 14756 }, 14757 14758 play : function(e) { 14759 e.preventDefault(); 14760 this.$playBtn.addClass( this.dom.hiddenClass ); 14761 this.$pauseBtn.removeClass( this.dom.hiddenClass ); 14762 $( this.element ).slick( 'slickPlay' ); 14763 }, 14764 14765 pause : function(e) { 14766 e.preventDefault(); 14767 this.$pauseBtn.addClass( this.dom.hiddenClass ); 14768 this.$playBtn.removeClass( this.dom.hiddenClass ); 14769 $( this.element ).slick( 'slickPause' ); 14770 }, 14771 14772 toggleShare : function(e) { 14773 e.preventDefault(); 14774 this.$socialShare.toggleClass( this.dom.hiddenClass ); 14775 }, 14776 14777 getCurentId : function() { 14778 return this.$slides.filter( this.dom.active ).data( this.dom.dataId ); 14779 }, 14780 14781 toFullScreen : function() { 14782 var self = this; 14783 14784 this.$fullScrBtn.addClass( this.dom.hiddenClass ); 14785 this.$exitFullScrBtn.removeClass( this.dom.hiddenClass ); 14786 14787 this.$slider.hide().fadeIn( 500 ); 14788 utils.launchIntoFullscreen( document.documentElement ); 14789 this.updateSizes( 'screen' ); 14790 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14791 14792 // Update state with delay so we avoid triggering exitFullScreen fn from 14793 // fullscreenchange event 14794 setTimeout( function() { 14795 self.isFullScreen = true; 14796 }, 1000); 14797 }, 14798 14799 exitFullScreen : function() { 14800 if( this.isFullScreen ) { 14801 this.$exitFullScrBtn.addClass( this.dom.hiddenClass ); 14802 this.$fullScrBtn.removeClass( this.dom.hiddenClass ); 14803 14804 utils.exitFullscreen(); 14805 this.updateSizes( 'window' ); 14806 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14807 14808 this.isFullScreen = false;
14809 } 14810 14811 }, 14812 14813 onResize : function() { 14814 this.getViewportSizes(); 14815 this.updateSizes( this.isFullScreen ? 'screen' : 'window' ); 14816 $( this.element ).slick( 'refresh' ); 14817 $( this.element ).slick( 'slickGoTo', this.getCurentId(), true ); 14818 this.updateCacheElements(); 14819 this.createPagination(); 14820 }, 14821 14822 socialShare : function( e ) { 14823 e.preventDefault(); 14824 var $this = $( e.currentTarget ), 14825 network = $this.data( 'network' ), 14826 id = this.config.id, 14827 url = this.config.url, 14828 title = this.$wrapper.find( '.slick-title' ).text(), 14829 name; 14830 var picture = this.$slides.filter( this.dom.active ).children().first().attr( 'src' ); 14831 switch( network ) { 14832 case 'facebook': 14833 url = 'https://www.facebook.com/sharer/sharer.php?picture=' + picture+'&u=' + url + '#id=' + id; 14834 name = 'Facebook Share'; 14835 break; 14836 case 'twitter': 14837 url = 'http://twitter.com/intent/tweet?text=' + url + '#id=' + id; 14838 name = 'Twitter Share'; 14839 break; 14840 case 'pinterest': 14841 url = 'http://pinterest.com/pin/create/bookmarklet/?media=' + picture + '&url=' + url + '&is_video=false&description=' + title; 14842 // other available link paranmeters: media, description 14843 name = 'Pinterest Share'; 14844 break; 14845 14846 } 14847 14848 window.open( url, name, "height=380 ,width=660, resizable=0, toolbar=0, menubar=0, status=0, location=0, scrollbars=0" ); 14849 } 14850 }; 14851 14852})( jQuery ); 14853(function( $ ) { 14854 'use strict'; 14855 14856 /** 14857 * A fork from original VC function. 14858 * The original one is adding some padding which causes extra padding 14859 * on our templates so we customized the codes. 14860 * 14861 * @since 5.9.8 14862 * @since 6.0.1 Improve logic to handle LTR responsive state. 14863 * @since 6.0.1 Fix missing padding-left if row has its own padding-left when 14864 * Vertical Header is active. 14865 */ 14866 function MkfullWidthRow() { 14867 var $windowWidth = $(document).width(); 14868 var $elements = $('[data-mk-full-width="true"]'); 14869 var direction = $('body.rtl').length ? 'right' : 'left'; 14870 var verticalHeader = $('body.vertical-header-enabled').length ? true : false; 14871 var verticalHeaderWidth = ( $( '.header-style-4 .mk-header-inner' ).outerWidth() > 270 ) ? 0 : 270; 14872 var verticalHeaderRtl = $('body.rtl').length ? -1 : 1; 14873 var verticalHeaderRtlWidth = $('body.rtl.vertical-header-enabled').length ? verticalHeaderWidth : 0; 14874 var verticalHeaderRight = $('body.vertical-header-right').length ? -1 : 1; 14875 var verticalHeaderWidthBoxed = 0; 14876 var boxed = $('body.mk-boxed-enabled').length; 14877 var boxedOffset = ( boxed ) ? ( $(window).width() - $('#theme-page').outerWidth() ) / 2 : 0; 14878 var boxedMaxWidth = ( boxed ) ? $('#theme-page').outerWidth() : 'auto'; 14879 14880 if ( verticalHeader && boxed ) { 14881 verticalHeaderWidthBoxed = ( $( '.header-style-4 .mk-header-inner' ).outerWidth() > 270 ) ? 0 : verticalHeaderRtl * verticalHeaderRight * 135; 14882 } 14883 14884 var transparentHeader = $('.transparent-header').length; 14885 if ( transparentHeader > 0 ) { 14886 verticalHeaderWidthBoxed = 0; 14887 } 14888 14889 $.each($elements, function(key, item) { 14890 var $el = $(this); 14891 var css; 14892 $el.addClass("vc_hidden"); 14893 var $el_full = $el.next(".vc_row-full-width"); 14894 if ($el_full.length || ($el_full = $el.parent().next(".vc_row-full-width")), $el_full.length) { 14895 var el_margin_left = parseInt($el.css("margin-left"), 10), 14896 el_margin_right = parseInt($el.css("margin-right"), 10), 14897 offset = 0 - $el_full.offset().left - el_margin_left, 14898 width = $(window).width(); 14899 if (css = { 14900 position: "relative", 14901 "box-sizing": "border-box", 14902 width: $(window).width(), 14903 maxWidth: boxedMaxWidth 14904 }, 14905 css[direction] = offset + boxedOffset + verticalHeaderWidthBoxed + ( verticalHeaderRight * verticalHeaderRtlWidth ), 14906 $el.css(css), !$el.data("mkStretchContent")) { 14907 var padding = -1 * offset; 14908 0 > padding && (padding = 0); 14909 var paddingRight = width - padding - $el_full.width() + el_margin_left + el_margin_right; 14910 padding = padding - paddingRight; 14911 if ( 0 > paddingRight && (paddingRight = 0) ) { 14912 css = {}; 14913 if ( 'right' === direction ) { 14914 css["padding-left"] = padding + "px"; 14915 css["padding-right"] = 0; 14916 } else { 14917 css["padding-right"] = padding + "px"; 14918 css["padding-left"] = 0; 14919 } 14920 14921 $el.css(css) 14922 } 14923 } 14924 14925 /** 14926 * Fix AM-2974 14927 * 14928 * When user set padding-left or padding-right in the row, both of them 14929 * will be prioritized as important styles. It breaks padding-left set 14930 * by Vertical Header to make the row content fit with the content 14931 * container. We need to repopulate the padding-{side - based on Vertical 14932 * Header position} value. 14933 * 14934 * Works when: 14935 * - Vertical Header is active 14936 * - Main layout is not boxed 14937 * - Page Section is full layout 14938 * - Row is full width layout and content 14939 * - Row has its own padding-left and padding-right 14940 * - There is no additional padding added 14941 * 14942 * How it works: 14943 * Get current row padding-left and vertical header width. If both of them 14944 * are equal, it means row position is correct related to Vertical Header. 14945 * If not, we should sum both of them, then declare new padding-{side} 14946 * style attribute. 14947 * 14948 * Tested with: 14949 * - Main Layout: Full & Boxed Layout 14950 * - Page Section: Full Layout and Non Full Layout 14951 * - Row: Full Width, Full Width Content, and Non Full Width 14952 * - Header: Non vertical headers 14953 * - Transparent Header 14954 * - RTL and LTR 14955 */ 14956 if ( verticalHeader && ! boxed && ! transparentHeader && ! css.hasOwnProperty( 'padding-left' ) && ! css.hasOwnProperty( 'padding-right' )) { 14957 // Get row padding-{side} and Vertical Header width. 14958 var side = 'left'; 14959 if ( verticalHeaderRight === -1 ) { 14960 side = 'right'; 14961 } 14962 var el_padding_dir = parseInt( $el.css( 'padding-' + side ), 10 ); 14963 var header_padding_dir = $( '.header-style-4 .mk-header-inner' ).outerWidth(); 14964 // Compare both of row and vertical header padding-{side}. 14965 if ( el_padding_dir != header_padding_dir) { 14966 if($windowWidth > mk_responsive_nav_width) { 14967 $el[0].style.setProperty( 'padding-' + side, header_padding_dir + 'px', 'important' ); 14968 } else { 14969 $el[0].style.removeProperty( 'padding-' + side); 14970 } 14971 14972 14973 // Reset padding-{side} for page section. 14974 var $el_page_section = $el.find( '.mk-page-section.full_layout' ); 14975 if ( $el_page_section.length > 0 ) { 14976 $el_page_section[0].style.setProperty( 'padding-' + side, 'unset', 'important' ); 14977 } 14978 } 14979 } 14980 14981 $el.attr("data-mk-full-width-init", "true"), $el.removeClass("vc_hidden"), $(document).trigger("vc-full-width-row-single", { 14982 el: $el, 14983 offset: offset, 14984 marginLeft: el_margin_left, 14985 marginRight: el_margin_right, 14986 elFull: $el_full, 14987 width: width 14988 }) 14989 } 14990 }
14990), $(document).trigger("mk-full-width-row", $elements); 14991 } 14992 14993 MkfullWidthRow(); 14994 var debounceResize = null; 14995 $(window).on("resize", function() { 14996 if( debounceResize !== null ) { clearTimeout( debounceResize ); } 14997 debounceResize = setTimeout( MkfullWidthRow, 100 ); 14998 }); 14999 15000})( jQuery ); 15001 15002(function($) { 15003 'use strict'; 15004 15005 MK.component.Grid = function( el ) { 15006 var $container = $(el); 15007 var config = $container.data( 'grid-config' ); 15008 var isSlideshow = $container.closest('[data-mk-component="SwipeSlideshow"]').length; 15009 var miniGridConfig = { 15010 container: el, 15011 item: config.item + ':not(.is-hidden)', 15012 gutter: 0 15013 }; 15014 15015 var init = function init(){ 15016 // Flags for cancelling usage goes first : 15017 // Quit early if we discover that Grid is used inside SwipeSlideshow as it brings bug with crossoverriding positioning 15018 // + grid is not really needed as we have single row all handled by slider. 15019 // It happens only in woocommerce carousel as of hardcoded Grid in loop-start.php 15020 if(isSlideshow) return; 15021 MK.core.loadDependencies([ MK.core.path.plugins + 'minigrid.js' ], create); 15022 }; 15023 15024 // Remove el hidden without adding proper class 15025 var prepareForGrid = function prepareForGrid() { 15026 var $item = $(this); 15027 var isHidden = ($item.css('display') === 'none'); 15028 if(isHidden) $item.addClass('is-hidden'); 15029 else $item.removeClass('is-hidden'); 15030 }; 15031 15032 var create = function create() { 15033 var timer = null; 15034 15035 function draw() { 15036 // Prevent plugin breaking when feeding it with hidden elements 15037 $container.find(config.item).each( prepareForGrid ); 15038 minigrid(miniGridConfig); 15039 } 15040 15041 function redraw() { 15042 if (timer) clearTimeout(timer); 15043 timer = setTimeout(draw, 100); 15044 } 15045 15046 // init 15047 draw(); 15048 // If reinitializing drop existing event handler 15049 $(window).off('resize', redraw); 15050 $(window).on('resize', redraw); 15051 MK.utils.eventManager.subscribe('item-expanded', redraw); 15052 MK.utils.eventManager.subscribe('ajaxLoaded', redraw); 15053 MK.utils.eventManager.subscribe('staticFilter', redraw); 15054 }; 15055 15056 return { 15057 init : init 15058 }; 15059 }; 15060 15061})(jQuery); 15062 15063 15064 15065 15066 15067 15068 15069 15070/** 15071 * ICON FACTORY @ Maki 15072 * 15073 * Javascript part that translates css styles into link to server side manufacturer. 15074 * By passing extra args we generate icons as needed on server and send them back with ajax and attch them to DOM here. 15075 * 15076 * 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. 15077 * 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. 15078 * 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. 15079 * 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. 15080 * 15081 * Prons: 15082 * - automated process where we can mimic old behaviour 15083 * - possible bugs are easy to patch 15084 * - 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) 15085 * - icon generation moved to server side where our main templating is already happening 15086 * - we can easily switch how our Icon should be outputed - inline, img, object, iframe. Whatever our situation requires (img for responsive icons for example) 15087 * 15088 * Cons: 15089 * - possible bugs based on different nature of svgs / fonts. Need to be manually fixed 15090 * - some manual work to straighten things - litle css refactors here and there 15091 * - as the mapping the icons happens in server side, it may cause CPU bottlenecks and should be cashed in DB for repetitve usage. 15092 * 15093 * 15094 * TODO Refactor to config obj as parameter for easier passing it. Also decouple more - especially loops 15095 */ 15096(function ($) { 15097 'use strict'; 15098 15099 // As long as we can manage this manually it is better like so. We avoid delay based on waiting for ajax to finish 15100 // $.get( MK.core.path.theme + '/assets/icons/icon-families.json', createAll); 15101 var families = [ 15102 ['awesome-icons' , 'mk-icon-'], // [family, prefix] 15103 ['icomoon' , 'mk-moon-'], 15104 ['pe-line-icons' , 'mk-li-'], 15105 ['theme-icons' , 'mk-jupiter-icon-'] 15106 ]; 15107 15108 // Our css contains this extends for particular families of fonts. For now we automate the process and extend it here as well 15109 var extend = { 15110 'awesome-icons' : [], 15111 'icomoon' : [], 15112 'pe-line-icons' : [], 15113 'theme-icons' : [] 15114 }; 15115 15116 // Cache holder for quick access to repeating icons. config / svg 15117 var _cache = {}; 15118 var _cacheId = 0;
15119 // Merge configs to call it with single ajax 15120 var _config = []; 15121 // Counter for all families. We merge all our families for single ajax request and countdown when to trigger the download. 15122 var _roundCount = 0; 15123 // Mapper for icons - config / array with nodes 15124 var _iconMap = {}; 15125 15126 15127 // Closure to merge $icons together and to run the download when we counted down icon families to 0 15128 var getIconsSprite = (function() { 15129 var $icons = null; 15130 var iterator = 0; 15131 15132 function run(callback) { 15133 var config = encodeURIComponent(JSON.stringify(_config)); 15134 15135 $.ajax({ 15136 url : MK.core.path.ajaxUrl, 15137 // dataType: 'xml', 15138 method: 'POST', 15139 data: {action : 'mk_get_icon', iterator: iterator++, config: config}, 15140 success: function(sprite) { 15141 callback(sprite, $icons); 15142 _config = []; 15143 _iconMap = {}; 15144 $icons = null; 15145 }, 15146 error: function(err) { 15147 console.log('Icon load problem'); 15148 } 15149 }); 15150 } 15151 15152 return function(callback, $els, count) { 15153 if(!$icons) $icons = $els; 15154 else $icons.add($els); 15155 // console.log(count); 15156 if(!count) run(callback); 15157 }; 15158 }()); 15159 15160 // Wait for DOM manipulations and run where really needed 15161 $(window).on('load', function() { 15162 setTimeout(function() { 15163 createAll(document); 15164 if($('.mk-header').length) createAll('.mk-header'); 15165 if($('.js-flexslider, .mk-flexslider').length) createAll('.js-flexslider, .mk-flexslider'); 15166 if($('.mk-accordion').length) createAll('.mk-accordion'); 15167 }, 1000); 15168 }); 15169 15170 MK.utils.eventManager.subscribe('ajaxLoaded', function() { 15171 // Wait for actuall DOM insertion 15172 setTimeout(createAll, 100, '.js-loop'); 15173 }); 15174 15175 MK.utils.eventManager.subscribe('ajax-preview', function() { 15176 setTimeout(createAll, 100, '.ajax-container'); 15177 }); 15178 15179 MK.utils.eventManager.subscribe('photoAlbum-open', function() { 15180 setTimeout(createAll, 100, '.gallery-share'); 15181 }); 15182 15183 MK.utils.eventManager.subscribe('quickViewOpen', function() { 15184 setTimeout(createAll, 300, '.mk-modal-content'); 15185 }); 15186 15187 // Our main runner that creates all icons in given scope 15188 function createAll(scope) { 15189 // iterate through families 15190 for(var i = 0, l = families.length; i < l; i++) { 15191 var family = families[i][0]; 15192 var prefix = families[i][1]; 15193 var $icons = getIcons(family, prefix, scope); 15194 if($icons.length) { 15195 _roundCount++; 15196 // 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. 15197 // We need it to pass all $icons first so we can merge all families before ajax request. 15198 setTimeout(createIcons, 0, $icons, family, prefix); 15199 } 15200 } 15201 } 15202 15203 // Grab all els in given scope that contains our prefix, anywhere in class attribute. Extend if needed 15204 function getIcons(family, prefix, scope) { 15205 var $scope = $(scope); 15206 var $icons = $scope.find('[class*='+ prefix +']'); 15207 var extraClassNames = extend[family]; 15208 // if nothing to extend just return what we have till now 15209 if(!extraClassNames) return $icons; 15210 // Extend it with our css garbish 15211 extraClassNames.forEach(function(className) { 15212 var $icon = $scope.find(className); 15213 $icons = $icons.add($icon); 15214 }); 15215 // serve all 15216 return $icons; 15217 } 15218 15219 15220 function createIcons($icons, family, prefix, i, unicode) { 15221 var id = i || 0; 15222 var icon = $icons[id]; 15223 15224 // quick check point, if no more icons break recursion 15225 if(!icon) { 15226 _roundCount--; 15227 getIconsSprite(insertIcons, $icons, _roundCount, _config); 15228 return; 15229 } 15230 15231 var css = getComputedStyle( icon, ':before'); 15232 var classAttr = icon.getAttribute('class'); 15233 var name = (classAttr) ? matchClass(classAttr.split(' '), prefix) : false; 15234 var h = getComputedStyle(icon).fontSize; 15235 var config = createConfig(css, name, family, unicode, h); 15236 var cache = JSON.stringify(config); 15237 15238 if(!config) { 15239 // 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) 15240 // As we expect empty response from server, we just skip to next icon 15241 15242 } else if(_cache[cache]) { 15243 // Use cached icon when possible 15244 // 15245 if(typeof _iconMap[cache] === 'undefined') _iconMap[cache] = [$icons.eq(id)]; 15246 else _iconMap[cache].push($icons.eq(id)); 15247 15248 } else { 15249 if(typeof _iconMap[cache] === 'undefined') _iconMap[cache] = [$icons.eq(id)]; 15250 else _iconMap[cache].push($icons.eq(id)); 15251 15252 _cache[cache] = _cacheId.toString(); // number as string so we could do [var] key lookup after reversal 15253 config.id = _cacheId; // extend config for server only 15254 _config.push(config); 15255 15256 _cacheId++; // increment after caching 15257 } 15258 15259 createIcons($icons, family, prefix, ++id); 15260 } 15261 15262 function insertIcons(sprite, $icons) { 15263 var $sprite = $(sprite); 15264 var $svgs = $sprite.find('svg'); 15265 var idMap = invert(_cache); 15266 15267 $sprite.each(function() { 15268 var $svg = $(this); 15269 var id = $svg.attr('data-cacheid'); // read as string 15270 var configKey = idMap[id]; 15271 15272 _cache[configKey] = this; 15273 // console.log(id, this) 15274 }); 15275 15276 // console.log('sprite',sprite) 15277 // 15278
15279 Object.keys(_iconMap).forEach(function (cacheKey) { 15280 _iconMap[cacheKey].forEach(function($icons) { 15281 $icons.each(function() { 15282 var $svg = $(_cache[cacheKey]).clone(); 15283 var $icon = $(this); 15284 15285 // console.log($icon[0], _cache[cacheKey]); 15286 15287 function remove() { 15288 // exclude problematic stuff 15289 if($icon.parents('.pricing-features')) return; 15290 $icon.not('.mk-jupiter-icon-xing') 15291 .not('.mk-jupiter-icon-square-xing') 15292 .not('.mk-jupiter-icon-simple-xing') 15293 .find('.mk-svg-icon') 15294 .not('[data-name="mk-moon-zoom-in"]') 15295 .remove(); // and remove else to reapply styles with new loaded svg 15296 } 15297 15298 if($svg.length ) remove(); 15299 15300 if(!$icon.find('svg').length) { 15301 if($icon.parents('.widget ul').length) $icon.prepend($svg); 15302 else $icon.append($svg); 15303 } 15304 }); 15305 }); 15306 }); 15307 15308 // Notify rest of app 15309 MK.utils.eventManager.publish('iconsInsert'); 15310 } 15311 15312 // Prepare configuration for server side. We need to grab icons and manipulate them a little there 15313 function createConfig(css, name, family, unicode, height) { 15314 var hasGradient = checkGradient(css); 15315 var hasDirection = extractGradient('direction', css.background); 15316 15317 var config = { 15318 family: family, 15319 // this is mostly for Extends. Because we've been customizing singular elements and assigning content unicode manually, 15320 // now we don't have any proper anchor for usage with our json map 15321 unicode: (unicode) ? unicode : decodeUnicode(css.content), 15322 name: name, 15323 // fill: css.color, 15324 gradient_type: hasGradient ? extractGradient('type' , css.background) : false, 15325 gradient_start: hasGradient ? extractGradient('start', css.background) : false, 15326 gradient_stop: hasGradient ? extractGradient('stop' , css.background) : false, 15327 gradient_direction: hasDirection ? extractGradient('direction', css.background).replace(' ', '-') : false, 15328 height: height 15329 }; 15330 15331 // If there is no name or unicode the whole config is invalid 15332 if(!config.name && !config.unicode) return false; 15333 else return config; 15334 } 15335 15336 // Extract class name that represents the icon that we try to generate
15337 function matchClass(classes, prefix) { 15338 // var missing = ''; 15339 for(var i = 0, l = classes.length; i < l; i++) { 15340 if(classes[i].indexOf(prefix) !== -1) return classes[i]; // return first matched class name 15341 // else missing += ' .' + classes[i]; // collect not matching classes 15342 } 15343 // if we didn't returned anything within loop we output notification here. Keep it for testing only 15344 // console.log('Possibility of missing icons for element with classes:' + missing); 15345 } 15346 15347 // Check if background conatains gradient declaration and return bg string if so or false 15348 function checkGradient(css) { 15349 var bg = css.background; 15350 if(bg.indexOf('radial') !== -1 || bg.indexOf('linear') !== -1) return bg; 15351 else return false; 15352 } 15353 15354 // Extract background attribiutes from bg string. 15355 // Multipurpose function which returns part we ask for 15356 function extractGradient(attr, grad) { 15357 // quit if gradient is falsy 15358 // console.log(grad); 15359 if(!grad) return false; 15360 15361 var isRadial = grad.indexOf('radial') !== -1; 15362 var isLinear = grad.indexOf('linear') !== -1; 15363 var hasDirection = grad.indexOf('(to') !== -1; 15364 var f, t; // from, to 15365 15366 if(attr === 'type') { 15367 if(isRadial) return 'radial'; 15368 if(isLinear) return 'linear'; 15369 15370 } else if(attr === 'start') { 15371 f = getStrPosition(grad, 'rgb(', 1); 15372 t = getStrPosition(grad, '0%' , 1); 15373 15374 } else if(attr === 'stop') { 15375 f = getStrPosition(grad, 'rgb(', 2); 15376 t = getStrPosition(grad, '100%', 1); 15377 15378 } else if(attr === 'direction') { 15379 if(!hasDirection) return false; 15380 f = getStrPosition(grad, '(to', 1) + 4; 15381 t = getStrPosition(grad, ', rgb(', 1); 15382 15383 } else { 15384 return false; 15385 } 15386 15387 return grad.slice(f, t); 15388 } 15389 15390 // Helper for getting indexOf but with lookup for instance of the string 15391 // str - string to search 15392 // m - what we're looking for 15393 // i - instance, starting at 1 15394 function getStrPosition(str, m, i) { 15395 return str.split(m, i).join(m).length; 15396 } 15397 15398 // JS outputs unicode results rather than code representation. This helper function does the job 15399 function decodeUnicode(content) { 15400 // escape() kills \ escape signature and outputs it as %u 15401 // str replace to drop special chars 15402 // and we bring it all to lowercase to match filenames we keep on server 15403 if(content && content !== 'none') return escape(content).replace(/%22/g, '').replace('%u', '').toLowerCase(); 15404 else return false; // just in case we haven't provided content to work with 15405 } 15406 15407 function invert(obj) { 15408 var new_obj = {}; 15409 for (var prop in obj) { 15410 if(obj.hasOwnProperty(prop)) { 15411 new_obj[obj[prop]] = prop; 15412 } 15413 } 15414 return new_obj; 15415 } 15416 15417}(jQuery)); 15418/*! 15419 * imagesLoaded PACKAGED v4.1.1 15420 * JavaScript is all like "You images are done yet or what?" 15421 * MIT License 15422 */ 15423 15424!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
15424h(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}); 15425 15426(function($, window){ 15427 'use strict'; 15428 15429 var scrollY = MK.val.scroll; 15430 var dynamicHeight = MK.val.dynamicHeight; 15431 15432 var $window = $(window); 15433 var $containers = $('.js-loop'); 15434 15435 $containers.each( pagination ); 15436 15437 $window.on( 'vc_reload', function() { 15438 $('.js-loop').each( pagination ); 15439 } ); 15440 15441 function pagination() { 15442 var unique = Date.now(); 15443 var $container = $(this); 15444 var $superContainer = $container.parent(); // should contain clearing so it stretches with floating children 15445 var $loadBtn = $container.siblings('.js-loadmore-holder').find('.js-loadmore-button'); 15446 var $loadScroll = $('.js-load-more-scroll'); 15447 var style = $container.data('pagination-style'); 15448 var maxPages = $container.data('max-pages'); 15449 var id = '#' + ($container.attr('id')); 15450 var ajaxLoader = new MK.utils.ajaxLoader(id); 15451 var isLoadBtn = (style === 2); 15452 var isInfiniteScroll = (style === 3); // add flag for last container 15453 var scrollCheckPoint = null; 15454 var isHandlerBinded = false;
15455 15456 ajaxLoader.init(); 15457 15458 init(); 15459 15460 function init() { 15461 MK.utils.eventManager.subscribe('ajaxLoaded', onLoad); 15462 bindHandlers(); 15463 if( isInfiniteScroll ) scrollCheckPoint = spyScrollCheckPoint(); 15464 15465 $window.on( 'vc_reload', function() { 15466 $window.off('scroll', handleScroll); 15467 } ); 15468 } 15469 15470 function bindHandlers() { 15471 if( isLoadBtn ) $loadBtn.on('click', handleClick); 15472 if( isInfiniteScroll ) $window.on('scroll', handleScroll); 15473 isHandlerBinded = true; 15474 } 15475 15476 function unbindHandlers() { 15477 if( isLoadBtn ) $loadBtn.off('click', handleClick); 15478 if( isInfiniteScroll ) $window.off('scroll', handleScroll); 15479 isHandlerBinded = false; 15480 } 15481 15482 function handleClick(e) { 15483 e.preventDefault(); 15484 if(!ajaxLoader.isLoading) loadMore(); 15485 } 15486 15487 function handleScroll() { 15488 if((scrollY() > scrollCheckPoint()) && !ajaxLoader.isLoading) loadMore(); 15489 } 15490 15491 function loadMore() { 15492 loadingIndicatorStart(); 15493 var page = ajaxLoader.getData('paged'); 15494 ajaxLoader.setData({paged: ++page}); 15495 ajaxLoader.load(unique); 15496 } 15497 15498 function onLoad(e, response) { 15499 if( typeof response !== 'undefined' && response.id === id) { 15500 // Checking found posts helps to fix all pagination styles 15501 if( ajaxLoader.getData('found_posts') <= 0 && ajaxLoader.getData('paged') >= ajaxLoader.getData('maxPages')) loadingIndicatorHide(); 15502 else loadingIndicatorShow(); 15503 if(response.unique === unique) $container.append(response.content); 15504 loadingIndicatorStop(); 15505 } 15506 } 15507 15508 function loadingIndicatorStart() { 15509 if(isLoadBtn) $loadBtn.addClass('is-active'); 15510 else if(isInfiniteScroll) MK.ui.loader.add('.js-load-more-scroll'); 15511 15512 } 15513 15514 function loadingIndicatorStop() { 15515 if(isLoadBtn) $loadBtn.removeClass('is-active'); 15516 else if(isInfiniteScroll) MK.ui.loader.remove('.js-load-more-scroll'); 15517 } 15518 15519 function loadingIndicatorShow() { 15520 if(isHandlerBinded) return; 15521 if(isLoadBtn) $loadBtn.show(); 15522 else if(isInfiniteScroll) $loadScroll.show(); 15523 bindHandlers(); 15524 } 15525 15526 function loadingIndicatorHide() { 15527 if(!isHandlerBinded) return; 15528 if(isLoadBtn) $loadBtn.hide(); 15529 else if(isInfiniteScroll) $loadScroll.hide(); 15530 unbindHandlers(); 15531 } 15532 15533 15534 function spyScrollCheckPoint() { 15535 var containerO = 0; 15536 var containerH = dynamicHeight( $superContainer ); 15537 var winH = dynamicHeight( window ); 15538 15539 var setVals = function() { 15540 containerO = $superContainer.offset().top; 15541 }; 15542 15543 setVals(); 15544 $window.on('resize', function() { requestAnimationFrame(setVals); }); 15545 15546 return function() { 15547 return (containerH() + containerO) - (winH() * 2); 15548 }; 15549 } 15550 } 15551 15552})(jQuery, window); 15553(function($) { 15554 'use strict'; 15555 15556 // Check if it's inside hidden parent 15557 // Cannot be position: fixed 15558 function isHidden(el) { 15559 return (el.offsetParent === null); 15560 } 15561 15562 MK.component.Masonry = function(el) { 15563 var $window = $(window); 15564 var $container = $(el); 15565 var config = $container.data( 'masonry-config' ); 15566 var $masonryItems = $container.find(config.item); 15567 var cols = config.cols || 8; 15568 var $filterItems = null; // assign only when apply filter 15569 var wall = null; 15570 15571 var init = function init() { 15572 MK.core.loadDependencies([ MK.core.path.plugins + 'freewall.js' ], onDepLoad); 15573 }; 15574 15575 var onDepLoad = function onDepLoad() { 15576 masonry(); 15577 15578 // Events 15579 $window.on('resize', onResize); 15580 MK.utils.eventManager.subscribe('ajaxLoaded', onPostAddition); 15581 MK.utils.eventManager.subscribe('staticFilter', resize); 15582 }; 15583 15584 var masonry = function masonry() { 15585 // Quit for hidden elements for now. 15586 if(isHidden(el)) return; 15587 15588 var newCols; 15589 if(window.matchMedia( '(max-width:600px)' ).matches) newCols = 2; 15590 else if(window.matchMedia( '(max-width:850px)' ).matches) newCols = 4; 15591 else newCols = cols; 15592 15593 var colW = $container.width() / newCols; 15594 15595 wall = new Freewall( config.container ); 15596 15597 // We need to pass settings to a plugin via reset method. Strange but works fine. 15598 wall.reset({
15599 selector: config.item + ':not(.is-hidden)', 15600 gutterX: 0, // set default gutter to 0 and again - apply margins to item holders in css 15601 gutterY: 0, 15602 cellW: colW, 15603 cellH: colW 15604 }); 15605 15606 wall.fillHoles(); 15607 wall.fitWidth(); 15608 15609 $masonryItems.each(function() { 15610 $(this).data('loaded', true); 15611 }); 15612 }; 15613 15614 15615 // Clear attributes after the plugin. It's API method dosn't handle it properly 15616 var destroyContainer = function destroyContainer() { 15617 $container.removeAttr('style') 15618 .removeData('wall-height') 15619 .removeData('wall-width') 15620 .removeData('min-width') 15621 .removeData('total-col') 15622 .removeData('total-row') 15623 .removeAttr('data-wall-height') 15624 .removeAttr('data-wall-width') 15625 .removeAttr('data-min-width') 15626 .removeAttr('data-total-col') 15627 .removeAttr('data-total-row'); 15628 }; 15629 15630 var destroyItem = function destroyItem() { 15631 var $item = $(this); 15632 $item.removeAttr('style') 15633 .removeData('delay') 15634 .removeData('height') 15635 .removeData('width') 15636 .removeData('state') 15637 .removeAttr('data-delay') 15638 .removeAttr('data-height') 15639 .removeAttr('data-width') 15640 .removeAttr('data-state'); 15641 }; 15642 15643 var destroyAll = function destroyAll() { 15644 if( !wall ) return; 15645 wall.destroy(); // API destroy 15646 destroyContainer(); 15647 $masonryItems.each( destroyItem ); // run our deeper destroy 15648 }; 15649 15650 var onResize = function onResize() { 15651 requestAnimationFrame(resize); 15652 }; 15653 15654 var refresh = function refresh() { 15655 if( !wall ) return; 15656 setTimeout(wall.fitWidth.bind(wall), 5); 15657 }; 15658 15659 var resize = function resize() { 15660 destroyAll(); 15661 masonry(); 15662 }; 15663 15664 var onPostAddition = function onPostAddition() { 15665 $masonryItems = $container.find(config.item); 15666 15667 $masonryItems.each(function() { 15668 var $item = $(this), 15669 isLoaded = $item.data('loaded'); 15670 15671 if(!isLoaded) $item.css('visibility', 'hidden'); 15672 }); 15673 15674 15675 $container.mk_imagesLoaded().then(function() { 15676 destroyAll(); 15677 masonry(); 15678 }); 15679 }; 15680 15681 return { 15682 init : init 15683 }; 15684 }; 15685 15686}(jQuery)); 15687(function($) { 15688 'use strict'; 15689 15690 MK.component.Pagination = function(el) { 15691 this.el = el; 15692 }; 15693 15694 MK.component.Pagination.prototype = { 15695 init: function init() { 15696 this.cacheElements(); 15697 this.bindEvents(); 15698 this.onInitLoad(); 15699 }, 15700 15701 cacheElements: function cacheElements() { 15702 this.lastId = 1; 15703 this.unique = Date.now(); 15704 this.$pagination = $(this.el); 15705 this.$container = this.$pagination.prev('.js-loop'); 15706 this.$pageLinks = this.$pagination.find('.js-pagination-page'); 15707 this.$nextLink = this.$pagination.find('.js-pagination-next'); 15708 this.$prevLink = this.$pagination.find('.js-pagination-prev'); 15709 this.$current = this.$pagination.find('.js-current-page'); 15710 this.$maxPages = this.$pagination.find('.pagination-max-pages'); // TODO change in DOM and here to js class 15711 this.containerId = '#' + this.$container.attr('id'); 15712 this.pagePathname = window.location.pathname; 15713 this.pageSearch = window.location.search; 15714 this.popState = false; 15715 this.ajaxLoader = new MK.utils.ajaxLoader('#' + this.$container.attr('id')); 15716 this.ajaxLoader.init(); 15717 }, 15718 15719 bindEvents: function bindEvents() { 15720 this.$pageLinks.on('click', this.pageClick.bind(this)); 15721 this.$nextLink.on('click', this.nextClick.bind(this)); 15722 this.$prevLink.on('click', this.prevClick.bind(this)); 15723 MK.utils.eventManager.subscribe('ajaxLoaded', this.onLoad.bind(this)); 15724 }, 15725 15726 pageClick: function pageClick(e) { 15727 e.preventDefault(); 15728 var $this = $(e.currentTarget); 15729 var id = parseFloat($this.attr('data-page-id')); 15730 15731 if(id > this.ajaxLoader.getData('maxPages') || id < 1) return; 15732 this.load(id, $this); 15733 this.updatePagedNumUrl( id ); 15734 }, 15735 15736 nextClick: function nextClick(e) { 15737 e.preventDefault(); 15738 if(this.ajaxLoader.getData('paged') === this.ajaxLoader.getData('maxPages')) return; 15739 this.load(++this.lastId, $(e.currentTarget)); 15740 this.updatePagedNumUrl( this.lastId ); 15741 }, 15742 15743 prevClick: function prevClick(e) { 15744 e.preventDefault(); 15745 if(this.ajaxLoader.getData('paged') === 1) return; 15746 this.load(--this.lastId, $(e.currentTarget)); 15747 this.updatePagedNumUrl( this.lastId ); 15748 }, 15749 15750 load: function load(id, $el) { 15751 this.lastId = id; 15752 this.ajaxLoader.setData({paged: id}); 15753 this.ajaxLoader.load(this.unique); 15754 this.removeIndicator(); 15755 MK.ui.loader.add($el); 15756 }, 15757 15758 onLoad: function success(e, response) { 15759 if( typeof response !== 'undefined' && response.id === this.containerId) { 15760 this.updatePagination(); 15761 this.lastId = this.ajaxLoader.getData('paged'); 15762 15763 if(response.unique === this.unique) { 15764 this.removeIndicator(); 15765 this.scrollPage(); 15766 this.$container.html(response.content); 15767 } 15768 } 15769 }, 15770 15771 updatePagination: function updatePagination() { 15772 var self = this; 15773 15774 // Hide / show arrows 15775 var isFirst = (this.ajaxLoader.getData('paged') === 1); 15776 var isLast = (this.ajaxLoader.getData('paged') === this.ajaxLoader.getData('maxPages')); 15777 15778 if(isFirst) this.$prevLink.addClass('is-vis-hidden'); 15779 else this.$prevLink.removeClass('is-vis-hidden'); 15780 15781 if(isLast) this.$nextLink.addClass('is-vis-hidden'); 15782 else this.$nextLink.removeClass('is-vis-hidden'); 15783 15784 // X of Y 15785 this.$current.html(this.ajaxLoader.getData('paged')); 15786 this.$maxPages.html(this.ajaxLoader.getData('maxPages')); 15787 15788 // Move overfloating items 15789 var displayItems = 10; 15790 var centerAt = displayItems / 2; 15791 15792 if(this.ajaxLoader.getData('maxPages') > displayItems) { 15793 this.$pageLinks.each(function(i) { 15794 15795 var id = self.lastId - centerAt; 15796 id = Math.max(id, 1); 15797 id = Math.min(id, self.ajaxLoader.getData('maxPages') - displayItems + 1); 15798 id = id + i; 15799 15800 $(this).html( id ).attr('data-page-id', id).show(); 15801 15802 if(i === 0 && id > 1) $(this).html('...'); 15803 if(i === displayItems - 1 && id < self.ajaxLoader.getData('maxPages')) $(this).html('...'); 15804 }); 15805 } else { 15806 this.$pageLinks.each(function(i) { 15807 var $link = $(this); 15808 var id = i + 1; 15809 15810 $link.html(id).attr('data-page-id', id); 15811 15812 if( self.ajaxLoader.getData('maxPages') === 1) { 15813 self.$pageLinks.hide(); 15814 } else { 15815 if(i > self.ajaxLoader.getData('maxPages') - 1) $link.hide(); 15816 else $link.show(); 15817 } 15818 15819 }); 15820 } 15821 15822 // Highlight current only 15823 this.$pageLinks.filter('[data-page-id="' + this.ajaxLoader.getData('paged') + '"]' ).addClass('current-page') 15824 .siblings().removeClass('current-page'); 15825 15826 }, 15827 15828 scrollPage: function scrollPage() { 15829 var containerOffset = this.$container.offset().top; 15830 var offset = containerOffset - MK.val.offsetHeaderHeight( containerOffset ) - 20; 15831 15832 this.$container.find('a:first').focus() 15833 MK.utils.scrollTo( offset ); 15834 }, 15835 15836 removeIndicator: function removeIndicator() { 15837 MK.ui.loader.remove('.js-pagination-page, .js-pagination-next, .js-pagination-prev'); 15838 }, 15839 15840 /** 15841 * Set some actions when archive/category page is loaded. Actions list: 15842 * - Select the correct paged ID on pagination list. 15843 * - Set current paged ID on the label. 15844 * - Add event listener onpopstate for handling prev/next button of Browser URL. 15845 * - Set info for updatePagedNumUrl() about request comes from popstate. 15846 * 15847 * @since 5.9.8 15848 */ 15849 onInitLoad: function onInitLoad() { 15850 var initPagedID = this.$pagination.data( 'init-pagination' ); 15851 if ( initPagedID && initPagedID > 1 ) { 15852 this.$current.html( initPagedID ); 15853 this.$pageLinks.filter( '[data-page-id="' + initPagedID + '"]' ).addClass( 'current-page' ).siblings().removeClass( 'current-page' ); 15854 } 15855 15856 // Run popstate only if it's supported by the browser. 15857 if ( 'onpopstate' in window ) { 15858 var thisPop = this; 15859 window.onpopstate = function( event ) { 15860 var id = 1; 15861 15862 // At start, state is always null. So, we should check it before processing. 15863 if ( typeof event.state === 'object' && event.state ) { 15864 var state = event.state; 15865 15866 // Set paged ID for updating page. 15867 if ( state.hasOwnProperty( 'MkPagination' ) ) { 15868 var currentState = state.MkPagination; 15869 if ( currentState.hasOwnProperty( 'paged' ) ) { 15870 id = parseFloat( currentState.paged ); 15871 } 15872 } 15873 } else { 15874 id = parseFloat( thisPop.getURLPagedID() ); 15875 } 15876 15877 // Tell updatePagedNumUrl() if request come from popstate. 15878 thisPop.popState = true; 15879 thisPop.$pageLinks.filter( '[data-page-id="' + id + '"]' ).trigger( 'click' ); 15880 } 15881 } 15882 }, 15883 15884 /** 15885 * Update current pagination browser URL by adding/changing paged number. Only run if 15886 * the browser support pushState and the request not coming from popstate. 15887 * 15888 * WordPress has some ways to set paged number: 15889 * 1. page/[number], paged=[number] will be directed here. 15890 * 2. page=[number] 15891 * So, we should check which one the request is used here. 15892 * 15893 * @since 5.9.8 15894 */ 15895 updatePagedNumUrl: function updatePagedNumUrl( id ) { 15896 // Check pushState browser support and ignore if request come from popstate. 15897 if ( 'history' in window && 'pushState' in history && id && ! this.popState ) { 15898 var fullPage = this.pagePathname + this.pageSearch; 15899 var isQueryPage = false;
15900 15901 // Style 1 - /page/[number], as default value. 15902 var newPage = 'page/' + id + '/'; 15903 var expPage = /page\/\d+\/?/; 15904 var result = this.pagePathname.match( /\/page\/\d+/ ); 15905 var isPagedExist = ( result ) ? true : false; 15906 15907 // Style 2 - ?page=[number], only run if /page/ is not exist and URL query var exist. 15908 if ( ! isPagedExist && this.pageSearch ) { 15909 isQueryPage = this.pageSearch.match( /page\=\d+/ ); 15910 if ( isQueryPage ) { 15911 newPage = 'page=' + id; 15912 expPage = /page\=\d+/; 15913 } 15914 } 15915 15916 // If page number is 1, remove paged number from URL. 15917 if ( id === 1 ) { 15918 newPage = ''; 15919 if ( isQueryPage ) { 15920 expPage = ( this.pageSearch.match( /\&+/ ) ) ? /page\=\d+\&?/ : /\?page\=\d+\&?/; 15921 } 15922 } 15923 15924 // Set new pathname. Do replacement only if the new pathname contains paged number. 15925 var newURL = this.pagePathname + newPage + this.pageSearch; 15926 if ( fullPage.match( expPage ) ) { 15927 newURL = fullPage.replace( expPage, newPage ); 15928 } 15929 15930 // Set history state and return popstate back to fal
15930se. 15931 var historyState = { 15932 MkPagination: { 15933 url: newURL, 15934 paged: id 15935 } 15936 } 15937 this.popState = false; 15938 15939 // Push new pathname to display/hide the paged number. 15940 window.history.pushState( historyState, null, newURL ); 15941 } 15942 this.popState = false; 15943 }, 15944 15945 /** 15946 * Get current URL page ID. Notes: 15947 * 1. page/[number], paged=[number] will be directed here. 15948 * 2. page=[number] 15949 * 15950 * @return {integer} Current paged ID. Default 1. 15951 */ 15952 getURLPagedID: function getURLPagedID() { 15953 var pathname = window.location.pathname; 15954 var search = window.location.search; 15955 var pagedId = 1; 15956 var result = ''; 15957 var isPagedExist = false; 15958 15959 // Search based on style 1. 15960 result = pathname.match( /\/page\/(\d+)/ ); 15961 if ( result ) { 15962 isPagedExist = true; 15963 pagedId = ( result.hasOwnProperty( 1 ) ) ? result[1] : 1; 15964 } 15965 15966 // Search based on style 2. 15967 if ( ! isPagedExist && search ) { 15968 result = search.match( /page\=(\d+)/ ); 15969 if ( result ) { 15970 isPagedExist = true; 15971 pagedId = ( result.hasOwnProperty( 1 ) ) ? result[1] : 1; 15972 } 15973 } 15974 15975 return pagedId; 15976 } 15977 }; 15978 15979}(jQuery)); 15980(function( $ ) { 15981 'use strict'; 15982 15983 var val = MK.val, 15984 utils = MK.utils; 15985 15986 MK.component.Parallax = function( el ) { 15987 var self = this, 15988 $this = $( el ), 15989 obj = $this[0], 15990 $window = $( window ), 15991 container = document.getElementById( 'mk-theme-container' ), 15992 config = $this.data( 'parallax-config' ), 15993 $holder = $( config.holder ), 15994 headerHeight = null, 15995 offset = null, 15996 elHeight = null, 15997 ticking = false, 15998 isMobile = null; 15999 16000 16001 var clientRect = null; 16002 16003 var update = function() { 16004 // Clear styles to check for natural styles 16005 // then apply position and size 16006 obj.style.transform = null; 16007 obj.style.top = null; 16008 obj.style.bottom = null; 16009 16010 isMobile = MK.utils.isMobile(); 16011 16012 if( isMobile ) { 16013 $this.css( 'height', '' ); 16014 return; 16015 } 16016 16017 clientRect = $this[ 0 ].getBoundingClientRect(); 16018 offset = clientRect.top; 16019 elHeight = clientRect.height; 16020 headerHeight = val.offsetHeaderHeight( offset ); 16021 offset = offset - headerHeight + val.scroll(); 16022 16023 setPosition(); 16024 setSize( ); 16025 }; 16026 16027 16028 var h = 0, 16029 winH = 0, 16030 proportion = 0, 16031 height = 0; 16032 16033 // Position and background attachement should me moved to CSS but we repair it high specificity here as styles are not reliable currently 16034 var setSize = function() { 16035 $this.css( 'height', '' ); 16036 winH = $window.height() - headerHeight; 16037 h = obj.getBoundingClientRect().height; 16038 16039 if( config.speed <= 1 && config.speed > 0 ) { 16040 if( offset === 0 ) { 16041 $this.css({ 16042 backgroundAttachment: 'scroll', 16043 'will-change': 'transform' 16044 }); 16045 } else { 16046 $this.css({ 16047 height : h + ( (winH - h) * config.speed ), 16048 backgroundAttachment: 'scroll', 16049 'will-change': 'transform' 16050 }); 16051 } 16052 16053 } else if ( config.speed > 1 && h <= winH ) { 16054 $this.css({ 16055 // good for full heights - 2 because it's viewable by 2 screen heights 16056 height: ( winH + ( ( winH * config.speed ) - winH ) * 2 ), 16057 top: -( ( winH * config.speed ) - winH ), 16058 backgroundAttachment: 'scroll', 16059 'will-change': 'transform' 16060 }); 16061 16062 } else if ( config.speed > 1 && h > winH ) { 16063 proportion = h / winH; 16064 height = ( winH + ( ( winH * config.speed ) - winH ) * (1 + proportion) ); 16065 16066 $this.css({ 16067 height: height, 16068 top: -( height - (winH * config.speed) ), 16069 backgroundAttachment: 'scroll', 16070 'will-change': 'transform' 16071 }); 16072 16073 } else if ( config.speed < 0 && h >= winH ) { 16074 height = h * (1 - config.speed); 16075 $this.css({ 16076 height: height + (height - h), 16077 top: h - height, 16078 backgroundAttachment: 'scroll', 16079 'will-change': 'transform' 16080 }); 16081 16082 } else if ( config.speed < 0 && h < winH ) { 16083 // candidate to change 16084 var display = (winH + h) / winH; 16085 height = h * -config.speed * display; 16086 $this.css({ 16087 height: h + (height * 2), 16088 top: -height, 16089 backgroundAttachment: 'scroll', 16090 'will-change': 'transform' 16091 }); 16092 } 16093 }; 16094 16095 16096 var currentPoint = null, 16097 progressVal = null, 16098 startPoint = null, 16099 endPoint = null, 16100 $opacityLayer = config.opacity ? $this.find( config.opacity ) : null, 16101 scrollY = null; 16102 16103 var setPosition = function() { 16104 startPoint = offset - winH; 16105 endPoint = offset + elHeight + winH - headerHeight; 16106 scrollY = val.scroll(); 16107 16108 if( scrollY < startPoint || scrollY > endPoint ) { 16109 ticking = false;
16110 return; 16111 } 16112 16113 currentPoint = (( -offset + scrollY ) * config.speed); 16114 16115 $this.css({ 16116 '-webkit-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 16117 '-moz-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 16118 '-ms-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 16119 '-o-transform': 'translateY(' + currentPoint + 'px) translateZ(0)', 16120 'transform': 'translateY(' + currentPoint + 'px) translateZ(0)' 16121 }); 16122 16123 ticking = false; 16124 }; 16125 16126 16127 var requestTick = function() { 16128 if( !ticking && !isMobile ) { 16129 ticking = true; 16130 window.requestAnimationFrame( setPosition ); 16131 } 16132 }; 16133 16134 16135 var init = function() { 16136 // Disable scroll effects when smooth scroll is disabled 16137 if( !MK.utils.isSmoothScroll ) { return; } 16138 16139 update(); 16140 setTimeout(update, 100); 16141 $window.on( 'load', update ); 16142 $window.on( 'resize', update ); 16143 window.addResizeListener( container, update ); 16144 16145 $window.on( 'scroll', requestTick ); 16146 }; 16147 16148 16149 return { 16150 init : init 16151 }; 16152 }; 16153 16154})( jQuery ); 16155(function($) { 16156 'use strict'; 16157 16158 MK.component.Preloader = function(el) { 16159 this.el = el; 16160 }; 16161 16162 MK.component.Preloader.prototype = { 16163 init: function init() { 16164 this.cacheElements(); 16165 this.bindEvents(); 16166 }, 16167 16168 cacheElements: function cacheElements() { 16169 this.$preloader = $(this.el); 16170 }, 16171 16172 bindEvents: function bindEvents() { 16173 this.onLoad(); // all components inited on page load 16174 }, 16175 16176 onLoad: function onLoad() { 16177 setTimeout(this.hidePreloader.bind(this), 300); 16178 }, 16179 16180 hidePreloader: function hidePreloader() { 16181 this.$preloader.hide(); 16182 } 16183 }; 16184 16185}(jQuery)); 16186 16187(function($) { 16188 'use strict'; 16189 16190 // Image added for proportional scaling 16191 MK.ui.loader = { 16192 tpl : function() { 16193 return '<div class="mk-loading-indicator">' + 16194 '<div class="mk-loading-indicator__inner">' + 16195 '<div class="mk-loading-indicator__icon"></div>' + 16196 '<img style="height:100%; width:auto;" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">' + 16197 '</div>' + 16198 '</div>'; 16199 }, 16200 16201 add : function(item) { 16202 $(item).append(this.tpl); 16203 }, 16204 16205 remove : function(item) { 16206 if(item) $(item).find('.mk-loading-indicator').remove(); 16207 else $('.mk-loading-indicator').remove(); 16208 } 16209 }; 16210 16211}(jQuery)); 16212(function( $ ) { 16213 // IE / Edge fix for fixed positioned elements 16214 // MS clip path doesnt redraw properly so we expirience similar bug like with background attachement fixed on Chrome 16215 if (MK.utils.browser.name === 'Edge' || MK.utils.browser.name === 'IE') { 16216 var val = 1; 16217 var $edgeClipper = $('.mk-slider-slide'); // edge slider 16218 var $sectionClipper = $('.clipper-true'); // edge slider 16219 var $bgLayer = $('.background-layer'); // page section 16220 16221 var onScroll = function onScroll() { 16222 val *= -1; 16223 if( $edgeClipper.length ) $edgeClipper.each( redraw ); 16224 if( $sectionClipper.length ) $sectionClipper.each( redraw ); 16225 if( $bgLayer.length ) $bgLayer.each( redraw ); 16226 }; 16227 16228 var redraw = function redraw() { 16229 $(this).css('margin-top', val / 100); 16230 }; 16231 16232 $(window).on("scroll", function () { 16233 window.requestAnimationFrame(onScroll); 16234 }); 16235 } 16236}(jQuery)); 16237MK.component.ResponsiveImageSetter = (function ($) { 16238 16239 'use strict'; 16240 16241 var module = {}; 16242 16243 16244 /*---------------------------------------------------------------------------------*/ 16245 /* Private Variables 16246 /*---------------------------------------------------------------------------------*/ 16247 16248 var viewportClass = getViewportClass(); 16249 var isRetina = window.devicePixelRatio >= 2; 16250 16251 16252 /*---------------------------------------------------------------------------------*/ 16253 /* Private Methods 16254 /*---------------------------------------------------------------------------------*/ 16255 16256 function run($imgs) { 16257 $imgs.filter( function() { 16258 return !this.hasAttribute("mk-img-src-setted"); 16259 }).each(setSrcAttr); 16260 } 16261 16262 function setSrcAttr() { 16263 var $img = $(this); 16264 var set = $img.data('mk-image-src-set'); 16265 // Set src attribute to img link suitable for our logic. It will load the image. 16266 if(set['responsive'] === 'false' && isRetina && set['2x']) $img.attr('src', set['2x']); 16267 else if(set['responsive'] === 'false') $img.attr('src', set.default); 16268 else if(viewportClass === 1 && isRetina && set['2x']) $img.attr('src', set['2x']); // default x2 for retina 16269 else if(viewportClass === 0 && set.mobile) $img.attr('src', set.mobile); 16270 else $img.attr('src', set.default); 16271 16272 $img.load(function() { 16273 $(window).trigger('mk-image-loaded') 16274 }) 16275 } 16276 16277 function getViewportClass() { 16278 if(window.matchMedia('(max-width: 736px)').matches) return 0; 16279 else return 1; 16280 } 16281 16282 function handleResize($imgs) { 16283 if(!$imgs.length) return;
16283 // Do not run if empty collection 16284 var currentViewportClass = getViewportClass(); 16285 // We don't need to reload bigger images when screen size is decreasing as browser already performs resize operation. 16286 // Run update on for increasing screen size 16287 if( currentViewportClass > viewportClass) { 16288 viewportClass = currentViewportClass; // update for further reference 16289 run($imgs); 16290 } 16291 } 16292 16293 16294 /*---------------------------------------------------------------------------------*/ 16295 /* Public Methods 16296 /*---------------------------------------------------------------------------------*/ 16297 16298 module.init = function ($imgs) { 16299 16300 // Do not run if empty collection 16301 if(!$imgs.length) return; 16302 16303 // Run and bind to events 16304 run($imgs); 16305 $imgs.attr('mk-img-src-setted', ''); 16306 16307 }; 16308 16309 module.onResize = function ($imgs) { 16310 16311 $(window).on( 'resize', MK.utils.throttle( 500, function() { 16312 handleResize($imgs); 16313 })); 16314 16315 16316 }; 16317 16318 16319 module.handleAjax = function () { 16320 setTimeout(function ajaxDelayedCallback() { // give it a chance to insert content first 16321 var $newImgs = $('img[data-mk-image-src-set]').filter( function() { 16322 return !this.hasAttribute("mk-lazyload"); 16323 }); 16324 if(!$newImgs.length) return; 16325 run($newImgs); 16326 }, 100); 16327 } 16328 16329 16330 return module; 16331 16332}(jQuery)); 16333 16334 16335jQuery(function($) { 16336 16337 var init = function init() { 16338 // Get All Responsive Images 16339 $allImages = $('img[data-mk-image-src-set]').filter(function(index) { 16340 var isNotPortfolioImage = !$(this).hasClass('portfolio-image'), 16341 isNotBlogImage = $(this).closest('.mk-blog-container').length == 0, 16342 isNotSwiperImage = !$(this).hasClass('swiper-slide-image'), 16343 isNotGalleryImage = !$(this).hasClass('mk-gallery-image'); 16344 return isNotPortfolioImage && isNotBlogImage && isNotSwiperImage && isNotGalleryImage; 16345 });; 16346 16347 // Handle the resize 16348 MK.component.ResponsiveImageSetter.onResize($allImages); 16349 16350 MK.component.ResponsiveImageSetter.init($allImages); 16351 16352 MK.utils.eventManager.subscribe('ajaxLoaded', MK.component.ResponsiveImageSetter.handleAjax ); // ajax loops 16353 MK.utils.eventManager.subscribe('ajax-preview', MK.component.ResponsiveImageSetter.handleAjax ); // ajax portfolio 16354 MK.utils.eventManager.subscribe('quickViewOpen', MK.component.ResponsiveImageSetter.handleAjax ); 16355 }
16356 init(); 16357 $(window).on('vc_reload', init); 16358 16359}); 16360 16361 16362 16363(function( $ ) { 16364 'use strict'; 16365 16366 var utils = MK.utils; 16367 var val = MK.val; 16368 16369 /** 16370 * Keep track of top Level sections so we can easly skip to next one. 16371 * We must be explicit about DOM level to nested sections. 16372 * 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). 16373 * We keep track for the same sections in Footer for mutating window location with '!loading' to prevent native anchor behaviour. 16374 */ 16375 var $topLevelSections = $('#theme-page > .vc_row, #theme-page > .mk-main-wrapper-holder, #theme-page > .mk-page-section'); 16376 16377 $( document ).on( 'click', '.mk-skip-to-next', function() { 16378 var $this = $( this ), 16379 /** 16380 * Static height of button + the space to the bottom of the container. 16381 * 16382 * @TODO Possible to calculate dynamically. 16383 */ 16384 btnHeight = $this.hasClass( 'edge-skip-slider' ) ? 150 : 76, 16385 offset = $this.offset().top + btnHeight, 16386 nextOffset = utils.nextHigherVal( utils.offsets( $topLevelSections ), [offset] ); 16387 16388 utils.scrollTo( nextOffset - val.offsetHeaderHeight( nextOffset ) ); 16389 }); 16390 16391})( jQuery ); 16392 16393(function($) { 16394 'use strict'; 16395 16396 // 16397 // Constructor 16398 // 16399 // ///////////////////////////////////////////////////////// 16400 16401 MK.ui.Slider = function( container, config ) { 16402 16403 var defaults = { 16404 slide : '.mk-slider-slide', 16405 nav : '.mk-slider-nav', 16406 effect : 'roulete', 16407 ease : 'easeOutQuart', // should not be changed, remove 16408 slidesPerView : 1, 16409 slidesToView : 1, 16410 transitionTime : 700, 16411 displayTime : 3000, 16412 autoplay : true, 16413 hasNav : true, 16414 hasPagination : true, 16415 paginationTpl : '<span></span>', 16416 paginationEl : '#pagination', 16417 draggable : true, 16418 fluidHeight : false, 16419 pauseOnHover : false, 16420 lazyload : false, 16421 activeClass : 'is-active', 16422 edgeSlider : false, 16423 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>', 16424 onInitialize : function() {}, 16425 onAfterSlide : function( id ) {}, 16426 onBeforeSlide : function( id ) {} 16427 }; 16428 16429 this.state = { 16430 id : 0, 16431 moveForward : true, 16432 running : false, 16433 zIFlow : null, 16434 stop : false, 16435 }; 16436 16437 this.config = $.extend( defaults, config ); 16438 this.container = container; 16439 16440 this.initPerView = this.config.slidesPerView; 16441 16442 // Timer holder 16443 this.activeTimer = null; 16444 this.autoplay = null; 16445 this.timer = null; 16446 this.timerRemaining = parseInt(this.config.displayTime); 16447 16448 // Boolean 'Em All, Making sure it's not string 16449 this.config.lazyload = JSON.parse(this.config.lazyload); 16450 this.config.edgeSlider = JSON.parse(this.config.edgeSlider); 16451 16452 // Image Loader Instance 16453 this.imageLoader = null; 16454 16455 // Add abort command to imagesLoaded, Placing it inside script makes it to work with different versions 16456 // of imagesLoaded if loaded by other Plugins 16457 imagesLoaded.prototype.abort = function() { 16458 this.progress = this.complete = function() { }; 16459 }; 16460 }; 16461 16462 16463 16464 // 16465 // Shared methods 16466 // 16467 // ///////////////////////////////////////////////////////// 16468 16469 MK.ui.Slider.prototype = { 16470 16471 init : function() { 16472 this.setPerViewItems(); 16473 this.cacheElements(); 16474 this.getSlideSize(); 16475 this.bindEvents(); 16476 this.setSize(); 16477 this.setPos(); 16478 16479 // Hack for preparing 'prev' on first click if needed 16480 this.updateId( -1 ); 16481 this.updateId( 1 ); 16482 16483 this.val = this.dynamicVal(); 16484 this.timeline = this.prepareTimeline( this.config.transitionTime ); 16485 16486 this.timeline.build(); 16487 16488 if( this.config.hasPagination ) { this.buildPagination(); } 16489
16490 if( this.config.autoplay && document.hasFocus() ) { this.setTimer(); } 16491 16492 if( typeof this.config.onInitialize === 'function' ) { 16493 this.config.onInitialize( this.slides ); 16494 } 16495 16496 if( this.config.fluidHeight === true ) { 16497 $( this.slides ).height( 'auto' ); 16498 $( this.container ).css( 'transition', 'height ' + 200 + 'ms ease-out' ); 16499 this.setHeight( 0 ); 16500 } 16501 16502 16503 if( this.config.fluidHeight === 'toHighest' ) { 16504 this.setHeightToHighest(); 16505 } 16506 16507 // Create timer per slide if required 16508 $(this.slides).each(this.createTimer); 16509 16510 // If it's Edge Slider and Lazy Load is enabled 16511 if ( this.config.lazyload && this.config.edgeSlider ) { 16512 16513 // If It's not a Video Slide 16514 if ( $(this.slides[this.state.id]).find('video').length === 0 ) { 16515 // Set the first slide's BG image 16516 var $slideImg = $(this.slides[this.state.id]).children('[data-mk-img-set]'); 16517 MK.component.BackgroundImageSetter.init( $slideImg ); 16518 } 16519 $(this.config.spinnerTpl).prependTo( this.$slides ); 16520 16521 } else { 16522 16523 // Set all slides's BG images 16524 MK.component.BackgroundImageSetter.init( $(this.slides).children('[data-mk-img-set]') ); 16525 16526 } 16527 16528 16529 16530 }, 16531 16532 16533 cacheElements : function () { 16534 this.container = this.isNode( this.container ) ? this.container 16535 : document.querySelectorAll( this.container )[0]; 16536 this.slides = this.container.querySelectorAll( this.config.slide ); 16537 this.$slides = $(this.slides); 16538 16539 if( this.config.hasNav ) { this.$nav = $( this.config.nav ); } 16540 if( this.config.hasPagination ) { this.$pagination = $( this.config.paginationEl ); } 16541 }, 16542 16543 16544 bindEvents : function() { 16545 var $window = $( window ); 16546 16547 if( this.config.slidesPerView > 1 ) { $window.on( 'resize', this.setPerViewItems.bind( this ) ); } 16548 if( this.config.hasNav ) { this.eventsNav(); } 16549 if( this.config.hasPagination ) { this.eventsPag(); } 16550 if( this.config.draggable ) { this.dragHandler(); } 16551 if( this.config.autoplay ) { 16552 $window.on( 'focus', this.windowActive.bind( this ) ); 16553 $window.on( 'blur', this.windowInactive.bind( this ) ); 16554 } 16555 if( this.config.pauseOnHover ) { 16556 $(this.container).on( 'mouseleave', this.setTimer.bind( this ) ); 16557 $(this.container).on( 'mouseenter', this.unsetTimer.bind( this ) ); 16558 } 16559 if( this.config.fluidHeight === 'toHighest' ) { 16560 $window.on( 'resize', this.setHeightToHighest.bind( this ) ); 16561 } 16562 }, 16563 16564 16565 setPerViewItems: function() { 16566 if(window.matchMedia( '(max-width: 500px)' ).matches) { this.config.slidesPerView = 1; } 16567 else if(window.matchMedia( '(max-width: 767px)' ).matches && this.initPerView >= 2 ) { this.config.slidesPerView = 2; } 16568 else if(window.matchMedia( '(max-width: 1024px)' ).matches && this.initPerView >= 3 ) { this.config.slidesPerView = 3; } 16569 else { this.config.slidesPerView = this.initPerView; } 16570 16571 if( typeof this.slides === 'undefined' ) return; 16572 this.getSlideSize(); 16573 this.setSize(); 16574 this.setPos(); 16575 this.timeline = this.prepareTimeline( this.config.transitionTime ); 16576 this.timeline.build(); 16577 }, 16578 16579 16580 eventsNav : function() { 16581 this.$nav.on( 'click', 'a', this.handleNav.bind( this ) ); 16582 }, 16583 16584 16585 eventsPag : function() { 16586 this.$pagination.on( 'click', 'a', this.handlePagination.bind( this ) ); 16587 }, 16588 16589 16590 handleNav : function( e ) { 16591 e.preventDefault(); 16592 16593 if( this.state.running ) { return; } 16594 this.state.running = true; 16595 16596 var $this = $( e.currentTarget ), 16597 moveForward = $this.data( 'direction' ) === 'next'; 16598 16599 16600 if( this.config.autoplay ) { 16601 this.unsetTimer(); 16602 setTimeout( this.setTimer.bind( this ), this.config.transitionTime ); 16603 } 16604 16605 this.state.moveForward = moveForward; 16606 this.timeline.build(); 16607 this.timeline.play(); 16608 16609 this.setActive( this.nextId( moveForward ? 1 : -1 ) ); 16610 if( this.config.fluidHeight ) { this.setHeight( this.nextId( moveForward ? 1 : -1 ) ); } 16611 }, 16612 16613 16614 handlePagination : function( e ) { 16615 e.preventDefault(); 16616 16617 var $this = $( e.currentTarget ), 16618 id = $this.index(); 16619 16620 this.goTo( id ); 16621 }, 16622 16623 16624 reset: function() { 16625 this.state.stop = true; 16626 this.state.id = 0; 16627 this.setPos(); 16628 this.unsetTimer(); 16629 this.setTimer(); 16630 }, 16631 16632 16633 goTo : function(id) { 16634 if( this.state.running ) { return; } 16635 this.state.running = true; 16636 16637 var lastId = this.state.id; 16638 16639 if( lastId === id ) { 16640 return; 16641 } else if( lastId < id ) { 16642 this.state.moveForward = true; 16643 } else { 16644 this.state.moveForward = false;
16645 } 16646 16647 if( this.config.autoplay ) { 16648 this.unsetTimer(); 16649 setTimeout( this.setTimer.bind( this ), this.config.transitionTime ); 16650 } 16651 16652 this.timeline.build( Math.abs( lastId - id ) ); 16653 this.timeline.play(); 16654 16655 this.setActive( id ); 16656 if( this.config.fluidHeight ) { this.setHeight( id ); } 16657 }, 16658 16659 16660 windowActive : function() { 16661 this.setTimer(false, true); 16662 $(this.container).removeClass('is-paused'); 16663 }, 16664 16665 16666 windowInactive : function() { 16667 this.unsetTimer(); 16668 $(this.container).addClass('is-paused'); 16669 }, 16670 16671 16672 updateId : function( val ) { 16673 this.state.id = this.nextId(val); 16674 }, 16675 16676 nextId : function( val ) { 16677 var len = this.slides.length, 16678 insertVal = this.state.id + val; 16679 insertVal = ( insertVal >= 0 ) ? insertVal : len + val; 16680 insertVal = ( insertVal >= len ) ? 0 : insertVal; 16681 16682 return insertVal; 16683 }, 16684 16685 16686 setStyle : function( obj, style ) { 16687 var hasT = style.transform, 16688 t = { 16689 x : ( hasT ) ? style.transform.translateX : null, 16690 y : ( hasT ) ? style.transform.translateY : null, 16691 scale : ( hasT ) ? style.transform.scale : null, 16692 rotate : ( hasT ) ? style.transform.rotate : null, 16693 rotateX : ( hasT ) ? style.transform.rotateX : null, 16694 rotateY : ( hasT ) ? style.transform.rotateY : null 16695 }, 16696 z = 'translateZ(0)', 16697 x = (t.x) ? 'translateX(' + t.x + '%)' : 'translateX(0)', 16698 y = (t.y) ? 'translateY(' + t.y + '%)' : 'translateY(0)', 16699 s = (t.scale) ? 'scale(' + t.scale + ')' : 'scale(1)', 16700 r = (t.rotate) ? 'rotate(' + t.rotate + 'deg)' : 'rotate(0)', 16701 rX = (t.rotateX) ? 'rotateX(' + t.rotateX + 'deg)' : '', 16702 rY = (t.rotateY) ? 'rotateY(' + t.rotateY + 'deg)' : '', 16703 16704 o = style.opacity, 16705 h = style.height, 16706 w = style.width; 16707 16708 var c = z + x + y + s + r + rX + rY; 16709 16710 if( c.length ) { 16711 obj.style.webkitTransform = c; 16712 obj.style.msTransform = c; 16713 obj.style.transform = c; 16714 } 16715 16716 if( typeof o === 'number' ) { obj.style.opacity = o; } 16717 if( h ) { obj.style.height = h + '%'; } 16718 if( w ) { obj.style.width = w + '%'; } 16719 }, 16720 16721 16722 setPos : function() { 16723 if( typeof this.slides === 'undefined' ) return; 16724 var id = this.state.id, 16725 i = 0, 16726 len = this.slides.length, 16727 animation = this.animation[ this.config.effect ], 16728 axis = animation.axis, 16729 animNext = animation.next, 16730 animActi = animation.active, 16731 animPrev = animation.prev, 16732 perView = this.config.slidesPerView, 16733 slideId = null, 16734 zIFlow = null, 16735 style = {}; 16736 16737 style.transform = {}; 16738 16739 16740 for( ; i < len; i += 1 ) { 16741 if(i < perView) { 16742 // Position for visible slides. Apply active styles 16743 style = animActi; 16744 style.transform[ 'translate' + axis ] = i * 100; 16745 } else { 16746 // Rest slides move after edge based on axis and moveForward. Apply Next / Prev styles 16747 style = this.state.moveForward ? animNext : animPrev; 16748 style.transform[ 'translate' + axis ] = this.state.moveForward ? perView * 100 : -100; 16749 } 16750 16751 this.slides[ i ].style.zIndex = 0; 16752 16753 slideId = ( i + id ) % len; 16754 this.setStyle( this.slides[ slideId ], style ); 16755 } 16756 }, 16757 16758 16759 // When we're setting animation along Y axis we're going to set up height 16760 // otherwise width. It is shared amongst all slides 16761 setSize : function() { 16762 if( typeof this.slides === 'undefined' ) return; 16763 var i = 0, 16764 len = this.slides.length, 16765 axis = this.animation[ this.config.effect ].axis, 16766 slideSize = this.slideSize, 16767 style = {}; 16768 16769 if( axis === 'Y' ) { 16770 style.height = slideSize[ axis ]; 16771 } else { 16772 style.width = slideSize[ axis ]; 16773 } 16774 16775 for( ; i < len; i += 1 ) { 16776 this.setStyle( this.slides[ i ], style ); 16777 } 16778 }, 16779 16780 16781 setHeight : function( id ) { 16782 var $slides = $( this.slides ), 16783 $activeSlide = $slides.eq( id ); 16784 16785 var currentHeight = $activeSlide.height(); 16786 $( this.container ).height( currentHeight ); 16787 }, 16788 16789 16790 setHeightToHighest : function() { 16791 // this is becouse of alliginig woocommrece carousel. Too much DOM 16792 // Refactor someday 16793 var $slides = $( this.slides ), 16794 height = 0; 16795 16796 $slides.each(function() { 16797 height = Math.max(height, $(this).find('> div').outerHeight()); 16798 }); 16799 16800 $( this.container ).height( height ); 16801 }, 16802 16803 16804 // Little utility inspired by GreenSock. 16805 // We export this to this.timeline on init. 16806 prepareTimeline : function( time ) { 16807 var self = this, 16808 iteration = 0, 16809 totalIter = time / (1000 / 60), 16810 animLoop = [], 16811 aL = 0, // animation length 16812 loops = 1, 16813 ease = this.config.ease, 16814 currentStyle, timeProg, 16815 build, move, add, play, reverse, progress, kill; 16816 16817 16818 // Build constants, run them only once 16819 // take out possibly 16820 var len = this.slides.length, 16821 perView = this.config.slidesPerView, 16822 animation = this.animation[ this.config.effect ], 16823 animAxis = animation.axis, 16824 animNext = animation.next, 16825 animActi = animation.active, 16826 animPrev = animation.prev, 16827 style = {}, 16828 slideId = null, 16829 zIFlow = null; 16830 16831 style.transform = {}; 16832 16833 16834 build = function( repeats ) { 16835 var currentEase = ease; 16836 loops = repeats || loops; 16837 16838 // console.log('build', loops); 16839 16840 if( !loops ) { return; } 16841 if( loops > 1 ) { 16842 currentEase = 'linearEase'; 16843 } 16844 16845 // clean before running new build 16846 kill(); 16847 // set new positions 16848 self.setPos(); 16849 16850 var id = self.state.id, 16851 moveForward = self.state.moveForward, 16852 i = 0, 16853 axisMove = (moveForward) ? -100 : 100; 16854
16855 for( ; i <= perView; i += 1 ) { 16856 slideId = ( (moveForward) ? i + id : i + id - 1 ) % len; 16857 slideId = ( slideId < 0 ) ? len + slideId : slideId; 16858 16859 if( i === 0 ) { 16860 style = moveForward ? animPrev : animActi; 16861 } else if( i === perView ) { 16862 style = moveForward ? animActi : animNext; 16863 } else { 16864 style = animActi; 16865 } 16866 16867 zIFlow = (self.state.moveForward) ? animNext.zIndex : animPrev.zIndex; 16868 if( zIFlow ) { 16869 // console.log( zIFlow ); 16870 self.slides[ slideId ].style.zIndex = (zIFlow === '+') ? i + 1 : len - i; 16871 } 16872 16873 style.transform[ 'translate' + animAxis ] = axisMove; 16874 add( self.slides[ slideId ], style, currentEase ); 16875 } 16876 }; 16877 16878 add = function( slide, toStyles, ease ) { 16879 if( typeof slide === 'undefined' ) { 16880 throw 'Add at least one slide'; 16881 } 16882 16883 var fromStyles = slide.style, 16884 style = self.refStyle( toStyles, fromStyles ); 16885 16886 animLoop.push( [slide, style, ease] ); 16887 aL += 1; 16888 }; 16889 16890 move = function( startProg, mode ) { 16891 if (isTest) return; 16892 var currentTotalIter = totalIter; 16893 16894 if( loops > 1 ) { 16895 currentTotalIter = totalIter / 5; 16896 } 16897 16898 if( !self.state.running ) { self.state.running = true; } 16899 16900 if( startProg ) { 16901 // update iteration val to cached outside var 16902 // ceil to handle properly play after mouse up / touch end 16903 iteration = Math.ceil(startProg * currentTotalIter); 16904 } 16905 16906 timeProg = iteration / currentTotalIter; 16907 progress( timeProg ); 16908 16909 // Break loop 16910 if( iteration >= currentTotalIter && mode === 'play' || 16911 iteration <= 0 && mode === 'reverse' ) { 16912 16913 self.state.running = false; 16914 iteration = 0; 16915 kill(); 16916 self.updateId( self.state.moveForward ? 1 : -1 ); 16917 // If we're creating multiple animation loop we trigger outside only first pass to start all game. 16918 // the rest are triggered as callback 16919 loops -= 1; 16920 if( loops > 0 ) { 16921 build(); 16922 play(); 16923 } 16924 16925 // if we run all loops reset back the default value 16926 if( !loops ) { 16927 loops = 1; 16928 self.timerRemaining = parseInt(self.config.displayTime); 16929 self.config.onAfterSlide( self.state.id ); 16930 } 16931 16932 return; 16933 } 16934 16935 // Run in given mode 16936 if( mode === 'play') { 16937 iteration += 1; 16938 } else { 16939 iteration -= 1; 16940 } 16941 16942 requestAnimationFrame( function() { 16943 if(self.state.stop) return; 16944 move( 0, mode ); 16945 }); 16946 }; 16947 16948 play = function( startProg ) { 16949 16950 var $nextSlide = $(self.slides[ self.nextId(self.state.moveForward ? 1 : -1) ] ); 16951 16952 // If it's Edge Slider and Lazy Load is enabled and It's not a Video Slide 16953 if ( self.config.lazyload && self.config.edgeSlider ) { 16954 16955 // Set the next slide's BG Image 16956 var $slideImg = $nextSlide.find('[data-mk-img-set]'); 16957 if ( $slideImg.length ) { 16958 MK.component.BackgroundImageSetter.init( $slideImg ); 16959 } 16960 16961 } 16962 16963 self.config.onBeforeSlide( self.nextId(self.state.moveForward ? 1 : -1) ); 16964 var start = startProg || 0; 16965 iteration = 0; 16966 self.state.stop = false; 16967 move( start, 'play' ); 16968 16969 }; 16970 16971 reverse = function( startProg ) { 16972 var start = startProg || 1; 16973 move( start, 'reverse' ); 16974 }; 16975 16976 progress = function( progVal ) { 16977 var aI = 0, 16978 currentStyle; 16979 16980 for( aI; aI < aL; aI++ ) { 16981 if( progVal !== 1 && progVal !== 0 ) { 16982 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], animLoop[ aI ][ 2 ] ); 16983 } else if( progVal === 1) { 16984 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], 'linearEase' ); 16985 } else if ( progVal === 0 ) { 16986 currentStyle = self.currentStyle( progVal, animLoop[ aI ][ 1 ], 'linearEase' ); 16987 } 16988 self.setStyle( animLoop[ aI ][ 0 ], currentStyle ); 16989 } 16990 }; 16991 16992 // Clear previous loop 16993 kill = function() { 16994 animLoop = []; 16995 aL = 0; 16996 }; 16997 16998 16999 return { 17000 build : build, 17001 add : add, 17002 play : play, 17003 reverse : reverse, 17004 progress : progress 17005 }; 17006 }, 17007 17008 17009 // Build reference styles. 17010 // Return object with array containig initial style and change of its value 17011 // as required for easing functions 17012 refStyle : function( toStyles, fromStyles ) { 17013 var axis = this.animation[ this.config.effect ].axis, 17014 style = {}, 17015 initVal, changeVal, endVal, dynamicEnd, styleProp, transProp, transform; 17016 17017 for( styleProp in toStyles ) { 17018 17019 if( styleProp === 'transform' ) { 17020 transform = this.getTransforms( fromStyles ); 17021 style.transform = {}; 17022 17023 for( transProp in toStyles.transform ) { 17024 // don't care about z 17025 if( transProp === 'translateZ' ) { continue; } 17026 17027 initVal = transform[ transProp ] || 0; // if it is undefined it means it's 0 17028 dynamicEnd = ( transProp === 'translate' + axis ) ? initVal : 0; 17029 endVal = toStyles.transform[ transProp ] + dynamicEnd; // it is dynamic, based on slide position in current set 17030 changeVal = endVal - initVal; 17031 style.transform[ transProp ] = [ initVal, changeVal ]; 17032 } 17033 } else if( styleProp === 'zIndex' ) { 17034 // console.log( 'z' ); 17035 continue; 17036 } else { 17037 initVal = parseFloat( fromStyles[ styleProp ] ) || 0; // if it is undefined it means it's 0 17038 endVal = toStyles[ styleProp ]; 17039 changeVal = endVal - initVal; 17040 style[ styleProp ] = [ initVal, changeVal ]; 17041 } 17042 } 17043 17044 return style; 17045 }, 17046 17047 17048 currentStyle : function( progress, style, ease ) { 17049 var self = this, 17050 currentStyle = {}, 17051 currentVals, styleProp, transProp, prog; 17052 17053 // Redo same loop but construct currentStyle object out of cached values 17054 for( styleProp in style ) { 17055 17056 if( styleProp === 'transform' ) { 17057 currentStyle.transform = {}; 17058 17059 for( transProp in style.transform ) { 17060 // remove this line. double check first if needed by logging 17061 if( transProp === 'translateZ' ) { continue; } 17062 17063 currentVals = style.transform[ transProp ]; 17064 currentStyle.transform[ transProp ] = 17065 // (currentIteration, startValue, changeInValue, totalIterations) 17066 self.ease[ ease ]( progress, currentVals[ 0 ], currentVals[ 1 ], 1 ); 17067 } 17068 } else { 17069 currentVals = style[ styleProp ]; 17070 currentStyle[ styleProp ] = 17071 self.ease[ ease ]( progress, currentVals[ 0 ], currentVals[ 1 ], 1 ); 17072 } 17073 } 17074 17075 return currentStyle; 17076 }, 17077 17078 17079 setActive : function( id ) { 17080 var $slides = $( this.slides ), 17081 className = this.config.activeClass; 17082 17083 $slides.removeClass( className ); 17084 17085 if( this.config.hasPagination ) { 17086 var $pagination = this.$pagination.find( 'a' ); 17087 $pagination.removeClass( className ); 17088 $pagination.eq( id ).addClass( className ); 17089 } 17090 17091 if( this.activeTimer ) { 17092 clearTimeout( this.activeTimer ); 17093 if ( this.imageLoader ) { 17094 this.imageLoader.abort(); 17095 } 17096 } 17097 17098 17099 var self = this; 17100 17101 this.activeTimer = setTimeout( function() { 17102 17103 var $currentSlide = $slides.eq( id ); 17104 17105 if ( self.config.lazyload && self.config.edgeSlider ) { // If it's Edge Slider and Lazy Load is enabled 17106
17107 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 17108 17109 var imgSet = $currentSlide.children('.mk-video-section-touch').data('mk-img-set'); 17110 var exactImg = MK.component.BackgroundImageSetter.getImage( imgSet ); 17111 var $bgImage = $('<img>').attr('src', exactImg ); 17112 17113 self.imageLoader = imagesLoaded( $bgImage[0], function( instance ) { 17114 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17115 setTimeout( function() { 17116 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17117 }, 200); 17118 $currentSlide.addClass( className ); 17119 }); 17120 17121 } 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 17122 17123 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17124 setTimeout( function() { 17125 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17126 }, 200); 17127 $currentSlide.addClass( className ); 17128 17129 } else { // If it's a Image Slide 17130 17131 if ( $currentSlide.children('[data-mk-img-set]').length ) { 17132 // Get the matching Image URL to start lazy loading 17133 var imgSet = $currentSlide.children('[data-mk-img-set]').data('mk-img-set'); 17134 var exactImg = MK.component.BackgroundImageSetter.getImage( imgSet ); 17135 var $bgImage = $('<img>').attr('src', exactImg ); 17136 17137 // Prevent counting time on slide until the image loads 17138 self.unsetTimer(); 17139 self.imageLoader = imagesLoaded( $bgImage[0], function( instance ) { 17140 // Hide spinner, Continue counting time on slide and show the content 17141 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17142 setTimeout( function() { 17143 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17144 }, 200); 17145 self.setTimer(false, false, $currentSlide.data('timer') || Number(self.config.displayTime) ); 17146 $currentSlide.addClass( className ); 17147 }); 17148 } else { 17149 $currentSlide.children('.mk-slider-spinner-wrap').addClass('mk-slider-spinner-wrap-hidden'); 17150 setTimeout( function() { 17151 $currentSlide.children('.mk-slider-spinner-wrap').hide(); 17152 }, 200); 17153 self.setTimer(false, false, $currentSlide.data('timer') || Number(self.config.displayTime) ); 17154 $currentSlide.addClass( className ); 17155 } 17156 17157 } 17158 17159 } else { 17160 17161 $currentSlide.addClass( className ); 17162 17163 } 17164 17165 17166 }, this.config.transitionTime ); 17167 }, 17168 17169 createTimer : function() { 17170 17171 var $slide = $(this), 17172 video = $slide.find('video').get(0); 17173 17174 if(video) { 17175 // A hacky but reliable way to ge the video duration 17176 var interval = setInterval( function() { 17177 // If the metadata is ready 17178 if ( video.readyState > 0 ) { 17179 $slide.data('timer', (video.duration * 1000)); 17180 $slide.attr('data-timer', (video.duration * 1000)); 17181 clearInterval(interval); 17182 } 17183 }, 100); 17184 } 17185 17186 }, 17187 17188 setTimer : function( isFirst, isPaused, fixed_time ) { 17189 // check for custom timer 17190 var customTimer = this.$slides.eq(this.nextId(this.state.moveForward ? 1 : -1)).data('timer'), 17191 trans = parseInt( this.config.transitionTime ), 17192 interval = customTimer ? customTimer : parseInt( this.config.displayTime ), 17193 timer = interval + trans; 17194 17195 var self = this, 17196 first = isFirst || true, 17197 fixed_time = fixed_time || 0, 17198 create, run; 17199 17200 this.timer = true; 17201 this.lastSetTimer = Date.now(); 17202 17203 create = function() { 17204 17205 if( self.autoplay ) { clearTimeout( self.autoplay ); } 17206 if( !self.timer ) { 17207 return; 17208 } 17209 self.state.moveForward = true; 17210 self.timeline.build(); 17211 self.timeline.play(); 17212 self.setActive( self.nextId( 1 ) ); 17213 if( self.config.fluidHeight ) { self.setHeight( self.nextId( 1 ) ); } 17214 first = false; 17215 self.lastSetTimer = Date.now(); 17216 17217 run(); 17218 }; 17219 17220 run = function(newInterval) { 17221 // check for custom timer 17222 customTimer = self.$slides.eq(self.nextId(self.state.moveForward ? 1 : -1)).data('timer'); 17223 interval = customTimer ? customTimer : parseInt( self.config.displayTime ); 17224 timer = interval + trans; // update timer with current val 17225 17226 var time = newInterval || timer; 17227 self.autoplay = setTimeout( create, time ); 17228 }; 17229 17230 if ( fixed_time ) { 17231 run( fixed_time ); 17232 } else if (isPaused) { 17233 run( this.timerRemaining ); 17234 } else { 17235 run(); 17236 } 17237 }, 17238 17239 17240 unsetTimer : function() { 17241 this.timer = false;
17242 this.lastUnsetTimer = Date.now(); 17243 this.timerRemaining -= this.lastUnsetTimer - this.lastSetTimer; 17244 if( this.autoplay ) { clearTimeout( this.autoplay ); } 17245 }, 17246 17247 17248 buildPagination : function() { 17249 var i = 0, 17250 len = this.slides.length, 17251 tpl = ''; 17252 17253 for( ; i < len; i += 1 ) { 17254 tpl += '<a href="javascript:;">' + this.config.paginationTpl + '</a>'; 17255 } 17256 17257 this.$pagination.html( tpl ); 17258 this.setActive( 0 ); 17259 }, 17260 17261 17262 getSlideSize : function() { 17263 this.slideSize = { 17264 X: 100 / this.config.slidesPerView, 17265 Y: 100 / this.config.slidesPerView 17266 }; 17267 }, 17268 17269 17270 getTransforms : function( style ) { 17271 // console.log( style ); 17272 var transform = style.transform || style.webkitTransform || style.mozTransform, 17273 regex = /(\w+)\(([^)]*)\)/g, 17274 match, 17275 T = {}; 17276 17277 if( typeof transform !== 'string' ) { 17278 throw 'Transform prop is not a string.'; 17279 } 17280 17281 if( !transform ) { return; } 17282 17283 // Run regex assignment 17284 while( match = regex.exec( transform ) ) { 17285 T[ match[ 1 ] ] = parseFloat( match[ 2 ] ); 17286 } 17287 17288 return T; 17289 }, 17290 17291 isNode : function( o ) { 17292 return ( 17293 typeof Node === "object" ? o instanceof Node : 17294 o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string" 17295 ); 17296 }, 17297 17298 17299 dragHandler : function() { 17300 var self = this, 17301 $container = $( this.container ), 17302 prevBuild = false, 17303 nextBuild = false, 17304 dragging = false, 17305 buffor = 5, // helpful for decoupling with click events 17306 dragStart, dragMove, dragEnd, progress; 17307 17308 progress = function( moveX ) { 17309 return moveX / self.val.viewportW(); 17310 }; 17311 17312 dragStart = function( moveX, startX ) { 17313 // console.log( 'start', moveX, startX ); 17314 }; 17315 17316 dragMove = function( moveX ) { 17317 // console.log('move'); 17318 if( self.state.running ) return; 17319 17320 // Don't need to check for existance here 17321 17322 if( moveX < -buffor ) { 17323 17324 if( !nextBuild ) { 17325 self.state.moveForward = true; 17326 self.timeline.build(); 17327 nextBuild = true; 17328 prevBuild = false; 17329 self.unsetTimer(); 17330 } else { 17331 // turn progress into positive val 17332 self.timeline.progress( -progress( moveX ) ); 17333 } 17334 dragging = true; 17335 } else if( moveX > buffor ) { 17336 17337 if( !prevBuild ) { 17338 self.state.moveForward = false; 17339 self.timeline.build(); 17340 prevBuild = true; 17341 nextBuild = false; 17342 self.unsetTimer(); 17343 } else { 17344 self.timeline.progress( progress( moveX ) ); 17345 } 17346 dragging = true; 17347 } 17348 }; 17349 17350 dragEnd = function( moveX ) { 17351 if( dragging ) { 17352 var prog = progress( moveX ), 17353 absProg = prog < 0 ? -prog : prog; 17354 17355 if( absProg > 0.1 ) { 17356 self.timeline.play( absProg ); 17357 self.setActive( self.nextId( prog < 0 ? 1 : -1 ) ); 17358 if( self.config.fluidHeight ) { self.setHeight( self.nextId( prog < 0 ? 1 : -1 ) ); } 17359 } else { 17360 self.timeline.reverse( absProg ); 17361 // eventually move this to reverse callbacks 17362 if(prog < 0) { 17363 self.updateId( -1 ); 17364 } else { 17365 self.updateId( 1 ); 17366 } 17367 } 17368 17369 prevBuild = false; 17370 nextBuild = false; 17371 dragging = false; 17372 if( self.config.autoplay ) { self.setTimer( false ); } 17373 } 17374 }; 17375 17376 this.drag( $container, dragStart, dragMove, dragEnd ); 17377 }, 17378 17379 17380 drag : function( $el, startFn, moveFn, stopFn ) { 17381 17382 var touchX, touchY, movX, movY, go, evt, 17383 prevent, start, move, stop; 17384 17385 prevent = function( e ) { 17386 e.preventDefault(); 17387 }; 17388 17389 start = function( e ) { 17390 // $el.on("touchmove", prevent); 17391 $el.on("mousemove", prevent); 17392 $el.on("touchmove", move); 17393 $el.on("mousemove", move); 17394 17395 evt = (e.type === 'touchstart') ? e.originalEvent.touches[0] : e; 17396 touchX = evt.pageX; 17397 17398 if(typeof startFn === 'function') { 17399 startFn(movX, touchX); 17400 } 17401 }; 17402 17403 move = function( e ) { 17404 evt = (e.type === 'touchmove') ? e.originalEvent.touches[0] : e; 17405 movX = evt.pageX - touchX; 17406 17407 if(typeof moveFn === 'function') { 17408 moveFn(movX); 17409 } 17410 }; 17411 17412 stop = function( e ) { 17413 // $el.off("touchmove", prevent); 17414 $el.off("mousemove", prevent); 17415 $el.off("touchmove", move); 17416 $el.off("mousemove", move); 17417 17418 if(typeof stopFn === 'function') { 17419 stopFn(movX); 17420 } 17421 }; 17422 17423 $el.on("touchstart", start); 17424 $el.on("mousedown", start); 17425 $el.on("touchend", stop); 17426 $el.on("touchleave", stop); 17427 $el.on("touchcancel", stop);
17428 $el.on("mouseup", stop); 17429 $el.on("mouseleave", stop); 17430 }, 17431 17432 17433 dynamicVal : function() { 17434 var $window = $( window ), 17435 update, 17436 getViewportW, viewportW; 17437 17438 update = function() { 17439 viewportW = $window.width(); 17440 }; 17441 17442 getViewportW = function() { 17443 return viewportW; 17444 }; 17445 17446 update(); 17447 $window.on( 'load', update ); 17448 $window.on( 'resize', update ); 17449 17450 return { 17451 viewportW : getViewportW 17452 }; 17453 } 17454 }; 17455 17456 17457 17458 // 17459 // Set of default animations 17460 // 17461 // ///////////////////////////////////////////////////////// 17462 17463 MK.ui.Slider.prototype.animation = { 17464 17465 slide : { 17466 axis : 'X', 17467 next : { transform: {} }, 17468 active : { transform: {} }, 17469 prev : { transform: {} } 17470 }, 17471 17472 vertical_slide : { 17473 axis : 'Y', 17474 next : { transform: {} }, 17475 active : { transform: {} }, 17476 prev : { transform: {} } 17477 }, 17478 17479 perspective_flip : { 17480 axis : 'Y', 17481 next : { 17482 transform: { 17483 rotateX : 80 17484 } 17485 }, 17486 active : { 17487 transform: { 17488 rotateX : 0 17489 } 17490 }, 17491 prev : { 17492 transform: { 17493 rotateX : 0 17494 } 17495 } 17496 }, 17497 17498 zoom : { 17499 axis : 'Z', 17500 next: { 17501 opacity : 0, 17502 transform : { 17503 scale : 0.9 17504 } 17505 }, 17506 active: { 17507 opacity : 1, 17508 transform : { 17509 scale : 1 17510 } 17511 }, 17512 prev: { 17513 opacity : 0, 17514 transform : { 17515 scale : 1.1 17516 } 17517 } 17518 }, 17519 17520 fade : { 17521 axis : 'Z', 17522 next: { 17523 opacity : 0, 17524 transform : {} 17525 }, 17526 active: { 17527 opacity : 1, 17528 transform : {} 17529 }, 17530 prev: { 17531 opacity : 0, 17532 transform : {} 17533 } 17534 }, 17535 17536 kenburned : { 17537 axis : 'Z', 17538 next: { 17539 opacity : 0, 17540 transform : {} 17541 }, 17542 active: { 17543 opacity : 1, 17544 transform : {} 17545 }, 17546 prev: { 17547 opacity : 0, 17548 transform : {} 17549 } 17550 }, 17551 17552 zoom_out : { 17553 axis : 'Z', 17554 next: { 17555 zIndex : '+', 17556 opacity : 1, 17557 transform : { 17558 translateY : 100, 17559 scale : 1 17560 } 17561 }, 17562 active: { 17563 opacity : 1, 17564 transform : { 17565 translateY : 0, 17566 scale : 1 17567 } 17568 }, 17569 prev: { 17570 zIndex : '+', 17571 opacity : 0, 17572 transform : { 17573 translateY : 0, 17574 scale : 0.5 17575 } 17576 } 17577 }, 17578 17579 // Problem with Z-Flow 17580 horizontal_curtain : { 17581 axis : 'Z', 17582 next: { 17583 zIndex : '+', 17584 transform : { 17585 translateX : 100, 17586 } 17587 }, 17588 active: { 17589 transform : { 17590 translateX : 0, 17591 } 17592 }, 17593 prev: { 17594 zIndex : '+', 17595 transform : { 17596 translateX : -70, 17597 } 17598 } 17599 }, 17600 17601 roulete : { 17602 axis : 'X', 17603 next: { 17604 opacity : 0.5, 17605 transform : { 17606 scale : 0.5, 17607 rotate : 10, 17608 translateY : 20 17609 } 17610 }, 17611 active: { 17612 opacity : 1, 17613 transform : { 17614 scale : 1, 17615 rotate : 0, 17616 translateY : 0 17617 } 17618 }, 17619 prev: { 17620 opacity : 0.3, 17621 transform : { 17622 scale : 0.5, 17623 rotate : -10, 17624 translateY : 20 17625 } 17626 } 17627 } 17628 }; 17629 17630 17631 17632 // 17633 // Penner's easing library 17634 // 17635 // ///////////////////////////////////////////////////////// 17636 17637 MK.ui.Slider.prototype.ease = { 17638 /* 17639 *
17640 * TERMS OF USE - EASING EQUATIONS 17641 * 17642 * Open source under the BSD License. 17643 * 17644 * Copyright © 2001 Robert Penner 17645 * All rights reserved. 17646 * 17647 * Redistribution and use in source and binary forms, with or without modification, 17648 * are permitted provided that the following conditions are met: 17649 * 17650 * Redistributions of source code must retain the above copyright notice, this list of 17651 * conditions and the following disclaimer. 17652 * Redistributions in binary form must reproduce the above copyright notice, this list 17653 * of conditions and the following disclaimer in the documentation and/or other materials 17654 * provided with the distribution. 17655 * 17656 * Neither the name of the author nor the names of contributors may be used to endorse 17657 * or promote products derived from this software without specific prior written permission. 17658 * 17659 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17660 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17661 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 17662 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17663 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 17664 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 17665 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 17666 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 17667 * OF THE POSSIBILITY OF SUCH DAMAGE. 17668 * 17669 */ 17670 linearEase : function(currentIteration, startValue, changeInValue, totalIterations) { 17671 return changeInValue * currentIteration / totalIterations + startValue; 17672 }, 17673 17674 easeInQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17675 return changeInValue * (currentIteration /= totalIterations) * currentIteration + startValue; 17676 }, 17677 17678 easeOutQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17679 return -changeInValue * (currentIteration /= totalIterations) * (currentIteration - 2) + startValue; 17680 }, 17681 17682 easeInOutQuad : function(currentIteration, startValue, changeInValue, totalIterations) { 17683 if ((currentIteration /= totalIterations / 2) < 1) { 17684 return changeInValue / 2 * currentIteration * currentIteration + startValue; 17685 } 17686 return -changeInValue / 2 * ((--currentIteration) * (currentIteration - 2) - 1) + startValue; 17687 }, 17688 17689 easeInCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17690 return changeInValue * Math.pow(currentIteration / totalIterations, 3) + startValue; 17691 }, 17692 17693 easeOutCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17694 return changeInValue * (Math.pow(currentIteration / totalIterations - 1, 3) + 1) + startValue; 17695 }, 17696 17697 easeInOutCubic : function(currentIteration, startValue, changeInValue, totalIterations) { 17698 if ((currentIteration /= totalIterations / 2) < 1) { 17699 return changeInValue / 2 * Math.pow(currentIteration, 3) + startValue; 17700 } 17701 return changeInValue / 2 * (Math.pow(currentIteration - 2, 3) + 2) + startValue; 17702 }, 17703 17704 easeInQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17705 return changeInValue * Math.pow (currentIteration / totalIterations, 4) + startValue; 17706 }, 17707 17708 easeOutQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17709 return -changeInValue * (Math.pow(currentIteration / totalIterations - 1, 4) - 1) + startValue; 17710 }, 17711 17712 easeInOutQuart : function(currentIteration, startValue, changeInValue, totalIterations) { 17713 if ((currentIteration /= totalIterations / 2) < 1) { 17714 return changeInValue / 2 * Math.pow(currentIteration, 4) + startValue; 17715 } 17716 return -changeInValue/2 * (Math.pow(currentIteration - 2, 4) - 2) + startValue; 17717 }, 17718 17719 easeInQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17720 return changeInValue * Math.pow (currentIteration / totalIterations, 5) + startValue; 17721 }, 17722 17723 easeOutQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17724 return changeInValue * (Math.pow(currentIteration / totalIterations - 1, 5) + 1) + startValue; 17725 }, 17726 17727 easeInOutQuint : function(currentIteration, startValue, changeInValue, totalIterations) { 17728 if ((currentIteration /= totalIterations / 2) < 1) { 17729 return changeInValue / 2 * Math.pow(currentIteration, 5) + startValue; 17730 } 17731 return changeInValue / 2 * (Math.pow(currentIteration - 2, 5) + 2) + startValue; 17732 }, 17733 17734 easeInSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17735 return changeInValue * (1 - Math.cos(currentIteration / totalIterations * (Math.PI / 2))) + startValue; 17736 }, 17737 17738 easeOutSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17739 return changeInValue * Math.sin(currentIteration / totalIterations * (Math.PI / 2)) + startValue; 17740 }, 17741 17742 easeInOutSine : function(currentIteration, startValue, changeInValue, totalIterations) { 17743 return changeInValue / 2 * (1 - Math.cos(Math.PI * currentIteration / totalIterations)) + startValue; 17744 }, 17745 17746 easeInExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17747 return changeInValue * Math.pow(2, 10 * (currentIteration / totalIterations - 1)) + startValue; 17748 }, 17749 17750 easeOutExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17751 return changeInValue * (-Math.pow(2, -10 * currentIteration / totalIterations) + 1) + startValue; 17752 }, 17753 17754 easeInOutExpo : function(currentIteration, startValue, changeInValue, totalIterations) { 17755 if ((currentIteration /= totalIterations / 2) < 1) { 17756 return changeInValue / 2 * Math.pow(2, 10 * (currentIteration - 1)) + startValue; 17757 } 17758 return changeInValue / 2 * (-Math.pow(2, -10 * --currentIteration) + 2) + startValue; 17759 }, 17760 17761 easeInCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17762 return changeInValue * (1 - Math.sqrt(1 - (currentIteration /= totalIterations) * currentIteration)) + startValue; 17763 }, 17764 17765 easeOutCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17766 return changeInValue * Math.sqrt(1 - (currentIteration = currentIteration / totalIterations - 1) * currentIteration) + startValue; 17767 }, 17768 17769 easeInOutCirc : function(currentIteration, startValue, changeInValue, totalIterations) { 17770 if ((currentIteration /= totalIterations / 2) < 1) { 17771 return changeInValue / 2 * (1 - Math.sqrt(1 - currentIteration * currentIteration)) + startValue; 17772 } 17773 return changeInValue / 2 * (Math.sqrt(1 - (currentIteration -= 2) * currentIteration) + 1) + startValue; 17774 } 17775 }; 17776 17777})(jQuery); 17778/* Social Share */ 17779/* -------------------------------------------------------------------- */ 17780(function($) { 17781 'use strict'; 17782 17783 MK.component.SocialShare = function( el ) { 17784 var networks = { 17785 twitter : 'http://twitter.com/intent/tweet?text={title}&url={url}', 17786 pinterest : 'http://pinterest.com/pin/create/button/?url={url}&media={image}&description={title}', 17787 facebook : 'https://www.facebook.com/sharer/sharer.php?u={url}', 17788 googleplus : 'https://plus.google.com/share?url={url}', 17789 linkedin : 'http://www.linkedin.com/shareArticle?mini=true&url={url}&title={title}&summary={desc}', 17790 digg : 'http://digg.com/submit?url={url}&title={title}', 17791 reddit : 'http://reddit.com/submit?url={url}&title={title}', 17792 }; 17793 17794 this.networks = networks; 17795 this.el = el; 17796 }; 17797 17798
17799 MK.component.SocialShare.prototype = { 17800 17801 init : function() { 17802 this.cacheElements(); 17803 this.bindEvents(); 17804 }, 17805 17806 cacheElements : function() { 17807 this.$this = $( this.el ); 17808 17809 17810 }, 17811 17812 bindEvents : function() { 17813 var thisObject = this; 17814 var tempClass = ""; 17815 $.each(this.networks, function( key, value ) { 17816 thisObject.$tempClass = $('.' + key + '-share'); 17817 thisObject.$tempClass.click(thisObject.openSharingDialog.bind(self, this, key)); 17818 17819 }); 17820 }, 17821 17822 openSharingDialog : function(url, site, args) { 17823 var urlWrapper = url; 17824 var rx = new RegExp('\{[a-z]*\}','g'), res; 17825 var match = rx.exec(url); 17826 while (match != null) { 17827 var pureAttr = match[0].replace("{", "").replace("}" , ""); 17828 var attValue = $(args.currentTarget).attr('data-' + pureAttr); 17829 if (attValue === undefined || attValue === null) { 17830 attValue = ""; 17831 } 17832 attValue = attValue.replace('#', '%23'); // fix for twitter description 17833 urlWrapper = urlWrapper.replace(match, attValue); 17834 match = rx.exec(url); 17835 } 17836 17837 window.open(urlWrapper, site + "Window", "height=320,width=660,resizable=0,toolbar=0,menubar=0,status=0,location=0,scrollbars=0"); 17838 17839 }, 17840 17841 17842 }; 17843 17844 17845 ////////////////////////////////////////// 17846 // 17847 // Apply to: 17848 // 17849 // /////////////////////////////////////// 17850 17851 var $body = $('body'); 17852 17853 if(!$body.length) return; 17854 17855 $body.each(function() { 17856 var socialShare = new MK.component.SocialShare(this); 17857 socialShare.init(); 17858 }); 17859 17860})(jQuery); 17861 17862 17863 17864//function mk_social_share() { 17865// 17866// "use strict";
17867// 17868// $('.twitter-share').on('click', function () { 17869// var $url = $(this).attr('data-url'), 17870// $title = $(this).attr('data-title'); 17871// 17872// 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"); 17873// return false; 17874// }); 17875// 17876// $('.pinterest-share').on('click', function () { 17877// var $url = $(this).attr('data-url'), 17878// $title = $(this).attr('data-title'), 17879// $image = $(this).attr('data-image'); 17880// 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"); 17881// return false; 17882// }); 17883// 17884// $('.facebook-share').on('click', function () { 17885// var $url = $(this).attr('data-url'); 17886// 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"); 17887// return false; 17888// }); 17889// 17890// $('.googleplus-share').on('click', function () { 17891// var $url = $(this).attr('data-url'); 17892// 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"); 17893// return false; 17894// }); 17895// 17896// $('.linkedin-share').on('click', function () { 17897// var $url = $(this).attr('data-url'), 17898// $title = $(this).attr('data-title'), 17899// $desc = $(this).attr('data-desc'); 17900// 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"); 17901// return false; 17902// }); 17903//} 17904 17905 17906 17907 17908(function($) { 17909 'use strict'; 17910 17911 MK.component.Sortable = function(el) { 17912 this.el = el; 17913 }; 17914 17915 MK.component.Sortable.prototype = { 17916 init: function init() { 17917 this.cacheElements(); 17918 this.bindEvents(); 17919 }, 17920 17921 cacheElements: function cacheElements() { 17922 this.unique = Date.now(); 17923 this.$filter = $(this.el); 17924 this.config = this.$filter.data('sortable-config'); 17925 17926 this.ajaxLoader = new MK.utils.ajaxLoader(this.config.container); 17927 this.ajaxLoader.init(); 17928 17929 this.$container = $( this.config.container ); 17930 this.$navItems = this.$filter.find('a'); 17931 this.$filterItems = this.$container.find(this.config.item); 17932 }, 17933 17934 bindEvents: function bindEvents() { 17935 this.$navItems.on('click', this.handleClick.bind(this)); 17936 MK.utils.eventManager.subscribe('ajaxLoaded', this.onLoad.bind(this)); 17937 }, 17938 17939 handleClick: function handleClick(e) { 17940 e.preventDefault(); 17941 17942 var $item = $(e.currentTarget); 17943 var term = $item.data('filter'); 17944 17945 this.$navItems.removeClass('current'); 17946 $item.addClass('current'); 17947 17948 if(this.config.mode === 'ajax') this.inDB(term, $item); 17949 else this.inPage(term); 17950 }, 17951 17952 inDB: function inDB(term, $item) { 17953 // Add load indicator only for long requests 17954 MK.ui.loader.remove(this.$filter); 17955 MK.ui.loader.add($item); 17956 17957 // If mk-ajax-loaded-posts span exists and one of the filter is clicked, 17958 // clear post ids 17959 if ( this.$container.siblings('.mk-ajax-loaded-posts').length ) { 17960 this.$container.siblings('.mk-ajax-loaded-posts').attr('data-loop-loaded-posts', ''); 17961 } 17962 17963 this.ajaxLoader.setData({ 17964 paged: 1, 17965 term: term 17966 }); 17967 this.ajaxLoader.load(this.unique); 17968 }, 17969 17970 inPage: function inPage(term) { 17971 var $filterItems = this.$container.find(this.config.item); 17972 $filterItems.removeClass('is-hidden'); // show all first 17973 // Replace all ', ' with ', .'. It's used to add '.' as class selector of each category. 17974 var className = term.replace( /, /g, ", ." ); 17975 if(term !== '*') $filterItems.not( '.' + className ).addClass('is-hidden'); // hide filtered 17976 MK.utils.eventManager.publish('staticFilter'); 17977 }, 17978 17979 onLoad: function onLoad(e, response) { 17980 if(this.config.mode === 'static') { 17981 this.$navItems.removeClass('current').first().addClass('current'); 17982 } 17983 if( typeof response !== 'undefined' && response.id === this.config.container) { 17984 MK.ui.loader.remove(this.$filter); 17985 if(response.unique === this.unique) { 17986 this.$container.html(response.content); 17987 this.ajaxLoader.setData({paged: 1}); 17988 } 17989 } 17990 } 17991 }; 17992 17993})(jQuery); 17994(function($) { 17995 'use strict'; 17996 17997 MK.component.Tabs = function( el ) { 17998 var defaults = { 17999 activeClass : 'is-active' 18000 }; 18001 18002 this.config = defaults; 18003 this.el = el; 18004 }; 18005 18006 MK.component.Tabs.prototype = { 18007 18008 init : function() { 18009 this.cacheElements(); 18010 this.bindEvents(); 18011 }, 18012 18013 cacheElements : function() { 18014 this.$this = $( this.el ); 18015 this.$tabs = this.$this.find( '.mk-tabs-tab' ); 18016 this.$panes = this.$this.find( '.mk-tabs-pane' ); 18017 this.currentId = 0; 18018 }, 18019 18020 bindEvents : function() { 18021 var self = this; 18022 18023 this.$tabs.on( 'click', this.switchPane.bind( this ) ); 18024 }, 18025 18026 switchPane : function( evt ) { 18027 evt.preventDefault(); 18028 18029 var clickedId = $( evt.currentTarget ).index(); 18030 18031 this.hide( this.currentId ); 18032 this.show( clickedId ); 18033 18034 // Update current id 18035 this.currentId = clickedId; 18036 18037 // Notify rest of the app 18038 MK.utils.eventManager.publish('item-expanded'); 18039 }, 18040 18041 show : function( id ) { 18042 this.$tabs.eq( id ).addClass( this.config.activeClass ); 18043 this.$panes.eq( id ).addClass( this.config.activeClass ); 18044 }, 18045 18046 hide : function( id ) { 18047 this.$tabs.eq( id ).removeClass( this.config.activeClass ); 18048 this.$panes.eq( id ).removeClass( this.config.activeClass ); 18049 } 18050 }; 18051 18052})(jQuery); 18053 18054 18055/* Tabs */ 18056/* -------------------------------------------------------------------- */ 18057 18058function mk_tabs() { 18059 18060 // "use strict"; 18061 18062 // if ($.exists('.mk-tabs, .mk-news-tab, .mk-woo-tabs')) { 18063 // $(".mk-tabs, .mk-news-tab, .mk-woo-tabs").tabs(); 18064 18065 // $('.mk-tabs').on('click', function () { 18066 // $('.mk-theme-loop').isotope('layout'); 18067 // }); 18068 18069 // $('.mk-tabs.vertical-style').each(function () { 18070 // $(this).find('.mk-tabs-pane').css('minHeight', $(this).find('.mk-tabs-tabs').height() - 1); 18071 // }); 18072 18073 // } 18074} 18075 18076function mk_tabs_responsive(){ 18077 // $('.mk-tabs, .mk-news-tab').each(function () { 18078 // $this = $(this); 18079 // if ($this.hasClass('mobile-true')) { 18080 // if (window.matchMedia('(max-width: 767px)').matches) 18081 // { 18082 // $this.tabs("destroy"); 18083 // } else { 18084 // $this.tabs(); 18085 // } 18086 // } 18087 // }); 18088 18089} 18090 18091 18092(function($) { 18093 'use strict'; 18094 18095 $(document).on('click', function(e) { 18096 $('.mk-toggle-trigger').removeClass('mk-toggle-active'); 18097 }); 18098 18099 function toggle(e) { 18100 e.preventDefault(); 18101 e.stopPropagation(); 18102 var $this = $(e.currentTarget); 18103 18104 if (!$this.hasClass('mk-toggle-active')) { 18105 18106 $('.mk-box-to-trigger').fadeOut(200); 18107 $this.parent().find('.mk-box-to-trigger').fadeIn(250); 18108 $('.mk-toggle-trigger').removeClass('mk-toggle-active'); 18109 $this.addClass('mk-toggle-active'); 18110 18111 } else { 18112 18113 $('.mk-box-to-trigger').fadeOut(200); 18114 $this.removeClass('mk-toggle-active'); 18115 18116 } 18117 } 18118 18119 function assignToggle() { 18120 // wait for ajax response propagation and insertion 18121 setTimeout(function() { 18122 $('.mk-toggle-trigger').off('click', toggle); 18123 $('.mk-toggle-trigger').on('click', toggle); 18124 }, 100); 18125 } 18126 18127 assignToggle(); 18128 MK.utils.eventManager.subscribe('ajaxLoaded', assignToggle); 18129 MK.utils.eventManager.subscribe('ajax-preview', assignToggle); 18130 18131 $(window).on('vc_reload', function(){ 18132 assignToggle(); 18133 MK.utils.eventManager.subscribe('ajaxLoaded', assignToggle); 18134 MK.utils.eventManager.subscribe('ajax-preview', assignToggle); 18135 }); 18136 18137}(jQuery)); 18138(function( $ ) { 18139 'use strict'; 18140 if( MK.utils.isMobile() ) { 18141 $('.mk-animate-element').removeClass('mk-animate-element'); 18142 return; 18143 }
18144 var init = function init() { 18145 var $rootLevelEls = $('.js-master-row, .widget'); 18146 $rootLevelEls.each( spyViewport ); 18147 $rootLevelEls.each( function rootLevelEl() { 18148 var $animateEl = $(this).find( '.mk-animate-element' ); 18149 $animateEl.each( spyViewport ); 18150 /** 18151 * Firefox has known issue where horizontal scrollbar will appear if an 18152 * element uses animation CSS. The solution should be set the element 18153 * position as fixed or overflow-x as hidden. Position fixed is not possible 18154 * to use because it's only cause other big problems. The best way is 18155 * set overflow-x as hidden in the page content container #theme-page. 18156 * 18157 * NOTE: The problem is spotted on Right To Left viewport only. So, it's 18158 * limited to '.right-to-left' selector only for now to avoid other 18159 * problems. Please extend the functionallity if it's happen in 18160 * other viewport animation effect. 18161 */ 18162 var browserName = MK.utils.browser.name; 18163 if ( browserName === 'Firefox' ) { 18164 var $rightToLeft = $( this ).find( '.right-to-left' ); 18165 if ( $rightToLeft.length > 0 ) { 18166 $( '#theme-page' ).css( 'overflow-x', 'hidden' ); 18167 } 18168 } 18169 }); 18170 }; 18171 var spyViewport = function spyViewport(i) { 18172 var self = this; 18173 MK.utils.scrollSpy( this, { 18174 position : 'bottom', 18175 threshold : 200, 18176 after : function() { 18177 animate.call(self, i); 18178 } 18179 }); 18180 }; 18181 var animate = function animate(i) { 18182 var $this = $(this); 18183 setTimeout(function() { 18184 $this.addClass( 'mk-in-viewport' ); 18185 }, 100 * i); 18186 }; 18187 $(window).on('load vc_reload', init); 18188}(jQuery)); 18189 18190function product_loop_add_cart() { 18191 var $body = $('body'); 18192 $body.on('click', '.add_to_cart_button', function() { 18193 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>'; 18194 var $holder = $(this).parents('.product:eq(0)'); 18195 var $i = $holder.find('.product-loading-icon'); 18196 $holder.addClass('adding-to-cart').removeClass('added-to-cart'); 18197 $i.html(icon); 18198 }); 18199 $body.bind('added_to_cart', function() { 18200 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>'; 18201 var $holder = $('.adding-to-cart'); 18202 var $i = $holder.find('.product-loading-icon'); 18203 $holder.removeClass('adding-to-cart').addClass('added-to-cart'); 18204 $i.html(icon); 18205 }); 18206} 18207(function($) { 18208 'use strict'; 18209 18210 var $iframes = $('iframe'); 18211 18212 $iframes.each(function() { 18213 var $iframe = $(this); 18214 var parent = $iframe.parent().get(0); 18215 var tagName = parent.tagName; 18216 18217 if(tagName === 'P') $iframe.wrap('<div class="mk-video-container"></div>'); 18218 }); 18219 18220}(jQuery)); 18221(function($) { 18222 'use strict'; 18223 18224 /** 18225 * Entry point of application. Runs all components 18226 */ 18227 $( window ).on( 'load', function() { 18228 MK.core.initAll( document ); 18229 MK.utils.scrollToURLHash(); 18230 // TODO move preloader to components and manage it state from within 18231 setTimeout( function() { 18232 MK.ui.preloader.hide(); // site wide 18233 $('.mk-preloader').hide(); // components 18234 $('body').removeClass('loading'); 18235 }, 150 ); 18236 }); 18237 18238 /** 18239 * VC frontend editor. Init all components. 18240 */ 18241 $( window ).on( 'vc_reload', function() { 18242 setTimeout(function(){ 18243 MK.core.initAll( document ); 18244 }, 100); 18245 }); 18246 18247 /** 18248 * Assign global click handlers 18249 */ 18250 $( document ).on( 'click', '.js-smooth-scroll, .js-main-nav a', smoothScrollToAnchor); 18251 $( '.side_dashboard_menu a' ).on( 'click', smoothScrollToAnchor); 18252 18253 function smoothScrollToAnchor( evt ) { 18254 var anchor = MK.utils.detectAnchor( this ); 18255 var $this = $(evt.currentTarget); 18256 var loc = window.location; 18257 var currentPage = loc.origin + loc.pathname; 18258 var href = $this.attr( 'href' ); 18259 var linkSplit = (href) ? href.split( '#' ) : ''; 18260 var hrefPage = linkSplit[0] ? linkSplit[0] : ''; 18261 var hrefHash = linkSplit[1] ? linkSplit[1] : ''; 18262 18263 if( anchor.length ) { 18264 if(hrefPage === currentPage || hrefPage === '') evt.preventDefault(); 18265 MK.utils.scrollToAnchor( anchor ); 18266 18267 } else if( $this.attr( 'href' ) === '#' ) { 18268 evt.preventDefault(); 18269 } 18270 } 18271 18272}(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.