1(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 2/*! 3 * Cropper.js v0.7.0 4 * https://github.com/fengyuanchen/cropperjs 5 * 6 * Copyright (c) 2015-2016 Fengyuan Chen 7 * Released under the MIT license 8 * 9 * Date: 2016-03-20T06:15:36.234Z 10 */ 11 12(function (global, factory) { 13 if (typeof module === 'object' && typeof module.exports === 'object') { 14 module.exports = global.document ? factory(global, true) : function (window) { 15 if (!window.document) { 16 throw new Error('Cropper requires a window with a document'); 17 } 18 19 return factory(window); 20 }; 21 } else { 22 factory(global); 23 } 24})(typeof window !== 'undefined' ? window : this, function (window, noGlobal) { 25 26 'use strict'; 27 28 // Globals 29 var document = window.document; 30 var location = window.location; 31 var navigator = window.navigator; 32 var ArrayBuffer = window.ArrayBuffer; 33 var Object = window.Object; 34 var Array = window.Array; 35 var String = window.String; 36 var Number = window.Number; 37 var Math = window.Math; 38 39 // Constants 40 var NAMESPACE = 'cropper'; 41 42 // Classes 43 var CLASS_MODAL = NAMESPACE + '-modal'; 44 var CLASS_HIDE = NAMESPACE + '-hide'; 45 var CLASS_HIDDEN = NAMESPACE + '-hidden'; 46 var CLASS_INVISIBLE = NAMESPACE + '-invisible'; 47 var CLASS_MOVE = NAMESPACE + '-move'; 48 var CLASS_CROP = NAMESPACE + '-crop'; 49 var CLASS_DISABLED = NAMESPACE + '-disabled'; 50 var CLASS_BG = NAMESPACE + '-bg'; 51 52 // Events 53 var EVENT_MOUSE_DOWN = 'mousedown touchstart pointerdown MSPointerDown'; 54 var EVENT_MOUSE_MOVE = 'mousemove touchmove pointermove MSPointerMove'; 55 var EVENT_MOUSE_UP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel'; 56 var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll'; 57 var EVENT_DBLCLICK = 'dblclick'; 58 var EVENT_RESIZE = 'resize'; 59 var EVENT_ERROR = 'error'; 60 var EVENT_LOAD = 'load'; 61 var EVENT_BUILD = 'build'; 62 var EVENT_BUILT = 'built'; 63 var EVENT_CROP_START = 'cropstart'; 64 var EVENT_CROP_MOVE = 'cropmove'; 65 var EVENT_CROP_END = 'cropend'; 66 var EVENT_CROP = 'crop'; 67 var EVENT_ZOOM = 'zoom'; 68 69 // RegExps 70 var REGEXP_ACTIONS = /e|w|s|n|se|sw|ne|nw|all|crop|move|zoom/; 71 var REGEXP_SUFFIX = /width|height|left|top|marginLeft|marginTop/; 72 var REGEXP_ORIGINS = /^(https?:)\/\/([^\:\/\?#]+):?(\d*)/i; 73 var REGEXP_TRIM = /^\s+(.*)\s+$/; 74 var REGEXP_SPACES = /\s+/; 75 var REGEXP_DATA_URL = /^data\:/; 76 var REGEXP_DATA_URL_HEAD = /^data\:([^\;]+)\;base64,/; 77 var REGEXP_DATA_URL_JPEG = /^data\:image\/jpeg.*;base64,/; 78 79 // Data 80 var DATA_PREVIEW = 'preview'; 81 var DATA_ACTION = 'action'; 82 83 // Actions 84 var ACTION_EAST = 'e'; 85 var ACTION_WEST = 'w'; 86 var ACTION_SOUTH = 's'; 87 var ACTION_NORTH = 'n'; 88 var ACTION_SOUTH_EAST = 'se'; 89 var ACTION_SOUTH_WEST = 'sw'; 90 var ACTION_NORTH_EAST = 'ne'; 91 var ACTION_NORTH_WEST = 'nw'; 92 var ACTION_ALL = 'all'; 93 var ACTION_CROP = 'crop'; 94 var ACTION_MOVE = 'move'; 95 var ACTION_ZOOM = 'zoom'; 96 var ACTION_NONE = 'none'; 97 98 // Supports 99 var SUPPORT_CANVAS = !!document.createElement('canvas').getContext; 100 var IS_SAFARI = navigator && /safari/i.test(navigator.userAgent) && /apple computer/i.test(navigator.vendor); 101 102 // Maths 103 var min = Math.min; 104 var max = Math.max; 105 var abs = Math.abs; 106 var sin = Math.sin; 107 var cos = Math.cos; 108 var sqrt = Math.sqrt; 109 var round = Math.round; 110 var floor = Math.floor; 111 var PI = Math.PI; 112 113 // Utilities 114 var objectProto = Object.prototype; 115 var toString = objectProto.toString; 116 var hasOwnProperty = objectProto.hasOwnProperty; 117 var slice = Array.prototype.slice; 118 var fromCharCode = String.fromCharCode; 119 120 function typeOf(obj) { 121 return toString.call(obj).slice(8, -1).toLowerCase(); 122 } 123 124 function isNumber(num) { 125 return typeof num === 'number' && !isNaN(num); 126 } 127 128 function isUndefined(obj) { 129 return typeof obj === 'undefined'; 130 } 131 132 function isObject(obj) { 133 return typeof obj === 'object' && obj !== null; 134 } 135 136 function isPlainObject(obj) { 137 var constructor; 138 var prototype; 139 140 if (!isObject(obj)) { 141 return false;
142 } 143 144 try { 145 constructor = obj.constructor; 146 prototype = constructor.prototype; 147 148 return constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf'); 149 } catch (e) { 150 return false; 151 } 152 } 153 154 function isFunction(fn) { 155 return typeOf(fn) === 'function'; 156 } 157 158 function isArray(arr) { 159 return Array.isArray ? Array.isArray(arr) : typeOf(arr) === 'array'; 160 } 161 162 function toArray(obj, offset) { 163 offset = offset >= 0 ? offset : 0; 164 165 if (Array.from) { 166 return Array.from(obj).slice(offset); 167 } 168 169 return slice.call(obj, offset); 170 } 171 172 function trim(str) { 173 if (typeof str === 'string') { 174 str = str.trim ? str.trim() : str.replace(REGEXP_TRIM, '$1'); 175 } 176 177 return str; 178 } 179 180 function each(obj, callback) { 181 var length; 182 var i; 183 184 if (obj && isFunction(callback)) { 185 if (isArray(obj) || isNumber(obj.length)/* array-like */) { 186 for (i = 0, length = obj.length; i < length; i++) { 187 if (callback.call(obj, obj[i], i, obj) === false) { 188 break; 189 } 190 } 191 } else if (isObject(obj)) { 192 for (i in obj) { 193 if (obj.hasOwnProperty(i)) { 194 if (callback.call(obj, obj[i], i, obj) === false) { 195 break; 196 } 197 } 198 } 199 } 200 } 201 202 return obj; 203 } 204 205 function extend(obj) { 206 var args; 207 208 if (arguments.length > 1) { 209 args = toArray(arguments); 210 211 if (Object.assign) { 212 return Object.assign.apply(Object, args); 213 } 214 215 args.shift(); 216 217 each(args, function (arg) { 218 each(arg, function (prop, i) { 219 obj[i] = prop; 220 }); 221 }); 222 } 223 224 return obj; 225 } 226 227 function proxy(fn, context) { 228 var args = toArray(arguments, 2); 229 230 return function () { 231 return fn.apply(context, args.concat(toArray(arguments))); 232 }; 233 } 234 235 function setStyle(element, styles) { 236 var style = element.style; 237 238 each(styles, function (value, property) { 239 if (REGEXP_SUFFIX.test(property) && isNumber(value)) { 240 value += 'px'; 241 } 242 243 style[property] = value; 244 }); 245 } 246 247 function hasClass(element, value) { 248 return element.classList ? 249 element.classList.contains(value) : 250 element.className.indexOf(value) > -1; 251 } 252 253 function addClass(element, value) { 254 var className; 255 256 if (isNumber(element.length)) { 257 return each(element, function (elem) { 258 addClass(elem, value); 259 }); 260 } 261 262 if (element.classList) { 263 return element.classList.add(value); 264 } 265 266 className = trim(element.className); 267 268 if (!className) { 269 element.className = value; 270 } else if (className.indexOf(value) < 0) { 271 element.className = className + ' ' + value; 272 } 273 } 274 275 function removeClass(element, value) { 276 if (isNumber(element.length)) { 277 return each(element, function (elem) { 278 removeClass(elem, value); 279 }); 280 } 281 282 if (element.classList) { 283 return element.classList.remove(value); 284 } 285 286 if (element.className.indexOf(value) >= 0) { 287 element.className = element.className.replace(value, ''); 288 } 289 } 290 291 function toggleClass(element, value, added) { 292 if (isNumber(element.length)) { 293 return each(element, function (elem) { 294 toggleClass(elem, value, added); 295 }); 296 } 297 298 // IE10-11 doesn't support the second parameter of `classList.toggle` 299 if (added) { 300 addClass(element, value); 301 } else { 302 removeClass(element, value); 303 } 304 } 305 306 function getData(element, name) { 307 return isObject(element[name]) ? 308 element[name] : 309 element.dataset ? 310 element.dataset[name] : 311 element.getAttribute('data-' + name); 312 } 313 314 function setData(element, name, data) { 315 if (isObject(data) && isUndefined(element[name])) { 316 element[name] = data; 317 } else if (element.dataset) { 318 element.dataset[name] = data; 319 } else { 320 element.setAttribute('data-' + name, data); 321 } 322 } 323 324 function removeData(element, name) { 325 if (isObject(element[name])) { 326 delete element[name]; 327 } else if (element.dataset) { 328 delete element.dataset[name]; 329 } else { 330 element.removeAttribute('data-' + name); 331 } 332 } 333 334 function addListener(element, type, handler, once) { 335 var types = trim(type).split(REGEXP_SPACES); 336 var originalHandler = handler; 337 338 if (types.length > 1) { 339 return each(types, function (type) { 340 addListener(element, type, handler); 341 }); 342 } 343 344 if (once) { 345 handler = function () { 346 removeListener(element, type, handler); 347 348 return originalHandler.apply(element, arguments); 349 }; 350 } 351 352 if (element.addEventListener) { 353 element.addEventListener(type, handler, false); 354 } else if (element.attachEvent) { 355 element.attachEvent('on' + type, handler); 356 } 357 } 358 359 function removeListener(element, type, handler) { 360 var types = trim(type).split(REGEXP_SPACES); 361 362 if (types.length > 1) { 363 return each(types, function (type) { 364 removeListener(element, type, handler); 365 }); 366 } 367 368 if (element.removeEventListener) { 369 element.removeEventListener(type, handler, false); 370 } else if (element.detachEvent) { 371 element.detachEvent('on' + type, handler); 372 } 373 } 374 375 function dispatchEvent(element, type, data) { 376 var event; 377 378 if (element.dispatchEvent) { 379 380 // Event and CustomEvent on IE9-11 are global objects, not constructors 381 if (isFunction(Event) && isFunction(CustomEvent)) { 382 if (isUndefined(data)) { 383 event = new Event(type, { 384 bubbles: true, 385 cancelable: true 386 }); 387 } else { 388 event = new CustomEvent(type, { 389 detail: data, 390 bubbles: true, 391 cancelable: true 392 }); 393 } 394 } else { 395 // IE9-11 396 if (isUndefined(data)) { 397 event = document.createEvent('Event'); 398 event.initEvent(type, true, true); 399 } else { 400 event = document.createEvent('CustomEvent'); 401 event.initCustomEvent(type, true, true, data); 402 } 403 } 404 405 // IE9+ 406 return element.dispatchEvent(event); 407 } else if (element.fireEvent) { 408 409 // IE6-10 (native events only) 410 return element.fireEvent('on' + type); 411 } 412 } 413 414 function preventDefault(e) { 415 if (e.preventDefault) { 416 e.preventDefault(); 417 } else { 418 e.returnValue = false;
419 } 420 } 421 422 function getEvent(event) { 423 var e = event || window.event; 424 var doc; 425 426 // Fix target property (IE8) 427 if (!e.target) { 428 e.target = e.srcElement || document; 429 } 430 431 if (!isNumber(e.pageX)) { 432 doc = document.documentElement; 433 e.pageX = e.clientX + (window.scrollX || doc && doc.scrollLeft || 0) - (doc && doc.clientLeft || 0); 434 e.pageY = e.clientY + (window.scrollY || doc && doc.scrollTop || 0) - (doc && doc.clientTop || 0); 435 } 436 437 return e; 438 } 439 440 function getOffset(element) { 441 var doc = document.documentElement; 442 var box = element.getBoundingClientRect(); 443 444 return { 445 left: box.left + (window.scrollX || doc && doc.scrollLeft || 0) - (doc && doc.clientLeft || 0), 446 top: box.top + (window.scrollY || doc && doc.scrollTop || 0) - (doc && doc.clientTop || 0) 447 }; 448 } 449 450 function getTouchesCenter(touches) { 451 var length = touches.length; 452 var pageX = 0; 453 var pageY = 0; 454 455 if (length) { 456 each(touches, function (touch) { 457 pageX += touch.pageX; 458 pageY += touch.pageY; 459 }); 460 461 pageX /= length; 462 pageY /= length; 463 } 464 465 return { 466 pageX: pageX, 467 pageY: pageY 468 }; 469 } 470 471 function getByTag(element, tagName) { 472 return element.getElementsByTagName(tagName); 473 } 474 475 function getByClass(element, className) { 476 return element.getElementsByClassName ? 477 element.getElementsByClassName(className) : 478 element.querySelectorAll('.' + className); 479 } 480 481 function createElement(tagName) { 482 return document.createElement(tagName); 483 } 484 485 function appendChild(element, elem) { 486 element.appendChild(elem); 487 } 488 489 function removeChild(element) { 490 if (element.parentNode) { 491 element.parentNode.removeChild(element); 492 } 493 } 494 495 function empty(element) { 496 while (element.firstChild) { 497 element.removeChild(element.firstChild); 498 } 499 } 500 501 function isCrossOriginURL(url) { 502 var parts = url.match(REGEXP_ORIGINS); 503 504 return parts && ( 505 parts[1] !== location.protocol || 506 parts[2] !== location.hostname || 507 parts[3] !== location.port 508 ); 509 } 510 511 function addTimestamp(url) { 512 var timestamp = 'timestamp=' + (new Date()).getTime(); 513 514 return (url + (url.indexOf('?') === -1 ? '?' : '&') + timestamp); 515 } 516 517 function getImageSize(image, callback) { 518 var newImage; 519 520 // Modern browsers (ignore Safari) 521 if (image.naturalWidth && !IS_SAFARI) { 522 return callback(image.naturalWidth, image.naturalHeight); 523 } 524 525 // IE8: Don't use `new Image()` here 526 newImage = createElement('img'); 527 528 newImage.onload = function () { 529 callback(this.width, this.height); 530 }; 531 532 newImage.src = image.src; 533 } 534 535 function getTransform(data) { 536 var transforms = []; 537 var rotate = data.rotate; 538 var scaleX = data.scaleX; 539 var scaleY = data.scaleY; 540 541 if (isNumber(rotate)) { 542 transforms.push('rotate(' + rotate + 'deg)'); 543 } 544 545 if (isNumber(scaleX) && isNumber(scaleY)) { 546 transforms.push('scale(' + scaleX + ',' + scaleY + ')'); 547 } 548 549 return transforms.length ? transforms.join(' ') : 'none'; 550 } 551 552 function getRotatedSizes(data, reversed) { 553 var deg = abs(data.degree) % 180; 554 var arc = (deg > 90 ? (180 - deg) : deg) * PI / 180; 555 var sinArc = sin(arc); 556 var cosArc = cos(arc); 557 var width = data.width; 558 var height = data.height; 559 var aspectRatio = data.aspectRatio; 560 var newWidth; 561 var newHeight; 562 563 if (!reversed) { 564 newWidth = width * cosArc + height * sinArc; 565 newHeight = width * sinArc + height * cosArc; 566 } else { 567 newWidth = width / (cosArc + sinArc / aspectRatio); 568 newHeight = newWidth / aspectRatio; 569 } 570 571 return { 572 width: newWidth, 573 height: newHeight 574 }; 575 } 576 577 function getSourceCanvas(image, data) { 578 var canvas = createElement('canvas'); 579 var context = canvas.getContext('2d'); 580 var dstX = 0; 581 var dstY = 0; 582 var dstWidth = data.naturalWidth; 583 var dstHeight = data.naturalHeight; 584 var rotate = data.rotate; 585 var scaleX = data.scaleX; 586 var scaleY = data.scaleY;
587 var scalable = isNumber(scaleX) && isNumber(scaleY) && (scaleX !== 1 || scaleY !== 1); 588 var rotatable = isNumber(rotate) && rotate !== 0; 589 var advanced = rotatable || scalable; 590 var canvasWidth = dstWidth * abs(scaleX || 1); 591 var canvasHeight = dstHeight * abs(scaleY || 1); 592 var translateX; 593 var translateY; 594 var rotated; 595 596 if (scalable) { 597 translateX = canvasWidth / 2; 598 translateY = canvasHeight / 2; 599 } 600 601 if (rotatable) { 602 rotated = getRotatedSizes({ 603 width: canvasWidth, 604 height: canvasHeight, 605 degree: rotate 606 }); 607 608 canvasWidth = rotated.width; 609 canvasHeight = rotated.height; 610 translateX = canvasWidth / 2; 611 translateY = canvasHeight / 2; 612 } 613 614 canvas.width = canvasWidth; 615 canvas.height = canvasHeight; 616 617 if (advanced) { 618 dstX = -dstWidth / 2; 619 dstY = -dstHeight / 2; 620 621 context.save(); 622 context.translate(translateX, translateY); 623 } 624 625 if (rotatable) { 626 context.rotate(rotate * PI / 180); 627 } 628 629 // Should call `scale` after rotated 630 if (scalable) { 631 context.scale(scaleX, scaleY); 632 } 633 634 context.drawImage(image, floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight)); 635 636 if (advanced) { 637 context.restore(); 638 } 639 640 return canvas; 641 } 642 643 function getStringFromCharCode(dataView, start, length) { 644 var str = ''; 645 var i = start; 646 647 for (length += start; i < length; i++) { 648 str += fromCharCode(dataView.getUint8(i)); 649 } 650 651 return str; 652 } 653 654 function getOrientation(arrayBuffer) { 655 var dataView = new DataView(arrayBuffer); 656 var length = dataView.byteLength; 657 var orientation; 658 var exifIDCode; 659 var tiffOffset; 660 var firstIFDOffset; 661 var littleEndian; 662 var endianness; 663 var app1Start; 664 var ifdStart; 665 var offset; 666 var i; 667 668 // Only handle JPEG image (start by 0xFFD8) 669 if (dataView.getUint8(0) === 0xFF && dataView.getUint8(1) === 0xD8) { 670 offset = 2; 671 672 while (offset < length) { 673 if (dataView.getUint8(offset) === 0xFF && dataView.getUint8(offset + 1) === 0xE1) { 674 app1Start = offset; 675 break; 676 } 677 678 offset++; 679 } 680 } 681 682 if (app1Start) { 683 exifIDCode = app1Start + 4; 684 tiffOffset = app1Start + 10; 685 686 if (getStringFromCharCode(dataView, exifIDCode, 4) === 'Exif') { 687 endianness = dataView.getUint16(tiffOffset); 688 littleEndian = endianness === 0x4949; 689 690 if (littleEndian || endianness === 0x4D4D /* bigEndian */) { 691 if (dataView.getUint16(tiffOffset + 2, littleEndian) === 0x002A) { 692 firstIFDOffset = dataView.getUint32(tiffOffset + 4, littleEndian); 693 694 if (firstIFDOffset >= 0x00000008) { 695 ifdStart = tiffOffset + firstIFDOffset; 696 } 697 } 698 } 699 } 700 } 701 702 if (ifdStart) { 703 length = dataView.getUint16(ifdStart, littleEndian); 704 705 for (i = 0; i < length; i++) { 706 offset = ifdStart + i * 12 + 2; 707 708 if (dataView.getUint16(offset, littleEndian) === 0x0112 /* Orientation */) { 709 710 // 8 is the offset of the current tag's value 711 offset += 8; 712 713 // Get the original orientation value 714 orientation = dataView.getUint16(offset, littleEndian); 715 716 // Override the orientation with its default value for Safari 717 if (IS_SAFARI) { 718 dataView.setUint16(offset, 1, littleEndian); 719 } 720 721 break; 722 } 723 } 724 } 725 726 return orientation; 727 } 728 729 function dataURLToArrayBuffer(dataURL) { 730 var base64 = dataURL.replace(REGEXP_DATA_URL_HEAD, ''); 731 var binary = atob(base64); 732 var length = binary.length; 733 var arrayBuffer = new ArrayBuffer(length); 734 var dataView = new Uint8Array(arrayBuffer); 735 var i; 736 737 for (i = 0; i < length; i++) { 738 dataView[i] = binary.charCodeAt(i); 739 } 740 741 return arrayBuffer; 742 } 743 744 // Only available for JPEG image 745 function arrayBufferToDataURL(arrayBuffer) { 746 var dataView = new Uint8Array(arrayBuffer); 747 var length = dataView.length; 748 var base64 = ''; 749 var i; 750 751 for (i = 0; i < length; i++) { 752 base64 += fromCharCode(dataView[i]); 753 } 754 755 return 'data:image/jpeg;base64,' + btoa(base64); 756 } 757 758 function Cropper(element, options) { 759 var _this = this; 760 761 _this.element = element; 762 _this.options = extend({}, Cropper.DEFAULTS, isPlainObject(options) && options); 763 _this.ready = false;
764 _this.built = false; 765 _this.complete = false; 766 _this.rotated = false; 767 _this.cropped = false; 768 _this.disabled = false; 769 _this.replaced = false; 770 _this.limited = false; 771 _this.wheeling = false; 772 _this.isImg = false; 773 _this.originalUrl = ''; 774 _this.canvasData = null; 775 _this.cropBoxData = null; 776 _this.previews = null; 777 _this.init(); 778 } 779 780 Cropper.prototype = { 781 constructor: Cropper, 782 783 init: function () { 784 var _this = this; 785 var element = _this.element; 786 var tagName = element.tagName.toLowerCase(); 787 var url; 788 789 if (getData(element, NAMESPACE)) { 790 return; 791 } 792 793 setData(element, NAMESPACE, _this); 794 795 if (tagName === 'img') { 796 _this.isImg = true; 797 798 // e.g.: "img/picture.jpg" 799 _this.originalUrl = url = element.getAttribute('src'); 800 801 // Stop when it's a blank image 802 if (!url) { 803 return; 804 } 805 806 // e.g.: "http://example.com/img/picture.jpg" 807 url = element.src; 808 } else if (tagName === 'canvas' && SUPPORT_CANVAS) { 809 url = element.toDataURL(); 810 } 811 812 _this.load(url); 813 }, 814 815 load: function (url) { 816 var _this = this; 817 var options = _this.options; 818 var element = _this.element; 819 var xhr; 820 821 if (!url) { 822 return; 823 } 824 825 if (isFunction(options.build)) { 826 addListener(element, EVENT_BUILD, options.build, true); 827 } 828 829 if (dispatchEvent(element, EVENT_BUILD) === false) { 830 return; 831 } 832 833 _this.url = url; 834 _this.imageData = {}; 835 836 if (!options.checkOrientation || !ArrayBuffer) { 837 return _this.clone(); 838 } 839 840 // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari 841 if (REGEXP_DATA_URL.test(url)) { 842 return REGEXP_DATA_URL_JPEG.test(url) ? 843 _this.read(dataURLToArrayBuffer(url)) : 844 _this.clone(); 845 } 846 847 xhr = new XMLHttpRequest(); 848 849 xhr.onerror = xhr.onabort = function () { 850 _this.clone(); 851 }; 852 853 xhr.onload = function () { 854 _this.read(this.response); 855 }; 856 857 xhr.open('get', url); 858 xhr.responseType = 'arraybuffer'; 859 xhr.send(); 860 }, 861 862 read: function (arrayBuffer) { 863 var _this = this; 864 var options = _this.options; 865 var orientation = getOrientation(arrayBuffer); 866 var imageData = _this.imageData; 867 var rotate; 868 var scaleX; 869 var scaleY; 870 871 if (orientation > 1) { 872 _this.url = arrayBufferToDataURL(arrayBuffer); 873 874 switch (orientation) { 875 876 // flip horizontal 877 case 2: 878 scaleX = -1; 879 break; 880 881 // rotate left 180° 882 case 3: 883 rotate = -180; 884 break; 885 886 // flip vertical 887 case 4: 888 scaleY = -1; 889 break; 890 891 // flip vertical + rotate right 90° 892 case 5: 893 rotate = 90; 894 scaleY = -1; 895 break; 896 897 // rotate right 90° 898 case 6: 899 rotate = 90; 900 break; 901 902 // flip horizontal + rotate right 90° 903 case 7: 904 rotate = 90; 905 scaleX = -1; 906 break; 907 908 // rotate left 90° 909 case 8: 910 rotate = -90; 911 break; 912 } 913 } 914 915 if (options.rotatable) { 916 imageData.rotate = rotate; 917 } 918 919 if (options.scalable) { 920 imageData.scaleX = scaleX; 921 imageData.scaleY = scaleY; 922 } 923 924 _this.clone(); 925 }, 926 927 clone: function () { 928 var _this = this; 929 var element = _this.element; 930 var url = _this.url; 931 var crossOrigin; 932 var crossOriginUrl; 933 var image; 934 var start; 935 var stop; 936 937 if (_this.options.checkCrossOrigin && isCrossOriginURL(url)) { 938 crossOrigin = element.crossOrigin; 939 940 if (crossOrigin) { 941 crossOriginUrl = url; 942 } else { 943 crossOrigin = 'anonymous'; 944 945 // Bust cache when there is not a "crossOrigin" property 946 crossOriginUrl = addTimestamp(url); 947 } 948 } 949 950 _this.crossOrigin = crossOrigin; 951 _this.crossOriginUrl = crossOriginUrl; 952 image = createElement('img'); 953 954 if (crossOrigin) { 955 image.crossOrigin = crossOrigin; 956 } 957 958 image.src = crossOriginUrl || url; 959 _this.image = image; 960 _this._start = start = proxy(_this.start, _this); 961 _this._stop = stop = proxy(_this.stop, _this); 962 963 if (_this.isImg) { 964 if (element.complete) { 965 _this.start(); 966 } else { 967 addListener(element, EVENT_LOAD, start); 968 } 969 } else { 970 addListener(image, EVENT_LOAD, start); 971 addListener(image, EVENT_ERROR, stop); 972 addClass(image, CLASS_HIDE); 973 element.parentNode.insertBefore(image, element.nextSibling); 974 } 975 }, 976 977 start: function (event) { 978 var _this = this; 979 var image = _this.isImg ? _this.element : _this.image; 980 981 if (event) { 982 removeListener(image, EVENT_LOAD, _this._start); 983 removeListener(image, EVENT_ERROR, _this._stop); 984 } 985
986 getImageSize(image, function (naturalWidth, naturalHeight) { 987 extend(_this.imageData, { 988 naturalWidth: naturalWidth, 989 naturalHeight: naturalHeight, 990 aspectRatio: naturalWidth / naturalHeight 991 }); 992 993 _this.ready = true; 994 _this.build(); 995 }); 996 }, 997 998 stop: function () { 999 var _this = this; 1000 var image = _this.image; 1001 1002 removeListener(image, EVENT_LOAD, _this._start); 1003 removeListener(image, EVENT_ERROR, _this._stop); 1004 1005 removeChild(image); 1006 _this.image = null; 1007 }, 1008 1009 build: function () { 1010 var _this = this; 1011 var options = _this.options; 1012 var element = _this.element; 1013 var image = _this.image; 1014 var container; 1015 var template; 1016 var cropper; 1017 var canvas; 1018 var dragBox; 1019 var cropBox; 1020 var face; 1021 1022 if (!_this.ready) { 1023 return; 1024 } 1025 1026 // Unbuild first when replace 1027 if (_this.built) { 1028 _this.unbuild(); 1029 } 1030 1031 template = createElement('div'); 1032 template.innerHTML = Cropper.TEMPLATE; 1033 1034 // Create cropper elements 1035 _this.container = container = element.parentNode; 1036 _this.cropper = cropper = getByClass(template, 'cropper-container')[0]; 1037 _this.canvas = canvas = getByClass(cropper, 'cropper-canvas')[0]; 1038 _this.dragBox = dragBox = getByClass(cropper, 'cropper-drag-box')[0]; 1039 _this.cropBox = cropBox = getByClass(cropper, 'cropper-crop-box')[0]; 1040 _this.viewBox = getByClass(cropper, 'cropper-view-box')[0]; 1041 _this.face = face = getByClass(cropBox, 'cropper-face')[0]; 1042 1043 appendChild(canvas, image); 1044 1045 // Hide the original image 1046 addClass(element, CLASS_HIDDEN); 1047 1048 // Inserts the cropper after to the current image 1049 container.insertBefore(cropper, element.nextSibling); 1050 1051 // Show the image if is hidden 1052 if (!_this.isImg) { 1053 removeClass(image, CLASS_HIDE); 1054 } 1055 1056 _this.initPreview(); 1057 _this.bind(); 1058 1059 options.aspectRatio = max(0, options.aspectRatio) || NaN; 1060 options.viewMode = max(0, min(3, round(options.viewMode))) || 0; 1061 1062 if (options.autoCrop) { 1063 _this.cropped = true; 1064 1065 if (options.modal) { 1066 addClass(dragBox, CLASS_MODAL); 1067 } 1068 } else { 1069 addClass(cropBox, CLASS_HIDDEN); 1070 } 1071 1072 if (!options.guides) { 1073 addClass(getByClass(cropBox, 'cropper-dashed'), CLASS_HIDDEN); 1074 } 1075 1076 if (!options.center) { 1077 addClass(getByClass(cropBox, 'cropper-center'), CLASS_HIDDEN); 1078 } 1079 1080 if (options.background) { 1081 addClass(cropper, CLASS_BG); 1082 } 1083 1084 if (!options.highlight) { 1085 addClass(face, CLASS_INVISIBLE); 1086 } 1087 1088 if (options.cropBoxMovable) { 1089 addClass(face, CLASS_MOVE); 1090 setData(face, DATA_ACTION, ACTION_ALL); 1091 } 1092 1093 if (!options.cropBoxResizable) { 1094 addClass(getByClass(cropBox, 'cropper-line'), CLASS_HIDDEN); 1095 addClass(getByClass(cropBox, 'cropper-point'), CLASS_HIDDEN); 1096 } 1097 1098 _this.setDragMode(options.dragMode); 1099 _this.render(); 1100 _this.built = true; 1101 _this.setData(options.data); 1102 1103 // Call the built asynchronously to keep "image.cropper" is defined 1104 setTimeout(function () { 1105 if (isFunction(options.built)) { 1106 addListener(element, EVENT_BUILT, options.built, true); 1107 } 1108 1109 dispatchEvent(element, EVENT_BUILT); 1110 dispatchEvent(element, EVENT_CROP, _this.getData()); 1111 1112 _this.complete = true; 1113 }, 0); 1114 }, 1115 1116 unbuild: function () { 1117 var _this = this; 1118 1119 if (!_this.built) { 1120 return; 1121 } 1122 1123 _this.built = false; 1124 _this.complete = false; 1125 _this.initialImageData = null; 1126 1127 // Clear `initialCanvasData` is necessary when replace 1128 _this.initialCanvasData = null; 1129 _this.initialCropBoxData = null; 1130 _this.containerData = null; 1131 _this.canvasData = null; 1132 1133 // Clear `cropBoxData` is necessary when replace 1134 _this.cropBoxData = null; 1135 _this.unbind(); 1136 1137 _this.resetPreview(); 1138 _this.previews = null; 1139 1140 _this.viewBox = null; 1141 _this.cropBox = null; 1142 _this.dragBox = null; 1143 _this.canvas = null; 1144 _this.container = null; 1145 1146 removeChild(_this.cropper); 1147 _this.cropper = null; 1148 }, 1149 1150 render: function () { 1151 var _this = this; 1152 1153 _this.initContainer(); 1154 _this.initCanvas(); 1155 _this.initCropBox(); 1156 1157 _this.renderCanvas(); 1158 1159 if (_this.cropped) { 1160 _this.renderCropBox(); 1161 } 1162 }, 1163
1164 initContainer: function () { 1165 var _this = this; 1166 var options = _this.options; 1167 var element = _this.element; 1168 var container = _this.container; 1169 var cropper = _this.cropper; 1170 var containerData; 1171 1172 addClass(cropper, CLASS_HIDDEN); 1173 removeClass(element, CLASS_HIDDEN); 1174 1175 _this.containerData = containerData = { 1176 width: max( 1177 container.offsetWidth, 1178 Number(options.minContainerWidth) || 200 1179 ), 1180 height: max( 1181 container.offsetHeight, 1182 Number(options.minContainerHeight) || 100 1183 ) 1184 }; 1185 1186 setStyle(cropper, { 1187 width: containerData.width, 1188 height: containerData.height 1189 }); 1190 1191 addClass(element, CLASS_HIDDEN); 1192 removeClass(cropper, CLASS_HIDDEN); 1193 }, 1194 1195 // Canvas (image wrapper) 1196 initCanvas: function () { 1197 var _this = this; 1198 var viewMode = _this.options.viewMode; 1199 var containerData = _this.containerData; 1200 var imageData = _this.imageData; 1201 var rotated = abs(imageData.rotate) === 90; 1202 var naturalWidth = rotated ? imageData.naturalHeight : imageData.naturalWidth; 1203 var naturalHeight = rotated ? imageData.naturalWidth : imageData.naturalHeight;
1204 var aspectRatio = naturalWidth / naturalHeight; 1205 var canvasWidth = containerData.width; 1206 var canvasHeight = containerData.height; 1207 var canvasData; 1208 1209 if (containerData.height * aspectRatio > containerData.width) { 1210 if (viewMode === 3) { 1211 canvasWidth = containerData.height * aspectRatio; 1212 } else { 1213 canvasHeight = containerData.width / aspectRatio; 1214 } 1215 } else { 1216 if (viewMode === 3) { 1217 canvasHeight = containerData.width / aspectRatio; 1218 } else { 1219 canvasWidth = containerData.height * aspectRatio; 1220 } 1221 } 1222 1223 canvasData = { 1224 naturalWidth: naturalWidth, 1225 naturalHeight: naturalHeight, 1226 aspectRatio: aspectRatio, 1227 width: canvasWidth, 1228 height: canvasHeight 1229 }; 1230 1231 canvasData.oldLeft = canvasData.left = (containerData.width - canvasWidth) / 2; 1232 canvasData.oldTop = canvasData.top = (containerData.height - canvasHeight) / 2; 1233 1234 _this.canvasData = canvasData; 1235 _this.limited = (viewMode === 1 || viewMode === 2); 1236 _this.limitCanvas(true, true); 1237 _this.initialImageData = extend({}, imageData); 1238 _this.initialCanvasData = extend({}, canvasData); 1239 }, 1240 1241 limitCanvas: function (sizeLimited, positionLimited) { 1242 var _this = this; 1243 var options = _this.options; 1244 var viewMode = options.viewMode; 1245 var containerData = _this.containerData; 1246 var canvasData = _this.canvasData; 1247 var aspectRatio = canvasData.aspectRatio; 1248 var cropBoxData = _this.cropBoxData; 1249 var cropped = _this.cropped && cropBoxData; 1250 var minCanvasWidth; 1251 var minCanvasHeight; 1252 var newCanvasLeft; 1253 var newCanvasTop; 1254 1255 if (sizeLimited) { 1256 minCanvasWidth = Number(options.minCanvasWidth) || 0; 1257 minCanvasHeight = Number(options.minCanvasHeight) || 0; 1258 1259 if (viewMode > 1) { 1260 minCanvasWidth = max(minCanvasWidth, containerData.width); 1261 minCanvasHeight = max(minCanvasHeight, containerData.height); 1262 1263 if (viewMode === 3) { 1264 if (minCanvasHeight * aspectRatio > minCanvasWidth) { 1265 minCanvasWidth = minCanvasHeight * aspectRatio; 1266 } else { 1267 minCanvasHeight = minCanvasWidth / aspectRatio; 1268 } 1269 } 1270 } else if (viewMode > 0) { 1271 if (minCanvasWidth) { 1272 minCanvasWidth = max( 1273 minCanvasWidth, 1274 cropped ? cropBoxData.width : 0 1275 ); 1276 } else if (minCanvasHeight) { 1277 minCanvasHeight = max( 1278 minCanvasHeight, 1279 cropped ? cropBoxData.height : 0 1280 ); 1281 } else if (cropped) { 1282 minCanvasWidth = cropBoxData.width; 1283 minCanvasHeight = cropBoxData.height; 1284 1285 if (minCanvasHeight * aspectRatio > minCanvasWidth) { 1286 minCanvasWidth = minCanvasHeight * aspectRatio; 1287 } else { 1288 minCanvasHeight = minCanvasWidth / aspectRatio; 1289 } 1290 } 1291 } 1292 1293 if (minCanvasWidth && minCanvasHeight) { 1294 if (minCanvasHeight * aspectRatio > minCanvasWidth) { 1295 minCanvasHeight = minCanvasWidth / aspectRatio; 1296 } else { 1297 minCanvasWidth = minCanvasHeight * aspectRatio; 1298 } 1299 } else if (minCanvasWidth) { 1300 minCanvasHeight = minCanvasWidth / aspectRatio; 1301 } else if (minCanvasHeight) { 1302 minCanvasWidth = minCanvasHeight * aspectRatio; 1303 } 1304 1305 canvasData.minWidth = minCanvasWidth; 1306 canvasData.minHeight = minCanvasHeight; 1307 canvasData.maxWidth = Infinity; 1308 canvasData.maxHeight = Infinity; 1309 } 1310 1311 if (positionLimited) { 1312 if (viewMode) { 1313 newCanvasLeft = containerData.width - canvasData.width; 1314 newCanvasTop = containerData.height - canvasData.height; 1315 1316 canvasData.minLeft = min(0, newCanvasLeft); 1317 canvasData.minTop = min(0, newCanvasTop); 1318 canvasData.maxLeft = max(0, newCanvasLeft); 1319 canvasData.maxTop = max(0, newCanvasTop); 1320 1321 if (cropped && _this.limited) { 1322 canvasData.minLeft = min( 1323 cropBoxData.left, 1324 cropBoxData.left + cropBoxData.width - canvasData.width 1325 ); 1326 canvasData.minTop = min( 1327 cropBoxData.top, 1328 cropBoxData.top + cropBoxData.height - canvasData.height 1329 ); 1330 canvasData.maxLeft = cropBoxData.left; 1331 canvasData.maxTop = cropBoxData.top; 1332 1333 if (viewMode === 2) { 1334 if (canvasData.width >= containerData.width) { 1335 canvasData.minLeft = min(0, newCanvasLeft); 1336 canvasData.maxLeft = max(0, newCanvasLeft); 1337 } 1338 1339 if (canvasData.height >= containerData.height) { 1340 canvasData.minTop = min(0, newCanvasTop); 1341 canvasData.maxTop = max(0, newCanvasTop); 1342 } 1343 } 1344 } 1345 } else { 1346 canvasData.minLeft = -canvasData.width; 1347 canvasData.minTop = -canvasData.height; 1348 canvasData.maxLeft = containerData.width; 1349 canvasData.maxTop = containerData.height; 1350 } 1351 } 1352 }, 1353 1354 renderCanvas: function (changed) { 1355 var _this = this; 1356 var canvasData = _this.canvasData; 1357 var imageData = _this.imageData; 1358 var rotate = imageData.rotate; 1359 var aspectRatio; 1360 var rotatedData; 1361 1362 if (_this.rotated) { 1363 _this.rotated = false;
1364 1365 // Computes rotated sizes with image sizes 1366 rotatedData = getRotatedSizes({ 1367 width: imageData.width, 1368 height: imageData.height, 1369 degree: rotate 1370 }); 1371 1372 aspectRatio = rotatedData.width / rotatedData.height; 1373 1374 if (aspectRatio !== canvasData.aspectRatio) { 1375 canvasData.left -= (rotatedData.width - canvasData.width) / 2; 1376 canvasData.top -= (rotatedData.height - canvasData.height) / 2; 1377 canvasData.width = rotatedData.width; 1378 canvasData.height = rotatedData.height; 1379 canvasData.aspectRatio = aspectRatio; 1380 canvasData.naturalWidth = imageData.naturalWidth; 1381 canvasData.naturalHeight = imageData.naturalHeight; 1382 1383 // Computes rotated sizes with natural image sizes 1384 if (rotate % 180) { 1385 rotatedData = getRotatedSizes({ 1386 width: imageData.naturalWidth, 1387 height: imageData.naturalHeight, 1388 degree: rotate 1389 }); 1390 1391 canvasData.naturalWidth = rotatedData.width; 1392 canvasData.naturalHeight = rotatedData.height; 1393 } 1394 1395 _this.limitCanvas(true, false); 1396 } 1397 } 1398 1399 if (canvasData.width > canvasData.maxWidth || 1400 canvasData.width < canvasData.minWidth) { 1401 canvasData.left = canvasData.oldLeft; 1402 } 1403 1404 if (canvasData.height > canvasData.maxHeight || 1405 canvasData.height < canvasData.minHeight) { 1406 canvasData.top = canvasData.oldTop; 1407 } 1408 1409 canvasData.width = min( 1410 max(canvasData.width, canvasData.minWidth), 1411 canvasData.maxWidth 1412 ); 1413 canvasData.height = min( 1414 max(canvasData.height, canvasData.minHeight),
1415 canvasData.maxHeight 1416 ); 1417 1418 _this.limitCanvas(false, true); 1419 1420 canvasData.oldLeft = canvasData.left = min( 1421 max(canvasData.left, canvasData.minLeft), 1422 canvasData.maxLeft 1423 ); 1424 canvasData.oldTop = canvasData.top = min( 1425 max(canvasData.top, canvasData.minTop), 1426 canvasData.maxTop 1427 ); 1428 1429 setStyle(_this.canvas, { 1430 width: canvasData.width, 1431 height: canvasData.height, 1432 left: canvasData.left, 1433 top: canvasData.top 1434 }); 1435 1436 _this.renderImage(); 1437 1438 if (_this.cropped && _this.limited) { 1439 _this.limitCropBox(true, true); 1440 } 1441 1442 if (changed) { 1443 _this.output(); 1444 } 1445 }, 1446 1447 renderImage: function (changed) { 1448 var _this = this; 1449 var canvasData = _this.canvasData; 1450 var imageData = _this.imageData; 1451 var newImageData; 1452 var reversedData; 1453 var reversedWidth; 1454 var reversedHeight; 1455 var transform; 1456 1457 if (imageData.rotate) { 1458 reversedData = getRotatedSizes({ 1459 width: canvasData.width, 1460 height: canvasData.height, 1461 degree: imageData.rotate, 1462 aspectRatio: imageData.aspectRatio 1463 }, true); 1464 1465 reversedWidth = reversedData.width; 1466 reversedHeight = reversedData.height; 1467 1468 newImageData = { 1469 width: reversedWidth, 1470 height: reversedHeight, 1471 left: (canvasData.width - reversedWidth) / 2, 1472 top: (canvasData.height - reversedHeight) / 2 1473 }; 1474 } 1475 1476 extend(imageData, newImageData || { 1477 width: canvasData.width, 1478 height: canvasData.height, 1479 left: 0, 1480 top: 0 1481 }); 1482 1483 transform = getTransform(imageData); 1484 1485 setStyle(_this.image, { 1486 width: imageData.width, 1487 height: imageData.height, 1488 marginLeft: imageData.left, 1489 marginTop: imageData.top, 1490 WebkitTransform: transform, 1491 msTransform: transform, 1492 transform: transform 1493 }); 1494 1495 if (changed) { 1496 _this.output(); 1497 } 1498 }, 1499 1500 initCropBox: function () { 1501 var _this = this; 1502 var options = _this.options; 1503 var aspectRatio = options.aspectRatio; 1504 var autoCropArea = Number(options.autoCropArea) || 0.8; 1505 var canvasData = _this.canvasData; 1506 var cropBoxData = { 1507 width: canvasData.width, 1508 height: canvasData.height 1509 }; 1510 1511 if (aspectRatio) { 1512 if (canvasData.height * aspectRatio > canvasData.width) { 1513 cropBoxData.height = cropBoxData.width / aspectRatio; 1514 } else { 1515 cropBoxData.width = cropBoxData.height * aspectRatio; 1516 } 1517 } 1518 1519 _this.cropBoxData = cropBoxData; 1520 _this.limitCropBox(true, true); 1521 1522 // Initialize auto crop area 1523 cropBoxData.width = min( 1524 max(cropBoxData.width, cropBoxData.minWidth), 1525 cropBoxData.maxWidth 1526 ); 1527 cropBoxData.height = min( 1528 max(cropBoxData.height, cropBoxData.minHeight),
1529 cropBoxData.maxHeight 1530 ); 1531 1532 // The width/height of auto crop area must large than "minWidth/Height" 1533 cropBoxData.width = max( 1534 cropBoxData.minWidth, 1535 cropBoxData.width * autoCropArea 1536 ); 1537 cropBoxData.height = max( 1538 cropBoxData.minHeight, 1539 cropBoxData.height * autoCropArea 1540 ); 1541 cropBoxData.oldLeft = cropBoxData.left = ( 1542 canvasData.left + (canvasData.width - cropBoxData.width) / 2 1543 ); 1544 cropBoxData.oldTop = cropBoxData.top = ( 1545 canvasData.top + (canvasData.height - cropBoxData.height) / 2 1546 ); 1547 1548 _this.initialCropBoxData = extend({}, cropBoxData); 1549 }, 1550 1551 limitCropBox: function (sizeLimited, positionLimited) { 1552 var _this = this; 1553 var options = _this.options; 1554 var aspectRatio = options.aspectRatio; 1555 var containerData = _this.containerData; 1556 var canvasData = _this.canvasData; 1557 var cropBoxData = _this.cropBoxData; 1558 var limited = _this.limited; 1559 var minCropBoxWidth; 1560 var minCropBoxHeight; 1561 var maxCropBoxWidth; 1562 var maxCropBoxHeight; 1563 1564 if (sizeLimited) { 1565 minCropBoxWidth = Number(options.minCropBoxWidth) || 0; 1566 minCropBoxHeight = Number(options.minCropBoxHeight) || 0; 1567 1568 // The min/maxCropBoxWidth/Height must be less than containerWidth/Height 1569 minCropBoxWidth = min(minCropBoxWidth, containerData.width); 1570 minCropBoxHeight = min(minCropBoxHeight, containerData.height); 1571 maxCropBoxWidth = min( 1572 containerData.width, 1573 limited ? canvasData.width : containerData.width 1574 ); 1575 maxCropBoxHeight = min( 1576 containerData.height, 1577 limited ? canvasData.height : containerData.height 1578 ); 1579 1580 if (aspectRatio) { 1581 if (minCropBoxWidth && minCropBoxHeight) { 1582 if (minCropBoxHeight * aspectRatio > minCropBoxWidth) { 1583 minCropBoxHeight = minCropBoxWidth / aspectRatio; 1584 } else { 1585 minCropBoxWidth = minCropBoxHeight * aspectRatio; 1586 } 1587 } else if (minCropBoxWidth) { 1588 minCropBoxHeight = minCropBoxWidth / aspectRatio; 1589 } else if (minCropBoxHeight) { 1590 minCropBoxWidth = minCropBoxHeight * aspectRatio; 1591 } 1592 1593 if (maxCropBoxHeight * aspectRatio > maxCropBoxWidth) { 1594 maxCropBoxHeight = maxCropBoxWidth / aspectRatio; 1595 } else { 1596 maxCropBoxWidth = maxCropBoxHeight * aspectRatio; 1597 } 1598 } 1599 1600 // The minWidth/Height must be less than maxWidth/Height 1601 cropBoxData.minWidth = min(minCropBoxWidth, maxCropBoxWidth); 1602 cropBoxData.minHeight = min(minCropBoxHeight, maxCropBoxHeight); 1603 cropBoxData.maxWidth = maxCropBoxWidth; 1604 cropBoxData.maxHeight = maxCropBoxHeight; 1605 } 1606 1607 if (positionLimited) { 1608 if (limited) { 1609 cropBoxData.minLeft = max(0, canvasData.left); 1610 cropBoxData.minTop = max(0, canvasData.top); 1611 cropBoxData.maxLeft = min( 1612 containerData.width, 1613 canvasData.left + canvasData.width 1614 ) - cropBoxData.width; 1615 cropBoxData.maxTop = min( 1616 containerData.height, 1617 canvasData.top + canvasData.height 1618 ) - cropBoxData.height; 1619 } else { 1620 cropBoxData.minLeft = 0; 1621 cropBoxData.minTop = 0; 1622 cropBoxData.maxLeft = containerData.width - cropBoxData.width; 1623 cropBoxData.maxTop = containerData.height - cropBoxData.height; 1624 } 1625 } 1626 }, 1627 1628 renderCropBox: function () { 1629 var _this = this; 1630 var options = _this.options; 1631 var containerData = _this.containerData; 1632 var cropBoxData = _this.cropBoxData; 1633 1634 if (cropBoxData.width > cropBoxData.maxWidth || 1635 cropBoxData.width < cropBoxData.minWidth) { 1636 cropBoxData.left = cropBoxData.oldLeft; 1637 } 1638 1639 if (cropBoxData.height > cropBoxData.maxHeight || 1640 cropBoxData.height < cropBoxData.minHeight) { 1641 cropBoxData.top = cropBoxData.oldTop; 1642 } 1643 1644 cropBoxData.width = min( 1645 max(cropBoxData.width, cropBoxData.minWidth), 1646 cropBoxData.maxWidth 1647 ); 1648 cropBoxData.height = min( 1649 max(cropBoxData.height, cropBoxData.minHeight),
1650 cropBoxData.maxHeight 1651 ); 1652 1653 _this.limitCropBox(false, true); 1654 1655 cropBoxData.oldLeft = cropBoxData.left = min( 1656 max(cropBoxData.left, cropBoxData.minLeft), 1657 cropBoxData.maxLeft 1658 ); 1659 cropBoxData.oldTop = cropBoxData.top = min( 1660 max(cropBoxData.top, cropBoxData.minTop), 1661 cropBoxData.maxTop 1662 ); 1663 1664 if (options.movable && options.cropBoxMovable) { 1665 1666 // Turn to move the canvas when the crop box is equal to the container 1667 setData(_this.face, DATA_ACTION, cropBoxData.width === containerData.width && 1668 cropBoxData.height === containerData.height ? ACTION_MOVE : ACTION_ALL); 1669 } 1670 1671 setStyle(_this.cropBox, { 1672 width: cropBoxData.width, 1673 height: cropBoxData.height, 1674 left: cropBoxData.left, 1675 top: cropBoxData.top 1676 }); 1677 1678 if (_this.cropped && _this.limited) { 1679 _this.limitCanvas(true, true); 1680 } 1681 1682 if (!_this.disabled) { 1683 _this.output(); 1684 } 1685 }, 1686 1687 output: function () { 1688 var _this = this; 1689 1690 _this.preview(); 1691 1692 if (_this.complete) { 1693 dispatchEvent(_this.element, EVENT_CROP, _this.getData()); 1694 } 1695 }, 1696 1697 initPreview: function () { 1698 var _this = this; 1699 var preview = _this.options.preview; 1700 var image = createElement('img'); 1701 var crossOrigin = _this.crossOrigin; 1702 var url = crossOrigin ? _this.crossOriginUrl : _this.url; 1703 var previews; 1704 1705 if (crossOrigin) { 1706 image.crossOrigin = crossOrigin; 1707 } 1708 1709 image.src = url; 1710 appendChild(_this.viewBox, image); 1711 _this.image2 = image; 1712 1713 if (!preview) { 1714 return; 1715 } 1716 1717 _this.previews = previews = document.querySelectorAll(preview); 1718 1719 each(previews, function (element) { 1720 var image = createElement('img'); 1721 1722 // Save the original size for recover 1723 setData(element, DATA_PREVIEW, { 1724 width: element.offsetWidth, 1725 height: element.offsetHeight, 1726 html: element.innerHTML 1727 }); 1728 1729 if (crossOrigin) { 1730 image.crossOrigin = crossOrigin; 1731 } 1732 1733 image.src = url; 1734 1735 /** 1736 * Override img element styles 1737 * Add `display:block` to avoid margin top issue 1738 * Add `height:auto` to override `height` attribute on IE8 1739 * (Occur only when margin-top <= -height) 1740 */ 1741 1742 image.style.cssText = ( 1743 'display:block;' + 1744 'width:100%;' + 1745 'height:auto;' + 1746 'min-width:0!important;' + 1747 'min-height:0!important;' + 1748 'max-width:none!important;' + 1749 'max-height:none!important;' + 1750 'image-orientation:0deg!important;"' 1751 ); 1752 1753 empty(element); 1754 appendChild(element, image); 1755 }); 1756 }, 1757 1758 resetPreview: function () { 1759 each(this.previews, function (element) { 1760 var data = getData(element, DATA_PREVIEW); 1761 1762 setStyle(element, { 1763 width: data.width, 1764 height: data.height 1765 }); 1766 1767 element.innerHTML = data.html; 1768 removeData(element, DATA_PREVIEW); 1769 }); 1770 }, 1771 1772 preview: function () { 1773 var _this = this; 1774 var imageData = _this.imageData; 1775 var canvasData = _this.canvasData; 1776 var cropBoxData = _this.cropBoxData; 1777 var cropBoxWidth = cropBoxData.width; 1778 var cropBoxHeight = cropBoxData.height; 1779 var width = imageData.width; 1780 var height = imageData.height; 1781 var left = cropBoxData.left - canvasData.left - imageData.left; 1782 var top = cropBoxData.top - canvasData.top - imageData.top; 1783 var transform = getTransform(imageData); 1784 var transforms = { 1785 WebkitTransform: transform, 1786 msTransform: transform, 1787 transform: transform 1788 }; 1789 1790 if (!_this.cropped || _this.disabled) { 1791 return; 1792 } 1793 1794 setStyle(_this.image2, extend({ 1795 width: width, 1796 height: height, 1797 marginLeft: -left, 1798 marginTop: -top 1799 }, transforms)); 1800 1801 each(_this.previews, function (element) { 1802 var data = getData(element, DATA_PREVIEW); 1803 var originalWidth = data.width; 1804 var originalHeight = data.height; 1805 var newWidth = originalWidth; 1806 var newHeight = originalHeight; 1807 var ratio = 1; 1808 1809 if (cropBoxWidth) { 1810 ratio = originalWidth / cropBoxWidth; 1811 newHeight = cropBoxHeight * ratio; 1812 } 1813 1814 if (cropBoxHeight && newHeight > originalHeight) { 1815 ratio = originalHeight / cropBoxHeight; 1816 newWidth = cropBoxWidth * ratio; 1817 newHeight = originalHeight; 1818 } 1819 1820 setStyle(element, { 1821 width: newWidth, 1822 height: newHeight 1823 }); 1824 1825 setStyle(getByTag(element, 'img')[0], extend({ 1826 width: width * ratio, 1827 height: height * ratio, 1828 marginLeft: -left * ratio, 1829 marginTop: -top * ratio 1830 }, transforms)); 1831 }); 1832 }, 1833 1834 bind: function () { 1835 var _this = this; 1836 var options = _this.options; 1837 var element = _this.element; 1838 var cropper = _this.cropper; 1839 1840 if (isFunction(options.cropstart)) { 1841 addListener(element, EVENT_CROP_START, options.cropstart); 1842 } 1843 1844 if (isFunction(options.cropmove)) { 1845 addListener(element, EVENT_CROP_MOVE, options.cropmove); 1846 } 1847 1848 if (isFunction(options.cropend)) { 1849 addListener(element, EVENT_CROP_END, options.cropend); 1850 } 1851 1852 if (isFunction(options.crop)) { 1853 addListener(element, EVENT_CROP, options.crop); 1854 } 1855 1856 if (isFunction(options.zoom)) { 1857 addListener(element, EVENT_ZOOM, options.zoom); 1858 } 1859 1860 addListener(cropper, EVENT_MOUSE_DOWN, (_this._cropStart = proxy(_this.cropStart, _this))); 1861 1862 if (options.zoomable && options.zoomOnWheel) { 1863 addListener(cropper, EVENT_WHEEL, (_this._wheel = proxy(_this.wheel, _this))); 1864 } 1865 1866 if (options.toggleDragModeOnDblclick) { 1867 addListener(cropper, EVENT_DBLCLICK, (_this._dblclick = proxy(_this.dblclick, _this))); 1868 } 1869 1870 addListener(document, EVENT_MOUSE_MOVE, (_this._cropMove = proxy(_this.cropMove, _this))); 1871 addListener(document, EVENT_MOUSE_UP, (_this._cropEnd = proxy(_this.cropEnd, _this))); 1872 1873 if (options.responsive) { 1874 addListener(window, EVENT_RESIZE, (_this._resize = proxy(_this.resize, _this))); 1875 } 1876 }, 1877 1878 unbind: function () { 1879 var _this = this; 1880 var options = _this.options; 1881 var element = _this.element; 1882 var cropper = _this.cropper; 1883 1884 if (isFunction(options.cropstart)) { 1885 removeListener(element, EVENT_CROP_START, options.cropstart); 1886 } 1887 1888 if (isFunction(options.cropmove)) { 1889 removeListener(element, EVENT_CROP_MOVE, options.cropmove); 1890 } 1891 1892 if (isFunction(options.cropend)) { 1893 removeListener(element, EVENT_CROP_END, options.cropend); 1894 } 1895 1896 if (isFunction(options.crop)) { 1897 removeListener(element, EVENT_CROP, options.crop); 1898 } 1899 1900 if (isFunction(options.zoom)) { 1901 removeListener(element, EVENT_ZOOM, options.zoom); 1902 } 1903 1904 removeListener(cropper, EVENT_MOUSE_DOWN, _this._cropStart); 1905 1906 if (options.zoomable && options.zoomOnWheel) { 1907 removeListener(cropper, EVENT_WHEEL, _this._wheel); 1908 } 1909 1910 if (options.toggleDragModeOnDblclick) { 1911 removeListener(cropper, EVENT_DBLCLICK, _this._dblclick); 1912 } 1913 1914 removeListener(document, EVENT_MOUSE_MOVE, _this._cropMove); 1915 removeListener(document, EVENT_MOUSE_UP, _this._cropEnd); 1916 1917 if (options.responsive) { 1918 removeListener(window, EVENT_RESIZE, _this._resize); 1919 } 1920 }, 1921 1922 resize: function () { 1923 var _this = this; 1924 var restore = _this.options.restore; 1925 var container = _this.container; 1926 var containerData = _this.containerData; 1927 var canvasData; 1928 var cropBoxData; 1929 var ratio; 1930 1931 // Check `container` is necessary for IE8 1932 if (_this.disabled || !containerData) { 1933 return; 1934 } 1935 1936 ratio = container.offsetWidth / containerData.width; 1937 1938 // Resize when width changed or height changed 1939 if (ratio !== 1 || container.offsetHeight !== containerData.height) { 1940 if (restore) { 1941 canvasData = _this.getCanvasData(); 1942 cropBoxData = _this.getCropBoxData(); 1943 } 1944 1945 _this.render(); 1946 1947 if (restore) { 1948 _this.setCanvasData(each(canvasData, function (n, i) { 1949 canvasData[i] = n * ratio; 1950 })); 1951 _this.setCropBoxData(each(cropBoxData, function (n, i) { 1952 cropBoxData[i] = n * ratio; 1953 })); 1954 } 1955 } 1956 }, 1957 1958 dblclick: function () { 1959 var _this = this; 1960 1961 if (_this.disabled) { 1962 return; 1963 } 1964 1965 _this.setDragMode(hasClass(_this.dragBox, CLASS_CROP) ? ACTION_MOVE : ACTION_CROP); 1966 }, 1967 1968 wheel: function (event) { 1969 var _this = this; 1970 var e = getEvent(event); 1971 var ratio = Number(_this.options.wheelZoomRatio) || 0.1; 1972 var delta = 1; 1973 1974 if (_this.disabled) { 1975 return; 1976 } 1977 1978 preventDefault(e); 1979 1980 // Limit wheel speed to prevent zoom too fast (#21) 1981 if (_this.wheeling) { 1982 return; 1983 } 1984 1985 _this.wheeling = true; 1986 1987 setTimeout(function () { 1988 _this.wheeling = false;
1989 }, 50); 1990 1991 if (e.deltaY) { 1992 delta = e.deltaY > 0 ? 1 : -1; 1993 } else if (e.wheelDelta) { 1994 delta = -e.wheelDelta / 120; 1995 } else if (e.detail) { 1996 delta = e.detail > 0 ? 1 : -1; 1997 } 1998 1999 _this.zoom(-delta * ratio, e); 2000 }, 2001 2002 cropStart: function (event) { 2003 var _this = this; 2004 var options = _this.options; 2005 var e = getEvent(event); 2006 var touches = e.touches; 2007 var touchesLength; 2008 var touch; 2009 var action; 2010 2011 if (_this.disabled) { 2012 return; 2013 } 2014 2015 if (touches) { 2016 touchesLength = touches.length; 2017 2018 if (touchesLength > 1) { 2019 if (options.zoomable && options.zoomOnTouch && touchesLength === 2) { 2020 touch = touches[1]; 2021 _this.startX2 = touch.pageX; 2022 _this.startY2 = touch.pageY; 2023 action = ACTION_ZOOM; 2024 } else { 2025 return; 2026 } 2027 } 2028 2029 touch = touches[0]; 2030 } 2031 2032 action = action || getData(e.target, DATA_ACTION); 2033 2034 if (REGEXP_ACTIONS.test(action)) { 2035 if (dispatchEvent(_this.element, EVENT_CROP_START, { 2036 originalEvent: e, 2037 action: action 2038 }) === false) { 2039 return; 2040 } 2041 2042 preventDefault(e); 2043 2044 _this.action = action; 2045 _this.cropping = false; 2046 2047 _this.startX = touch ? touch.pageX : e.pageX; 2048 _this.startY = touch ? touch.pageY : e.pageY; 2049 2050 if (action === ACTION_CROP) { 2051 _this.cropping = true; 2052 addClass(_this.dragBox, CLASS_MODAL); 2053 } 2054 } 2055 }, 2056 2057 cropMove: function (event) { 2058 var _this = this; 2059 var options = _this.options; 2060 var e = getEvent(event); 2061 var touches = e.touches; 2062 var action = _this.action; 2063 var touchesLength; 2064 var touch; 2065 2066 if (_this.disabled) { 2067 return; 2068 } 2069 2070 if (touches) { 2071 touchesLength = touches.length; 2072 2073 if (touchesLength > 1) { 2074 if (options.zoomable && options.zoomOnTouch && touchesLength === 2) { 2075 touch = touches[1]; 2076 _this.endX2 = touch.pageX; 2077 _this.endY2 = touch.pageY; 2078 } else { 2079 return; 2080 } 2081 } 2082 2083 touch = touches[0]; 2084 } 2085 2086 if (action) { 2087 if (dispatchEvent(_this.element, EVENT_CROP_MOVE, { 2088 originalEvent: e, 2089 action: action 2090 }) === false) { 2091 return; 2092 } 2093 2094 preventDefault(e); 2095 2096 _this.endX = touch ? touch.pageX : e.pageX; 2097 _this.endY = touch ? touch.pageY : e.pageY; 2098 2099 _this.change(e.shiftKey, action === ACTION_ZOOM ? e : null); 2100 } 2101 }, 2102 2103 cropEnd: function (event) { 2104 var _this = this; 2105 var options = _this.options; 2106 var e = getEvent(event); 2107 var action = _this.action; 2108 2109 if (_this.disabled) { 2110 return; 2111 } 2112 2113 if (action) { 2114 preventDefault(e); 2115 2116 if (_this.cropping) { 2117 _this.cropping = false; 2118 toggleClass(_this.dragBox, CLASS_MODAL, _this.cropped && options.modal); 2119 } 2120 2121 _this.action = ''; 2122 2123 dispatchEvent(_this.element, EVENT_CROP_END, { 2124 originalEvent: e, 2125 action: action 2126 }); 2127 } 2128 }, 2129 2130 change: function (shiftKey, originalEvent) { 2131 var _this = this; 2132 var options = _this.options; 2133 var aspectRatio = options.aspectRatio; 2134 var action = _this.action; 2135 var containerData = _this.containerData; 2136 var canvasData = _this.canvasData; 2137 var cropBoxData = _this.cropBoxData; 2138 var width = cropBoxData.width; 2139 var height = cropBoxData.height; 2140 var left = cropBoxData.left; 2141 var top = cropBoxData.top; 2142 var right = left + width; 2143 var bottom = top + height; 2144 var minLeft = 0; 2145 var minTop = 0; 2146 var maxWidth = containerData.width; 2147 var maxHeight = containerData.height; 2148 var renderable = true; 2149 var offset; 2150 var range; 2151 2152 // Locking aspect ratio in "free mode" by holding shift key 2153 if (!aspectRatio && shiftKey) { 2154 aspectRatio = width && height ? width / height : 1; 2155 } 2156 2157 if (_this.limited) { 2158 minLeft = cropBoxData.minLeft; 2159 minTop = cropBoxData.minTop; 2160 maxWidth = minLeft + min(containerData.width, canvasData.left + canvasData.width); 2161 maxHeight = minTop + min(containerData.height, canvasData.top + canvasData.height); 2162 } 2163 2164 range = { 2165 x: _this.endX - _this.startX, 2166 y: _this.endY - _this.startY 2167 }; 2168 2169 if (aspectRatio) { 2170 range.X = range.y * aspectRatio; 2171 range.Y = range.x / aspectRatio; 2172 } 2173 2174 switch (action) { 2175 // Move crop box 2176 case ACTION_ALL: 2177 left += range.x; 2178 top += range.y; 2179 break; 2180 2181 // Resize crop box 2182 case ACTION_EAST: 2183 if (range.x >= 0 && (right >= maxWidth || aspectRatio && 2184 (top <= minTop || bottom >= maxHeight))) { 2185 2186 renderable = false;
2187 break; 2188 } 2189 2190 width += range.x; 2191 2192 if (aspectRatio) { 2193 height = width / aspectRatio; 2194 top -= range.Y / 2; 2195 } 2196 2197 if (width < 0) { 2198 action = ACTION_WEST; 2199 width = 0; 2200 } 2201 2202 break; 2203 2204 case ACTION_NORTH: 2205 if (range.y <= 0 && (top <= minTop || aspectRatio && 2206 (left <= minLeft || right >= maxWidth))) { 2207 2208 renderable = false; 2209 break; 2210 } 2211 2212 height -= range.y; 2213 top += range.y; 2214 2215 if (aspectRatio) { 2216 width = height * aspectRatio; 2217 left += range.X / 2; 2218 } 2219 2220 if (height < 0) { 2221 action = ACTION_SOUTH; 2222 height = 0; 2223 } 2224 2225 break; 2226 2227 case ACTION_WEST: 2228 if (range.x <= 0 && (left <= minLeft || aspectRatio && 2229 (top <= minTop || bottom >= maxHeight))) { 2230 2231 renderable = false; 2232 break; 2233 } 2234 2235 width -= range.x; 2236 left += range.x; 2237 2238 if (aspectRatio) { 2239 height = width / aspectRatio; 2240 top += range.Y / 2; 2241 } 2242 2243 if (width < 0) { 2244 action = ACTION_EAST; 2245 width = 0; 2246 } 2247 2248 break; 2249 2250 case ACTION_SOUTH: 2251 if (range.y >= 0 && (bottom >= maxHeight || aspectRatio && 2252 (left <= minLeft || right >= maxWidth))) { 2253 2254 renderable = false; 2255 break; 2256 } 2257 2258 height += range.y; 2259 2260 if (aspectRatio) { 2261 width = height * aspectRatio; 2262 left -= range.X / 2; 2263 } 2264 2265 if (height < 0) { 2266 action = ACTION_NORTH; 2267 height = 0; 2268 } 2269 2270 break; 2271 2272 case ACTION_NORTH_EAST: 2273 if (aspectRatio) { 2274 if (range.y <= 0 && (top <= minTop || right >= maxWidth)) { 2275 renderable = false; 2276 break; 2277 } 2278 2279 height -= range.y; 2280 top += range.y; 2281 width = height * aspectRatio; 2282 } else { 2283 if (range.x >= 0) { 2284 if (right < maxWidth) { 2285 width += range.x; 2286 } else if (range.y <= 0 && top <= minTop) { 2287 renderable = false; 2288 } 2289 } else { 2290 width += range.x; 2291 } 2292 2293 if (range.y <= 0) { 2294 if (top > minTop) { 2295 height -= range.y; 2296 top += range.y; 2297 } 2298 } else { 2299 height -= range.y; 2300 top += range.y; 2301 } 2302 } 2303 2304 if (width < 0 && height < 0) { 2305 action = ACTION_SOUTH_WEST; 2306 height = 0; 2307 width = 0; 2308 } else if (width < 0) { 2309 action = ACTION_NORTH_WEST; 2310 width = 0; 2311 } else if (height < 0) { 2312 action = ACTION_SOUTH_EAST; 2313 height = 0; 2314 } 2315 2316 break; 2317 2318 case ACTION_NORTH_WEST: 2319 if (aspectRatio) { 2320 if (range.y <= 0 && (top <= minTop || left <= minLeft)) { 2321 renderable = false; 2322 break; 2323 } 2324 2325 height -= range.y; 2326 top += range.y; 2327 width = height * aspectRatio; 2328 left += range.X; 2329 } else { 2330 if (range.x <= 0) { 2331 if (left > minLeft) { 2332 width -= range.x; 2333 left += range.x; 2334 } else if (range.y <= 0 && top <= minTop) { 2335 renderable = false; 2336 } 2337 } else { 2338 width -= range.x; 2339 left += range.x; 2340 } 2341 2342 if (range.y <= 0) { 2343 if (top > minTop) { 2344 height -= range.y; 2345 top += range.y; 2346 } 2347 } else { 2348 height -= range.y; 2349 top += range.y; 2350 } 2351 } 2352 2353 if (width < 0 && height < 0) { 2354 action = ACTION_SOUTH_EAST; 2355 height = 0; 2356 width = 0; 2357 } else if (width < 0) { 2358 action = ACTION_NORTH_EAST; 2359 width = 0; 2360 } else if (height < 0) { 2361 action = ACTION_SOUTH_WEST; 2362 height = 0; 2363 } 2364 2365 break; 2366 2367 case ACTION_SOUTH_WEST: 2368 if (aspectRatio) { 2369 if (range.x <= 0 && (left <= minLeft || bottom >= maxHeight)) { 2370 renderable = false;
2371 break; 2372 } 2373 2374 width -= range.x; 2375 left += range.x; 2376 height = width / aspectRatio; 2377 } else { 2378 if (range.x <= 0) { 2379 if (left > minLeft) { 2380 width -= range.x; 2381 left += range.x; 2382 } else if (range.y >= 0 && bottom >= maxHeight) { 2383 renderable = false; 2384 } 2385 } else { 2386 width -= range.x; 2387 left += range.x; 2388 } 2389 2390 if (range.y >= 0) { 2391 if (bottom < maxHeight) { 2392 height += range.y; 2393 } 2394 } else { 2395 height += range.y; 2396 } 2397 } 2398 2399 if (width < 0 && height < 0) { 2400 action = ACTION_NORTH_EAST; 2401 height = 0; 2402 width = 0; 2403 } else if (width < 0) { 2404 action = ACTION_SOUTH_EAST; 2405 width = 0; 2406 } else if (height < 0) { 2407 action = ACTION_NORTH_WEST; 2408 height = 0; 2409 } 2410 2411 break; 2412 2413 case ACTION_SOUTH_EAST: 2414 if (aspectRatio) { 2415 if (range.x >= 0 && (right >= maxWidth || bottom >= maxHeight)) { 2416 renderable = false; 2417 break; 2418 } 2419 2420 width += range.x; 2421 height = width / aspectRatio; 2422 } else { 2423 if (range.x >= 0) { 2424 if (right < maxWidth) { 2425 width += range.x; 2426 } else if (range.y >= 0 && bottom >= maxHeight) { 2427 renderable = false; 2428 } 2429 } else { 2430 width += range.x; 2431 } 2432 2433 if (range.y >= 0) { 2434 if (bottom < maxHeight) { 2435 height += range.y; 2436 } 2437 } else { 2438 height += range.y; 2439 } 2440 } 2441 2442 if (width < 0 && height < 0) { 2443 action = ACTION_NORTH_WEST; 2444 height = 0; 2445 width = 0; 2446 } else if (width < 0) { 2447 action = ACTION_SOUTH_WEST; 2448 width = 0; 2449 } else if (height < 0) { 2450 action = ACTION_NORTH_EAST; 2451 height = 0; 2452 } 2453 2454 break; 2455 2456 // Move canvas 2457 case ACTION_MOVE: 2458 _this.move(range.x, range.y); 2459 renderable = false; 2460 break; 2461 2462 // Zoom canvas 2463 case ACTION_ZOOM: 2464 _this.zoom((function (x1, y1, x2, y2) { 2465 var z1 = sqrt(x1 * x1 + y1 * y1); 2466 var z2 = sqrt(x2 * x2 + y2 * y2); 2467 2468 return (z2 - z1) / z1; 2469 })( 2470 abs(_this.startX - _this.startX2), 2471 abs(_this.startY - _this.startY2), 2472 abs(_this.endX - _this.endX2),
2473 abs(_this.endY - _this.endY2) 2474 ), originalEvent); 2475 _this.startX2 = _this.endX2; 2476 _this.startY2 = _this.endY2; 2477 renderable = false; 2478 break; 2479 2480 // Create crop box 2481 case ACTION_CROP: 2482 if (!range.x || !range.y) { 2483 renderable = false; 2484 break; 2485 } 2486 2487 offset = getOffset(_this.cropper); 2488 left = _this.startX - offset.left; 2489 top = _this.startY - offset.top; 2490 width = cropBoxData.minWidth; 2491 height = cropBoxData.minHeight; 2492 2493 if (range.x > 0) { 2494 action = range.y > 0 ? ACTION_SOUTH_EAST : ACTION_NORTH_EAST; 2495 } else if (range.x < 0) { 2496 left -= width; 2497 action = range.y > 0 ? ACTION_SOUTH_WEST : ACTION_NORTH_WEST; 2498 } 2499 2500 if (range.y < 0) { 2501 top -= height; 2502 } 2503 2504 // Show the crop box if is hidden 2505 if (!_this.cropped) { 2506 removeClass(_this.cropBox, CLASS_HIDDEN); 2507 _this.cropped = true; 2508 2509 if (_this.limited) { 2510 _this.limitCropBox(true, true); 2511 } 2512 } 2513 2514 break; 2515 2516 // No default 2517 } 2518 2519 if (renderable) { 2520 cropBoxData.width = width; 2521 cropBoxData.height = height; 2522 cropBoxData.left = left; 2523 cropBoxData.top = top; 2524 _this.action = action; 2525 2526 _this.renderCropBox(); 2527 } 2528 2529 // Override 2530 _this.startX = _this.endX; 2531 _this.startY = _this.endY; 2532 }, 2533 2534 // Show the crop box manually 2535 crop: function () { 2536 var _this = this; 2537 2538 if (_this.built && !_this.disabled) { 2539 if (!_this.cropped) { 2540 _this.cropped = true; 2541 _this.limitCropBox(true, true); 2542 2543 if (_this.options.modal) { 2544 addClass(_this.dragBox, CLASS_MODAL); 2545 } 2546 2547 removeClass(_this.cropBox, CLASS_HIDDEN); 2548 } 2549 2550 _this.setCropBoxData(_this.initialCropBoxData); 2551 } 2552 2553 return _this; 2554 }, 2555 2556 // Reset the image and crop box to their initial states 2557 reset: function () { 2558 var _this = this; 2559 2560 if (_this.built && !_this.disabled) { 2561 _this.imageData = extend({}, _this.initialImageData); 2562 _this.canvasData = extend({}, _this.initialCanvasData); 2563 _this.cropBoxData = extend({}, _this.initialCropBoxData); 2564 2565 _this.renderCanvas(); 2566 2567 if (_this.cropped) { 2568 _this.renderCropBox(); 2569 } 2570 } 2571 2572 return _this; 2573 }, 2574 2575 // Clear the crop box 2576 clear: function () { 2577 var _this = this; 2578 2579 if (_this.cropped && !_this.disabled) { 2580 extend(_this.cropBoxData, { 2581 left: 0, 2582 top: 0, 2583 width: 0, 2584 height: 0 2585 }); 2586 2587 _this.cropped = false; 2588 _this.renderCropBox(); 2589 2590 _this.limitCanvas(); 2591 2592 // Render canvas after crop box rendered 2593 _this.renderCanvas(); 2594 2595 removeClass(_this.dragBox, CLASS_MODAL); 2596 addClass(_this.cropBox, CLASS_HIDDEN); 2597 } 2598 2599 return _this; 2600 }, 2601 2602 /** 2603 * Replace the image's src and rebuild the cropper 2604 * 2605 * @param {String} url 2606 * @param {Boolean} onlyColorChanged (optional) 2607 */ 2608 replace: function (url, onlyColorChanged) { 2609 var _this = this; 2610 2611 if (!_this.disabled && url) { 2612 if (_this.isImg) { 2613 _this.element.src = url; 2614 } 2615 2616 if (onlyColorChanged) { 2617 _this.url = url; 2618 _this.image.src = url; 2619 2620 if (_this.built) { 2621 _this.image2.src = url; 2622 2623 each(_this.previews, function (element) { 2624 getByTag(element, 'img')[0].src = url; 2625 }); 2626 } 2627 } else { 2628 if (_this.isImg) { 2629 _this.replaced = true; 2630 } 2631 2632 // Clear previous data 2633 _this.options.data = null; 2634 _this.load(url); 2635 } 2636 } 2637 2638 return _this; 2639 }, 2640 2641 // Enable (unfreeze) the cropper 2642 enable: function () { 2643 var _this = this; 2644 2645 if (_this.built) { 2646 _this.disabled = false; 2647 removeClass(_this.cropper, CLASS_DISABLED); 2648 } 2649 2650 return _this; 2651 }, 2652 2653 // Disable (freeze) the cropper 2654 disable: function () { 2655 var _this = this; 2656 2657 if (_this.built) { 2658 _this.disabled = true; 2659 addClass(_this.cropper, CLASS_DISABLED); 2660 } 2661 2662 return _this; 2663 }, 2664 2665 // Destroy the cropper and remove the instance from the image 2666 destroy: function () { 2667 var _this = this; 2668 var element = _this.element; 2669 var image = _this.image; 2670 2671 if (_this.ready) { 2672 if (_this.isImg && _this.replaced) { 2673 element.src = _this.originalUrl; 2674 } 2675 2676 _this.unbuild(); 2677 removeClass(element, CLASS_HIDDEN); 2678 } else { 2679 if (_this.isImg) { 2680 removeListener(element, EVENT_LOAD, _this.start); 2681 } else if (image) { 2682 removeChild(image); 2683 } 2684 } 2685 2686 removeData(element, NAMESPACE); 2687 2688 return _this; 2689 }, 2690 2691 /** 2692 * Move the canvas with relative offsets 2693 * 2694 * @param {Number} offsetX 2695 * @param {Number} offsetY (optional) 2696 */ 2697 move: function (offsetX, offsetY) { 2698 var _this = this; 2699 var canvasData = _this.canvasData; 2700 2701 return _this.moveTo( 2702 isUndefined(offsetX) ? offsetX : canvasData.left + Number(offsetX), 2703 isUndefined(offsetY) ? offsetY : canvasData.top + Number(offsetY) 2704 ); 2705 }, 2706 2707 /** 2708 * Move the canvas to an absolute point 2709 * 2710 * @param {Number} x 2711 * @param {Number} y (optional) 2712 */ 2713 moveTo: function (x, y) { 2714 var _this = this; 2715 var canvasData = _this.canvasData; 2716 var changed = false;
2717 2718 // If "y" is not present, its default value is "x" 2719 if (isUndefined(y)) { 2720 y = x; 2721 } 2722 2723 x = Number(x); 2724 y = Number(y); 2725 2726 if (_this.built && !_this.disabled && _this.options.movable) { 2727 if (isNumber(x)) { 2728 canvasData.left = x; 2729 changed = true; 2730 } 2731 2732 if (isNumber(y)) { 2733 canvasData.top = y; 2734 changed = true; 2735 } 2736 2737 if (changed) { 2738 _this.renderCanvas(true); 2739 } 2740 } 2741 2742 return _this; 2743 }, 2744 2745 /** 2746 * Zoom the canvas with a relative ratio 2747 * 2748 * @param {Number} ratio 2749 * @param {Event} _originalEvent (private) 2750 */ 2751 zoom: function (ratio, _originalEvent) { 2752 var _this = this; 2753 var canvasData = _this.canvasData; 2754 2755 ratio = Number(ratio); 2756 2757 if (ratio < 0) { 2758 ratio = 1 / (1 - ratio); 2759 } else { 2760 ratio = 1 + ratio; 2761 } 2762 2763 return _this.zoomTo(canvasData.width * ratio / canvasData.naturalWidth, _originalEvent); 2764 }, 2765 2766 /** 2767 * Zoom the canvas to an absolute ratio 2768 * 2769 * @param {Number} ratio 2770 * @param {Event} _originalEvent (private) 2771 */ 2772 zoomTo: function (ratio, _originalEvent) { 2773 var _this = this; 2774 var options = _this.options; 2775 var canvasData = _this.canvasData; 2776 var width = canvasData.width; 2777 var height = canvasData.height; 2778 var naturalWidth = canvasData.naturalWidth; 2779 var naturalHeight = canvasData.naturalHeight; 2780 var newWidth; 2781 var newHeight; 2782 var offset; 2783 var center; 2784 2785 ratio = Number(ratio); 2786 2787 if (ratio >= 0 && _this.built && !_this.disabled && options.zoomable) { 2788 newWidth = naturalWidth * ratio; 2789 newHeight = naturalHeight * ratio; 2790 2791 if (dispatchEvent(_this.element, EVENT_ZOOM, { 2792 originalEvent: _originalEvent, 2793 oldRatio: width / naturalWidth, 2794 ratio: newWidth / naturalWidth 2795 }) === false) { 2796 return _this; 2797 } 2798 2799 if (_originalEvent) { 2800 offset = getOffset(_this.cropper); 2801 center = _originalEvent.touches ? getTouchesCenter(_originalEvent.touches) : { 2802 pageX: _originalEvent.pageX, 2803 pageY: _originalEvent.pageY 2804 }; 2805 2806 // Zoom from the triggering point of the event 2807 canvasData.left -= (newWidth - width) * ( 2808 ((center.pageX - offset.left) - canvasData.left) / width 2809 ); 2810 canvasData.top -= (newHeight - height) * ( 2811 ((center.pageY - offset.top) - canvasData.top) / height 2812 ); 2813 } else { 2814 2815 // Zoom from the center of the canvas 2816 canvasData.left -= (newWidth - width) / 2; 2817 canvasData.top -= (newHeight - height) / 2; 2818 } 2819 2820 canvasData.width = newWidth; 2821 canvasData.height = newHeight; 2822 _this.renderCanvas(true); 2823 } 2824 2825 return _this; 2826 }, 2827 2828 /** 2829 * Rotate the canvas with a relative degree 2830 * 2831 * @param {Number} degree 2832 */ 2833 rotate: function (degree) { 2834 var _this = this; 2835 2836 return _this.rotateTo((_this.imageData.rotate || 0) + Number(degree)); 2837 }, 2838 2839 /** 2840 * Rotate the canvas to an absolute degree 2841 * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate() 2842 * 2843 * @param {Number} degree 2844 */ 2845 rotateTo: function (degree) { 2846 var _this = this; 2847 2848 degree = Number(degree); 2849 2850 if (isNumber(degree) && _this.built && !_this.disabled && _this.options.rotatable) { 2851 _this.imageData.rotate = degree % 360; 2852 _this.rotated = true; 2853 _this.renderCanvas(true); 2854 } 2855 2856 return _this; 2857 }, 2858 2859 /** 2860 * Scale the image 2861 * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale() 2862 * 2863 * @param {Number} scaleX 2864 * @param {Number} scaleY (optional) 2865 */
2866 scale: function (scaleX, scaleY) { 2867 var _this = this; 2868 var imageData = _this.imageData; 2869 var changed = false; 2870 2871 // If "scaleY" is not present, its default value is "scaleX" 2872 if (isUndefined(scaleY)) { 2873 scaleY = scaleX; 2874 } 2875 2876 scaleX = Number(scaleX); 2877 scaleY = Number(scaleY); 2878 2879 if (_this.built && !_this.disabled && _this.options.scalable) { 2880 if (isNumber(scaleX)) { 2881 imageData.scaleX = scaleX; 2882 changed = true; 2883 } 2884 2885 if (isNumber(scaleY)) { 2886 imageData.scaleY = scaleY; 2887 changed = true; 2888 } 2889 2890 if (changed) { 2891 _this.renderImage(true); 2892 } 2893 } 2894 2895 return _this; 2896 }, 2897 2898 /** 2899 * Scale the abscissa of the image 2900 * 2901 * @param {Number} scaleX 2902 */ 2903 scaleX: function (scaleX) { 2904 var _this = this; 2905 var scaleY = _this.imageData.scaleY; 2906 2907 return _this.scale(scaleX, isNumber(scaleY) ? scaleY : 1); 2908 }, 2909 2910 /** 2911 * Scale the ordinate of the image 2912 * 2913 * @param {Number} scaleY 2914 */ 2915 scaleY: function (scaleY) { 2916 var _this = this; 2917 var scaleX = _this.imageData.scaleX; 2918 2919 return _this.scale(isNumber(scaleX) ? scaleX : 1, scaleY); 2920 }, 2921 2922 /** 2923 * Get the cropped area position and size data (base on the original image) 2924 * 2925 * @param {Boolean} rounded (optional) 2926 * @return {Object} data 2927 */ 2928 getData: function (rounded) { 2929 var _this = this; 2930 var options = _this.options; 2931 var imageData = _this.imageData; 2932 var canvasData = _this.canvasData; 2933 var cropBoxData = _this.cropBoxData; 2934 var ratio; 2935 var data; 2936 2937 if (_this.built && _this.cropped) { 2938 data = { 2939 x: cropBoxData.left - canvasData.left, 2940 y: cropBoxData.top - canvasData.top, 2941 width: cropBoxData.width, 2942 height: cropBoxData.height 2943 }; 2944 2945 ratio = imageData.width / imageData.naturalWidth; 2946 2947 each(data, function (n, i) { 2948 n = n / ratio;
2949 data[i] = rounded ? round(n) : n; 2950 }); 2951 2952 } else { 2953 data = { 2954 x: 0, 2955 y: 0, 2956 width: 0, 2957 height: 0 2958 }; 2959 } 2960 2961 if (options.rotatable) { 2962 data.rotate = imageData.rotate || 0; 2963 } 2964 2965 if (options.scalable) { 2966 data.scaleX = imageData.scaleX || 1; 2967 data.scaleY = imageData.scaleY || 1; 2968 } 2969 2970 return data; 2971 }, 2972 2973 /** 2974 * Set the cropped area position and size with new data 2975 * 2976 * @param {Object} data 2977 */ 2978 setData: function (data) { 2979 var _this = this; 2980 var options = _this.options; 2981 var imageData = _this.imageData; 2982 var canvasData = _this.canvasData; 2983 var cropBoxData = {}; 2984 var rotated; 2985 var scaled; 2986 var ratio; 2987 2988 if (isFunction(data)) { 2989 data = data.call(_this.element); 2990 } 2991 2992 if (_this.built && !_this.disabled && isPlainObject(data)) { 2993 if (options.rotatable) { 2994 if (isNumber(data.rotate) && data.rotate !== imageData.rotate) { 2995 imageData.rotate = data.rotate; 2996 _this.rotated = rotated = true; 2997 } 2998 } 2999 3000 if (options.scalable) { 3001 if (isNumber(data.scaleX) && data.scaleX !== imageData.scaleX) { 3002 imageData.scaleX = data.scaleX; 3003 scaled = true; 3004 } 3005 3006 if (isNumber(data.scaleY) && data.scaleY !== imageData.scaleY) { 3007 imageData.scaleY = data.scaleY; 3008 scaled = true; 3009 } 3010 } 3011 3012 if (rotated) { 3013 _this.renderCanvas(); 3014 } else if (scaled) { 3015 _this.renderImage(); 3016 } 3017 3018 ratio = imageData.width / imageData.naturalWidth; 3019 3020 if (isNumber(data.x)) { 3021 cropBoxData.left = data.x * ratio + canvasData.left; 3022 } 3023 3024 if (isNumber(data.y)) { 3025 cropBoxData.top = data.y * ratio + canvasData.top; 3026 } 3027 3028 if (isNumber(data.width)) { 3029 cropBoxData.width = data.width * ratio; 3030 } 3031 3032 if (isNumber(data.height)) { 3033 cropBoxData.height = data.height * ratio; 3034 } 3035 3036 _this.setCropBoxData(cropBoxData); 3037 } 3038 3039 return _this; 3040 }, 3041 3042 /** 3043 * Get the container size data 3044 * 3045 * @return {Object} data 3046 */ 3047 getContainerData: function () { 3048 var _this = this; 3049 3050 return _this.built ? _this.containerData : {}; 3051 }, 3052 3053 /** 3054 * Get the image position and size data 3055 * 3056 * @return {Object} data 3057 */ 3058 getImageData: function () { 3059 var _this = this; 3060 3061 return _this.ready ? _this.imageData : {}; 3062 }, 3063 3064 /** 3065 * Get the canvas position and size data 3066 * 3067 * @return {Object} data 3068 */ 3069 getCanvasData: function () { 3070 var _this = this; 3071 var canvasData = _this.canvasData; 3072 var data = {}; 3073 3074 if (_this.built) { 3075 each([ 3076 'left', 3077 'top', 3078 'width', 3079 'height', 3080 'naturalWidth', 3081 'naturalHeight' 3082 ], function (n) { 3083 data[n] = canvasData[n]; 3084 }); 3085 } 3086 3087 return data; 3088 }, 3089 3090 /** 3091 * Set the canvas position and size with new data 3092 * 3093 * @param {Object} data 3094 */ 3095 setCanvasData: function (data) { 3096 var _this = this; 3097 var canvasData = _this.canvasData; 3098 var aspectRatio = canvasData.aspectRatio; 3099 3100 if (isFunction(data)) { 3101 data = data.call(_this.element); 3102 } 3103 3104 if (_this.built && !_this.disabled && isPlainObject(data)) { 3105 if (isNumber(data.left)) { 3106 canvasData.left = data.left; 3107 } 3108 3109 if (isNumber(data.top)) { 3110 canvasData.top = data.top; 3111 } 3112 3113 if (isNumber(data.width)) { 3114 canvasData.width = data.width; 3115 canvasData.height = data.width / aspectRatio; 3116 } else if (isNumber(data.height)) { 3117 canvasData.height = data.height; 3118 canvasData.width = data.height * aspectRatio; 3119 } 3120 3121 _this.renderCanvas(true); 3122 } 3123 3124 return _this; 3125 }, 3126 3127 /** 3128 * Get the crop box position and size data 3129 * 3130 * @return {Object} data 3131 */ 3132 getCropBoxData: function () { 3133 var _this = this; 3134 var cropBoxData = _this.cropBoxData; 3135 var data; 3136 3137 if (_this.built && _this.cropped) { 3138 data = { 3139 left: cropBoxData.left, 3140 top: cropBoxData.top, 3141 width: cropBoxData.width, 3142 height: cropBoxData.height 3143 }; 3144 } 3145 3146 return data || {}; 3147 }, 3148 3149 /** 3150 * Set the crop box position and size with new data 3151 * 3152 * @param {Object} data 3153 */ 3154 setCropBoxData: function (data) { 3155 var _this = this; 3156 var cropBoxData = _this.cropBoxData; 3157 var aspectRatio = _this.options.aspectRatio; 3158 var widthChanged; 3159 var heightChanged; 3160 3161 if (isFunction(data)) { 3162 data = data.call(_this.element); 3163 } 3164 3165 if (_this.built && _this.cropped && !_this.disabled && isPlainObject(data)) { 3166 3167 if (isNumber(data.left)) { 3168 cropBoxData.left = data.left; 3169 } 3170 3171 if (isNumber(data.top)) { 3172 cropBoxData.top = data.top; 3173 } 3174 3175 if (isNumber(data.width)) { 3176 widthChanged = true; 3177 cropBoxData.width = data.width; 3178 } 3179 3180 if (isNumber(data.height)) { 3181 heightChanged = true; 3182 cropBoxData.height = data.height; 3183 } 3184 3185 if (aspectRatio) { 3186 if (widthChanged) { 3187 cropBoxData.height = cropBoxData.width / aspectRatio; 3188 } else if (heightChanged) { 3189 cropBoxData.width = cropBoxData.height * aspectRatio; 3190 } 3191 } 3192 3193 _this.renderCropBox(); 3194 } 3195 3196 return _this; 3197 }, 3198 3199 /** 3200 * Get a canvas drawn the cropped image 3201 * 3202 * @param {Object} options (optional) 3203 * @return {HTMLCanvasElement} canvas 3204 */ 3205 getCroppedCanvas: function (options) { 3206 var _this = this; 3207 var originalWidth; 3208 var originalHeight; 3209 var canvasWidth; 3210 var canvasHeight; 3211 var scaledWidth; 3212 var scaledHeight; 3213 var scaledRatio; 3214 var aspectRatio; 3215 var canvas; 3216 var context; 3217 var data; 3218 3219 if (!_this.built || !_this.cropped || !SUPPORT_CANVAS) { 3220 return; 3221 } 3222 3223 if (!isPlainObject(options)) { 3224 options = {}; 3225 } 3226 3227 data = _this.getData(); 3228 originalWidth = data.width; 3229 originalHeight = data.height; 3230 aspectRatio = originalWidth / originalHeight; 3231 3232 if (isPlainObject(options)) { 3233 scaledWidth = options.width; 3234 scaledHeight = options.height; 3235 3236 if (scaledWidth) { 3237 scaledHeight = scaledWidth / aspectRatio; 3238 scaledRatio = scaledWidth / originalWidth; 3239 } else if (scaledHeight) { 3240 scaledWidth = scaledHeight * aspectRatio; 3241 scaledRatio = scaledHeight / originalHeight; 3242 } 3243 } 3244 3245 // The canvas element will use `Math.floor` on a float number, so floor first 3246 canvasWidth = floor(scaledWidth || originalWidth); 3247 canvasHeight = floor(scaledHeight || originalHeight); 3248 3249 canvas = createElement('canvas'); 3250 canvas.width = canvasWidth; 3251 canvas.height = canvasHeight; 3252 context = canvas.getContext('2d'); 3253 3254 if (options.fillColor) { 3255 context.fillStyle = options.fillColor; 3256 context.fillRect(0, 0, canvasWidth, canvasHeight); 3257 } 3258 3259 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D.drawImage 3260 context.drawImage.apply(context, (function () { 3261 var source = getSourceCanvas(_this.image, _this.imageData); 3262 var sourceWidth = source.width; 3263 var sourceHeight = source.height; 3264 var canvasData = _this.canvasData; 3265 var params = [source]; 3266 3267 // Source canvas
3268 var srcX = data.x + canvasData.naturalWidth * (abs(data.scaleX || 1) - 1) / 2; 3269 var srcY = data.y + canvasData.naturalHeight * (abs(data.scaleY || 1) - 1) / 2; 3270 var srcWidth; 3271 var srcHeight; 3272 3273 // Destination canvas 3274 var dstX; 3275 var dstY; 3276 var dstWidth; 3277 var dstHeight; 3278 3279 if (srcX <= -originalWidth || srcX > sourceWidth) { 3280 srcX = srcWidth = dstX = dstWidth = 0; 3281 } else if (srcX <= 0) { 3282 dstX = -srcX; 3283 srcX = 0; 3284 srcWidth = dstWidth = min(sourceWidth, originalWidth + srcX); 3285 } else if (srcX <= sourceWidth) { 3286 dstX = 0; 3287 srcWidth = dstWidth = min(originalWidth, sourceWidth - srcX); 3288 } 3289 3290 if (srcWidth <= 0 || srcY <= -originalHeight || srcY > sourceHeight) { 3291 srcY = srcHeight = dstY = dstHeight = 0; 3292 } else if (srcY <= 0) { 3293 dstY = -srcY; 3294 srcY = 0; 3295 srcHeight = dstHeight = min(sourceHeight, originalHeight + srcY); 3296 } else if (srcY <= sourceHeight) { 3297 dstY = 0; 3298 srcHeight = dstHeight = min(originalHeight, sourceHeight - srcY); 3299 } 3300 3301 params.push(floor(srcX), floor(srcY), floor(srcWidth), floor(srcHeight)); 3302 3303 // Scale destination sizes 3304 if (scaledRatio) { 3305 dstX *= scaledRatio; 3306 dstY *= scaledRatio; 3307 dstWidth *= scaledRatio; 3308 dstHeight *= scaledRatio; 3309 } 3310 3311 // Avoid "IndexSizeError" in IE and Firefox 3312 if (dstWidth > 0 && dstHeight > 0) { 3313 params.push(floor(dstX), floor(dstY), floor(dstWidth), floor(dstHeight)); 3314 } 3315 3316 return params; 3317 }).call(_this)); 3318 3319 return canvas; 3320 }, 3321 3322 /** 3323 * Change the aspect ratio of the crop box 3324 * 3325 * @param {Number} aspectRatio 3326 */ 3327 setAspectRatio: function (aspectRatio) { 3328 var _this = this; 3329 var options = _this.options; 3330 3331 if (!_this.disabled && !isUndefined(aspectRatio)) { 3332 3333 // 0 -> NaN 3334 options.aspectRatio = max(0, aspectRatio) || NaN; 3335 3336 if (_this.built) { 3337 _this.initCropBox(); 3338 3339 if (_this.cropped) { 3340 _this.renderCropBox(); 3341 } 3342 } 3343 } 3344 3345 return _this; 3346 }, 3347 3348 /** 3349 * Change the drag mode 3350 * 3351 * @param {String} mode (optional) 3352 */ 3353 setDragMode: function (mode) { 3354 var _this = this; 3355 var options = _this.options; 3356 var dragBox = _this.dragBox; 3357 var face = _this.face; 3358 var croppable; 3359 var movable; 3360 3361 if (_this.ready && !_this.disabled) { 3362 croppable = mode === ACTION_CROP;
3363 movable = options.movable && mode === ACTION_MOVE; 3364 mode = (croppable || movable) ? mode : ACTION_NONE; 3365 3366 setData(dragBox, DATA_ACTION, mode); 3367 toggleClass(dragBox, CLASS_CROP, croppable); 3368 toggleClass(dragBox, CLASS_MOVE, movable); 3369 3370 if (!options.cropBoxMovable) { 3371 3372 // Sync drag mode to crop box when it is not movable 3373 setData(face, DATA_ACTION, mode); 3374 toggleClass(face, CLASS_CROP, croppable); 3375 toggleClass(face, CLASS_MOVE, movable); 3376 } 3377 } 3378 3379 return _this; 3380 } 3381 }; 3382 3383 Cropper.DEFAULTS = { 3384 3385 // Define the view mode of the cropper 3386 viewMode: 0, // 0, 1, 2, 3 3387 3388 // Define the dragging mode of the cropper 3389 dragMode: 'crop', // 'crop', 'move' or 'none' 3390 3391 // Define the aspect ratio of the crop box 3392 aspectRatio: NaN, 3393 3394 // An object with the previous cropping result data 3395 data: null, 3396 3397 // A selector for adding extra containers to preview 3398 preview: '', 3399 3400 // Re-render the cropper when resize the window 3401 responsive: true, 3402 3403 // Restore the cropped area after resize the window 3404 restore: true, 3405 3406 // Check if the current image is a cross-origin image 3407 checkCrossOrigin: true, 3408 3409 // Check the current image's Exif Orientation information 3410 checkOrientation: true, 3411 3412 // Show the black modal 3413 modal: true, 3414 3415 // Show the dashed lines for guiding 3416 guides: true, 3417 3418 // Show the center indicator for guiding 3419 center: true, 3420 3421 // Show the white modal to highlight the crop box 3422 highlight: true, 3423 3424 // Show the grid background 3425 background: true, 3426 3427 // Enable to crop the image automatically when initialize 3428 autoCrop: true, 3429 3430 // Define the percentage of automatic cropping area when initializes 3431 autoCropArea: 0.8, 3432 3433 // Enable to move the image 3434 movable: true, 3435 3436 // Enable to rotate the image 3437 rotatable: true, 3438 3439 // Enable to scale the image 3440 scalable: true, 3441 3442 // Enable to zoom the image 3443 zoomable: true, 3444 3445 // Enable to zoom the image by dragging touch 3446 zoomOnTouch: true, 3447 3448 // Enable to zoom the image by wheeling mouse 3449 zoomOnWheel: true, 3450 3451 // Define zoom ratio when zoom the image by wheeling mouse 3452 wheelZoomRatio: 0.1, 3453 3454 // Enable to move the crop box 3455 cropBoxMovable: true, 3456 3457 // Enable to resize the crop box 3458 cropBoxResizable: true, 3459 3460 // Toggle drag mode between "crop" and "move" when click twice on the cropper 3461 toggleDragModeOnDblclick: true, 3462 3463 // Size limitation 3464 minCanvasWidth: 0, 3465 minCanvasHeight: 0, 3466 minCropBoxWidth: 0, 3467 minCropBoxHeight: 0, 3468 minContainerWidth: 200, 3469 minContainerHeight: 100, 3470 3471 // Shortcuts of events 3472 build: null, 3473 built: null, 3474 cropstart: null, 3475 cropmove: null, 3476 cropend: null, 3477 crop: null, 3478 zoom: null 3479 }; 3480 3481 Cropper.TEMPLATE = (function (source, words) { 3482 words = words.split(','); 3483 3484 return source.replace(/\d+/g, function (i) { 3485 return words[i]; 3486 }); 3487 })('<0 6="5-container"><0 6="5-wrap-9"><0 6="5-canvas"></0></0><0 6="5-drag-9"></0><0 6="5-crop-9"><1 6="5-view-9"></1><1 6="5-8 8-h"></1><1 6="5-8 8-v"></1><1 6="5-center"></1><1 6="5-face"></1><1 6="5-7 7-e" 3-2="e"></1><1 6="5-7 7-n" 3-2="n"></1><1 6="5-7 7-w" 3-2="w"></1><1 6="5-7 7-s" 3-2="s"></1><1 6="5-4 4-e" 3-2="e"></1><1 6="5-4 4-n" 3-2="n"></1><1 6="5-4 4-w" 3-2="w"></1><1 6="5-4 4-s" 3-2="s"></1><1 6="5-4 4-ne" 3-2="ne"></1><1 6="5-4 4-nw" 3-2="nw"></1><1 6="5-4 4-sw" 3-2="sw"></1><1 6="5-4 4-se" 3-2="se"></1></0></0>', 'div,span,action,data,point,cropper,class,line,dashed,box'); 3488 3489 /*Cropper.TEMPLATE = ( 3490 '<div class="cropper-container">' + 3491 '<div class="cropper-wrap-box">' + 3492 '<div class="cropper-canvas"></div>' + 3493 '</div>' + 3494 '<div class="cropper-drag-box"></div>' + 3495 '<div class="cropper-crop-box">' + 3496 '<span class="cropper-view-box"></span>' + 3497 '<span class="cropper-dashed dashed-h"></span>' + 3498 '<span class="cropper-dashed dashed-v"></span>' + 3499 '<span class="cropper-center"></span>' + 3500 '<span class="cropper-face"></span>' + 3501 '<span class="cropper-line line-e" data-action="e"></span>' + 3502 '<span class="cropper-line line-n" data-action="n"></span>' + 3503 '<span class="cropper-line line-w" data-action="w"></span>' + 3504 '<span class="cropper-line line-s" data-action="s"></span>' + 3505 '<span class="cropper-point point-e" data-action="e"></span>' + 3506 '<span class="cropper-point point-n" data-action="n"></span>' + 3507 '<span class="cropper-point point-w" data-action="w"></span>' + 3508 '<span class="cropper-point point-s" data-action="s"></span>' + 3509 '<span class="cropper-point point-ne" data-action="ne"></span>' + 3510 '<span class="cropper-point point-nw" data-action="nw"></span>' + 3511 '<span class="cropper-point point-sw" data-action="sw"></span>' + 3512 '<span class="cropper-point point-se" data-action="se"></span>' + 3513 '</div>' + 3514 '</div>' 3515 );*/ 3516 3517 var _Cropper = window.Cropper; 3518 3519 Cropper.noConflict = function () { 3520 window.Cropper = _Cropper; 3521 return Cropper; 3522 }; 3523 3524 Cropper.setDefaults = function (options) { 3525 extend(Cropper.DEFAULTS, options); 3526 }; 3527 3528 if (typeof define === 'function' && define.amd) { 3529 define('cropper', [], function () { 3530 return Cropper; 3531 }); 3532 } 3533 3534 if (!noGlobal) { 3535 window.Cropper = Cropper; 3536 } 3537 3538 return Cropper; 3539 3540}); 3541 3542},{}],2:[function(require,module,exports){ 3543/*! 3544 * Draggabilly v2.1.0 3545 * Make that shiz draggable 3546 * http://draggabilly.desandro.com 3547 * MIT license 3548 */ 3549 3550/*jshint browser: true, strict: true, undef: true, unused: true */ 3551 3552( function( window, factory ) { 3553 // universal module definition 3554 /* jshint strict: false */ /*globals define, module, require */ 3555 if ( typeof define == 'function' && define.amd ) { 3556 // AMD 3557 define( [ 3558 'get-size/get-size', 3559 'unidragger/unidragger' 3560 ], 3561 function( getSize, Unidragger ) { 3562 return factory( window, getSize, Unidragger ); 3563 }); 3564 } else if ( typeof module == 'object' && module.exports ) { 3565 // CommonJS 3566 module.exports = factory( 3567 window, 3568 require('get-size'), 3569 require('unidragger') 3570 ); 3571 } else { 3572 // browser global 3573 window.Draggabilly = factory( 3574 window, 3575 window.getSize, 3576 window.Unidragger 3577 ); 3578 } 3579 3580}( window, function factory( window, getSize, Unidragger ) { 3581 3582'use strict'; 3583 3584// vars 3585var document = window.document; 3586 3587function noop() {} 3588 3589// -------------------------- helpers -------------------------- // 3590 3591// extend objects 3592function extend( a, b ) { 3593 for ( var prop in b ) { 3594 a[ prop ] = b[ prop ]; 3595 } 3596 return a; 3597} 3598 3599function isElement( obj ) { 3600 return obj instanceof HTMLElement; 3601} 3602 3603// -------------------------- requestAnimationFrame -------------------------- // 3604 3605// get rAF, prefixed, if present 3606var requestAnimationFrame = window.requestAnimationFrame || 3607 window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; 3608 3609// fallback to setTimeout 3610var lastTime = 0; 3611if ( !requestAnimationFrame ) { 3612 requestAnimationFrame = function( callback ) { 3613 var currTime = new Date().getTime(); 3614 var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); 3615 var id = setTimeout( callback, timeToCall ); 3616 lastTime = currTime + timeToCall; 3617 return id; 3618 }; 3619} 3620
3621// -------------------------- support -------------------------- // 3622 3623var docElem = document.documentElement; 3624var transformProperty = typeof docElem.style.transform == 'string' ? 3625 'transform' : 'WebkitTransform'; 3626 3627var jQuery = window.jQuery; 3628 3629// -------------------------- -------------------------- // 3630 3631function Draggabilly( element, options ) { 3632 // querySelector if string 3633 this.element = typeof element == 'string' ? 3634 document.querySelector( element ) : element; 3635 3636 if ( jQuery ) { 3637 this.$element = jQuery( this.element ); 3638 } 3639 3640 // options 3641 this.options = extend( {}, this.constructor.defaults ); 3642 this.option( options ); 3643 3644 this._create(); 3645} 3646 3647// inherit Unidragger methods 3648var proto = Draggabilly.prototype = Object.create( Unidragger.prototype ); 3649 3650Draggabilly.defaults = { 3651}; 3652 3653/** 3654 * set options 3655 * @param {Object} opts 3656 */ 3657proto.option = function( opts ) { 3658 extend( this.options, opts ); 3659}; 3660 3661proto._create = function() { 3662 3663 // properties 3664 this.position = {}; 3665 this._getPosition(); 3666 3667 this.startPoint = { x: 0, y: 0 }; 3668 this.dragPoint = { x: 0, y: 0 }; 3669 3670 this.startPosition = extend( {}, this.position ); 3671 3672 // set relative positioning 3673 var style = getComputedStyle( this.element ); 3674 if ( style.position != 'relative' && style.position != 'absolute' ) { 3675 this.element.style.position = 'relative'; 3676 } 3677 3678 this.enable(); 3679 this.setHandles(); 3680 3681}; 3682 3683/** 3684 * set this.handles and bind start events to 'em 3685 */ 3686proto.setHandles = function() { 3687 this.handles = this.options.handle ? 3688 this.element.querySelectorAll( this.options.handle ) : [ this.element ]; 3689 3690 this.bindHandles(); 3691}; 3692 3693/** 3694 * emits events via EvEmitter and jQuery events 3695 * @param {String} type - name of event 3696 * @param {Event} event - original event 3697 * @param {Array} args - extra arguments 3698 */ 3699proto.dispatchEvent = function( type, event, args ) { 3700 var emitArgs = [ event ].concat( args ); 3701 this.emitEvent( type, emitArgs ); 3702 var jQuery = window.jQuery; 3703 // trigger jQuery event 3704 if ( jQuery && this.$element ) { 3705 if ( event ) { 3706 // create jQuery event 3707 var $event = jQuery.Event( event ); 3708 $event.type = type; 3709 this.$element.trigger( $event, args ); 3710 } else { 3711 // just trigger with type if no event available 3712 this.$element.trigger( type, args ); 3713 } 3714 } 3715}; 3716 3717// -------------------------- position -------------------------- // 3718 3719// get x/y position from style 3720Draggabilly.prototype._getPosition = function() { 3721 var style = getComputedStyle( this.element ); 3722 var x = this._getPositionCoord( style.left, 'width' ); 3723 var y = this._getPositionCoord( style.top, 'height' ); 3724 // clean up 'auto' or other non-integer values 3725 this.position.x = isNaN( x ) ? 0 : x; 3726 this.position.y = isNaN( y ) ? 0 : y; 3727 3728 this._addTransformPosition( style ); 3729}; 3730 3731Draggabilly.prototype._getPositionCoord = function( styleSide, measure ) { 3732 if ( styleSide.indexOf('%') != -1 ) { 3733 // convert percent into pixel for Safari, #75 3734 var parentSize = getSize( this.element.parentNode ); 3735 return ( parseFloat( styleSide ) / 100 ) * parentSize[ measure ]; 3736 } 3737 3738 return parseInt( styleSide, 10 ); 3739}; 3740 3741// add transform: translate( x, y ) to position 3742proto._addTransformPosition = function( style ) { 3743 var transform = style[ transformProperty ]; 3744 // bail out if value is 'none' 3745 if ( transform.indexOf('matrix') !== 0 ) { 3746 return; 3747 } 3748 // split matrix(1, 0, 0, 1, x, y) 3749 var matrixValues = transform.split(','); 3750 // translate X value is in 12th or 4th position 3751 var xIndex = transform.indexOf('matrix3d') === 0 ? 12 : 4; 3752 var translateX = parseInt( matrixValues[ xIndex ], 10 ); 3753 // translate Y value is in 13th or 5th position 3754 var translateY = parseInt( matrixValues[ xIndex + 1 ], 10 ); 3755 this.position.x += translateX; 3756 this.position.y += translateY; 3757}; 3758 3759// -------------------------- events -------------------------- // 3760 3761/** 3762 * pointer start 3763 * @param {Event} event 3764 * @param {Event or Touch} pointer 3765 */ 3766proto.pointerDown = function( event, pointer ) { 3767 this._dragPointerDown( event, pointer ); 3768 // kludge to blur focused inputs in dragger 3769 var focused = document.activeElement; 3770 // do not blur body for IE10, metafizzy/flickity#117
3771 if ( focused && focused.blur && focused != document.body ) { 3772 focused.blur(); 3773 } 3774 // bind move and end events 3775 this._bindPostStartEvents( event ); 3776 this.element.classList.add('is-pointer-down'); 3777 this.dispatchEvent( 'pointerDown', event, [ pointer ] ); 3778}; 3779 3780/** 3781 * drag move 3782 * @param {Event} event 3783 * @param {Event or Touch} pointer 3784 */ 3785proto.pointerMove = function( event, pointer ) { 3786 var moveVector = this._dragPointerMove( event, pointer ); 3787 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] ); 3788 this._dragMove( event, pointer, moveVector ); 3789}; 3790 3791/** 3792 * drag start 3793 * @param {Event} event 3794 * @param {Event or Touch} pointer 3795 */ 3796proto.dragStart = function( event, pointer ) { 3797 if ( !this.isEnabled ) { 3798 return; 3799 } 3800 this._getPosition(); 3801 this.measureContainment(); 3802 // position _when_ drag began 3803 this.startPosition.x = this.position.x; 3804 this.startPosition.y = this.position.y; 3805 // reset left/top style 3806 this.setLeftTop(); 3807 3808 this.dragPoint.x = 0; 3809 this.dragPoint.y = 0; 3810 3811 this.element.classList.add('is-dragging'); 3812 this.dispatchEvent( 'dragStart', event, [ pointer ] ); 3813 // start animation 3814 this.animate(); 3815}; 3816 3817proto.measureContainment = function() { 3818 var containment = this.options.containment; 3819 if ( !containment ) { 3820 return; 3821 } 3822 3823 // use element if element 3824 var container = isElement( containment ) ? containment : 3825 // fallback to querySelector if string 3826 typeof containment == 'string' ? document.querySelector( containment ) : 3827 // otherwise just `true`, use the parent 3828 this.element.parentNode; 3829 3830 var elemSize = getSize( this.element ); 3831 var containerSize = getSize( container ); 3832 var elemRect = this.element.getBoundingClientRect(); 3833 var containerRect = container.getBoundingClientRect(); 3834 3835 var borderSizeX = containerSize.borderLeftWidth + containerSize.borderRightWidth; 3836 var borderSizeY = containerSize.borderTopWidth + containerSize.borderBottomWidth; 3837 3838 var position = this.relativeStartPosition = { 3839 x: elemRect.left - ( containerRect.left + containerSize.borderLeftWidth ), 3840 y: elemRect.top - ( containerRect.top + containerSize.borderTopWidth ) 3841 }; 3842 3843 this.containSize = { 3844 width: ( containerSize.width - borderSizeX ) - position.x - elemSize.width, 3845 height: ( containerSize.height - borderSizeY ) - position.y - elemSize.height 3846 }; 3847}; 3848 3849// ----- move event ----- // 3850 3851/** 3852 * drag move 3853 * @param {Event} event 3854 * @param {Event or Touch} pointer 3855 */ 3856proto.dragMove = function( event, pointer, moveVector ) { 3857 if ( !this.isEnabled ) { 3858 return; 3859 } 3860 var dragX = moveVector.x; 3861 var dragY = moveVector.y; 3862 3863 var grid = this.options.grid; 3864 var gridX = grid && grid[0]; 3865 var gridY = grid && grid[1]; 3866 3867 dragX = applyGrid( dragX, gridX ); 3868 dragY = applyGrid( dragY, gridY ); 3869 3870 dragX = this.containDrag( 'x', dragX, gridX ); 3871 dragY = this.containDrag( 'y', dragY, gridY ); 3872 3873 // constrain to axis 3874 dragX = this.options.axis == 'y' ? 0 : dragX; 3875 dragY = this.options.axis == 'x' ? 0 : dragY; 3876 3877 this.position.x = this.startPosition.x + dragX; 3878 this.position.y = this.startPosition.y + dragY; 3879 // set dragPoint properties 3880 this.dragPoint.x = dragX; 3881 this.dragPoint.y = dragY; 3882 3883 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] ); 3884}; 3885 3886function applyGrid( value, grid, method ) { 3887 method = method || 'round'; 3888 return grid ? Math[ method ]( value / grid ) * grid : value; 3889} 3890 3891proto.containDrag = function( axis, drag, grid ) { 3892 if ( !this.options.containment ) { 3893 return drag; 3894 } 3895 var measure = axis == 'x' ? 'width' : 'height'; 3896 3897 var rel = this.relativeStartPosition[ axis ]; 3898 var min = applyGrid( -rel, grid, 'ceil' ); 3899 var max = this.containSize[ measure ]; 3900 max = applyGrid( max, grid, 'floor' ); 3901 return Math.min( max, Math.max( min, drag ) ); 3902}; 3903 3904// ----- end event ----- // 3905 3906/** 3907 * pointer up 3908 * @param {Event} event 3909 * @param {Event or Touch} pointer 3910 */ 3911proto.pointerUp = function( event, pointer ) { 3912 this.element.classList.remove('is-pointer-down'); 3913 this.dispatchEvent( 'pointerUp', event, [ pointer ] ); 3914 this._dragPointerUp( event, pointer ); 3915}; 3916 3917/** 3918 * drag end 3919 * @param {Event} event 3920 * @param {Event or Touch} pointer 3921 */ 3922proto.dragEnd = function( event, pointer ) { 3923 if ( !this.isEnabled ) { 3924 return; 3925 } 3926 // use top left position when complete 3927 if ( transformProperty ) { 3928 this.element.style[ transformProperty ] = ''; 3929 this.setLeftTop(); 3930 } 3931 this.element.classList.remove('is-dragging'); 3932 this.dispatchEvent( 'dragEnd', event, [ pointer ] ); 3933}; 3934 3935// -------------------------- animation -------------------------- // 3936 3937proto.animate = function() { 3938 // only render and animate if dragging 3939 if ( !this.isDragging ) { 3940 return; 3941 } 3942 3943 this.positionDrag(); 3944 3945 var _this = this; 3946 requestAnimationFrame( function animateFrame() { 3947 _this.animate(); 3948 }); 3949 3950}; 3951 3952// left/top positioning 3953proto.setLeftTop = function() { 3954 this.element.style.left = this.position.x + 'px'; 3955 this.element.style.top = this.position.y + 'px'; 3956}; 3957 3958proto.positionDrag = function() { 3959 this.element.style[ transformProperty ] = 'translate3d( ' + this.dragPoint.x + 3960 'px, ' + this.dragPoint.y + 'px, 0)'; 3961}; 3962 3963// ----- staticClick ----- // 3964 3965proto.staticClick = function( event, pointer ) { 3966 this.dispatchEvent( 'staticClick', event, [ pointer ] ); 3967}; 3968 3969// ----- methods ----- // 3970 3971proto.enable = function() { 3972 this.isEnabled = true; 3973}; 3974 3975proto.disable = function() { 3976 this.isEnabled = false;
3977 if ( this.isDragging ) { 3978 this.dragEnd(); 3979 } 3980}; 3981 3982proto.destroy = function() { 3983 this.disable(); 3984 // reset styles 3985 this.element.style[ transformProperty ] = ''; 3986 this.element.style.left = ''; 3987 this.element.style.top = ''; 3988 this.element.style.position = ''; 3989 // unbind handles 3990 this.unbindHandles(); 3991 // remove jQuery data 3992 if ( this.$element ) { 3993 this.$element.removeData('draggabilly'); 3994 } 3995}; 3996 3997// ----- jQuery bridget ----- // 3998 3999// required for jQuery bridget 4000proto._init = noop; 4001 4002if ( jQuery && jQuery.bridget ) { 4003 jQuery.bridget( 'draggabilly', Draggabilly ); 4004} 4005 4006// ----- ----- // 4007 4008return Draggabilly; 4009 4010})); 4011 4012},{"get-size":4,"unidragger":5}],3:[function(require,module,exports){ 4013/** 4014 * EvEmitter v1.0.2 4015 * Lil' event emitter 4016 * MIT License 4017 */ 4018 4019/* jshint unused: true, undef: true, strict: true */ 4020 4021( function( global, factory ) { 4022 // universal module definition 4023 /* jshint strict: false */ /* globals define, module */ 4024 if ( typeof define == 'function' && define.amd ) { 4025 // AMD - RequireJS 4026 define( factory ); 4027 } else if ( typeof module == 'object' && module.exports ) { 4028 // CommonJS - Browserify, Webpack 4029 module.exports = factory(); 4030 } else { 4031 // Browser globals 4032 global.EvEmitter = factory(); 4033 } 4034 4035}( this, function() { 4036 4037"use strict"; 4038 4039function EvEmitter() {} 4040 4041var proto = EvEmitter.prototype; 4042 4043proto.on = function( eventName, listener ) { 4044 if ( !eventName || !listener ) { 4045 return; 4046 } 4047 // set events hash 4048 var events = this._events = this._events || {}; 4049 // set listeners array 4050 var listeners = events[ eventName ] = events[ eventName ] || []; 4051 // only add once 4052 if ( listeners.indexOf( listener ) == -1 ) { 4053 listeners.push( listener ); 4054 } 4055 4056 return this; 4057}; 4058 4059proto.once = function( eventName, listener ) { 4060 if ( !eventName || !listener ) { 4061 return; 4062 } 4063 // add event 4064 this.on( eventName, listener ); 4065 // set once flag 4066 // set onceEvents hash 4067 var onceEvents = this._onceEvents = this._onceEvents || {}; 4068 // set onceListeners object 4069 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 4070 // set flag 4071 onceListeners[ listener ] = true; 4072 4073 return this; 4074}; 4075 4076proto.off = function( eventName, listener ) { 4077 var listeners = this._events && this._events[ eventName ]; 4078 if ( !listeners || !listeners.length ) { 4079 return; 4080 } 4081 var index = listeners.indexOf( listener ); 4082 if ( index != -1 ) { 4083 listeners.splice( index, 1 ); 4084 } 4085 4086 return this; 4087}; 4088 4089proto.emitEvent = function( eventName, args ) { 4090 var listeners = this._events && this._events[ eventName ]; 4091 if ( !listeners || !listeners.length ) { 4092 return; 4093 } 4094 var i = 0; 4095 var listener = listeners[i]; 4096 args = args || []; 4097 // once stuff 4098 var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 4099 4100 while ( listener ) { 4101 var isOnce = onceListeners && onceListeners[ listener ]; 4102 if ( isOnce ) { 4103 // remove listener 4104 // remove before trigger to prevent recursion 4105 this.off( eventName, listener ); 4106 // unset once flag 4107 delete onceListeners[ listener ]; 4108 } 4109 // trigger listener 4110 listener.apply( this, args ); 4111 // get next listener 4112 i += isOnce ? 0 : 1; 4113 listener = listeners[i]; 4114 } 4115 4116 return this; 4117}; 4118 4119return EvEmitter; 4120 4121})); 4122 4123},{}],4:[function(require,module,exports){ 4124/*! 4125 * getSize v2.0.2 4126 * measure size of elements 4127 * MIT license 4128 */ 4129 4130/*jshint browser: true, strict: true, undef: true, unused: true */ 4131/*global define: false, module: false, console: false */ 4132 4133( function( window, factory ) { 4134 'use strict'; 4135 4136 if ( typeof define == 'function' && define.amd ) { 4137 // AMD 4138 define( function() { 4139 return factory(); 4140 }); 4141 } else if ( typeof module == 'object' && module.exports ) { 4142 // CommonJS 4143 module.exports = factory(); 4144 } else { 4145 // browser global 4146 window.getSize = factory(); 4147 } 4148 4149})( window, function factory() { 4150'use strict'; 4151 4152// -------------------------- helpers -------------------------- // 4153 4154// get a number from a string, not a percentage 4155function getStyleSize( value ) { 4156 var num = parseFloat( value ); 4157 // not a percent like '100%', and a number 4158 var isValid = value.indexOf('%') == -1 && !isNaN( num ); 4159 return isValid && num; 4160} 4161 4162function noop() {} 4163 4164var logError = typeof console == 'undefined' ? noop : 4165 function( message ) { 4166 console.error( message ); 4167 }; 4168 4169// -------------------------- measurements -------------------------- // 4170 4171var measurements = [ 4172 'paddingLeft', 4173 'paddingRight', 4174 'paddingTop', 4175 'paddingBottom', 4176 'marginLeft', 4177 'marginRight', 4178 'marginTop', 4179 'marginBottom', 4180 'borderLeftWidth', 4181 'borderRightWidth', 4182 'borderTopWidth', 4183 'borderBottomWidth' 4184]; 4185 4186var measurementsLength = measurements.length; 4187 4188function getZeroSize() { 4189 var size = { 4190 width: 0, 4191 height: 0, 4192 innerWidth: 0, 4193 innerHeight: 0, 4194 outerWidth: 0, 4195 outerHeight: 0 4196 }; 4197 for ( var i=0; i < measurementsLength; i++ ) { 4198 var measurement = measurements[i]; 4199 size[ measurement ] = 0; 4200 } 4201 return size; 4202} 4203 4204// -------------------------- getStyle -------------------------- // 4205 4206/** 4207 * getStyle, get style of element, check for Firefox bug 4208 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 4209 */ 4210function getStyle( elem ) { 4211 var style = getComputedStyle( elem ); 4212 if ( !style ) { 4213 logError( 'Style returned ' + style + 4214 '. Are you running this code in a hidden iframe on Firefox? ' + 4215 'See http://bit.ly/getsizebug1' ); 4216 } 4217 return style; 4218} 4219 4220// -------------------------- setup -------------------------- // 4221 4222var isSetup = false;
4223 4224var isBoxSizeOuter; 4225 4226/** 4227 * setup 4228 * check isBoxSizerOuter 4229 * do on first getSize() rather than on page load for Firefox bug 4230 */ 4231function setup() { 4232 // setup once 4233 if ( isSetup ) { 4234 return; 4235 } 4236 isSetup = true; 4237 4238 // -------------------------- box sizing -------------------------- // 4239 4240 /** 4241 * WebKit measures the outer-width on style.width on border-box elems 4242 * IE & Firefox<29 measures the inner-width 4243 */ 4244 var div = document.createElement('div'); 4245 div.style.width = '200px'; 4246 div.style.padding = '1px 2px 3px 4px'; 4247 div.style.borderStyle = 'solid'; 4248 div.style.borderWidth = '1px 2px 3px 4px'; 4249 div.style.boxSizing = 'border-box'; 4250 4251 var body = document.body || document.documentElement; 4252 body.appendChild( div ); 4253 var style = getStyle( div ); 4254 4255 getSize.isBoxSizeOuter = isBoxSizeOuter = getStyleSize( style.width ) == 200; 4256 body.removeChild( div ); 4257 4258} 4259 4260// -------------------------- getSize -------------------------- // 4261 4262function getSize( elem ) { 4263 setup(); 4264 4265 // use querySeletor if elem is string 4266 if ( typeof elem == 'string' ) { 4267 elem = document.querySelector( elem ); 4268 } 4269 4270 // do not proceed on non-objects 4271 if ( !elem || typeof elem != 'object' || !elem.nodeType ) { 4272 return; 4273 } 4274 4275 var style = getStyle( elem ); 4276 4277 // if hidden, everything is 0 4278 if ( style.display == 'none' ) { 4279 return getZeroSize(); 4280 } 4281 4282 var size = {}; 4283 size.width = elem.offsetWidth; 4284 size.height = elem.offsetHeight; 4285 4286 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; 4287 4288 // get all measurements 4289 for ( var i=0; i < measurementsLength; i++ ) { 4290 var measurement = measurements[i]; 4291 var value = style[ measurement ]; 4292 var num = parseFloat( value ); 4293 // any 'auto', 'medium' value will be 0 4294 size[ measurement ] = !isNaN( num ) ? num : 0; 4295 } 4296 4297 var paddingWidth = size.paddingLeft + size.paddingRight; 4298 var paddingHeight = size.paddingTop + size.paddingBottom; 4299 var marginWidth = size.marginLeft + size.marginRight; 4300 var marginHeight = size.marginTop + size.marginBottom; 4301 var borderWidth = size.borderLeftWidth + size.borderRightWidth; 4302 var borderHeight = size.borderTopWidth + size.borderBottomWidth; 4303 4304 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 4305 4306 // overwrite width and height if we can get it from style 4307 var styleWidth = getStyleSize( style.width ); 4308 if ( styleWidth !== false ) { 4309 size.width = styleWidth + 4310 // add padding and border unless it's already including it 4311 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 4312 } 4313 4314 var styleHeight = getStyleSize( style.height ); 4315 if ( styleHeight !== false ) { 4316 size.height = styleHeight + 4317 // add padding and border unless it's already including it 4318 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 4319 } 4320 4321 size.innerWidth = size.width - ( paddingWidth + borderWidth ); 4322 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 4323 4324 size.outerWidth = size.width + marginWidth; 4325 size.outerHeight = size.height + marginHeight; 4326 4327 return size; 4328} 4329 4330return getSize; 4331 4332}); 4333 4334},{}],5:[function(require,module,exports){ 4335/*! 4336 * Unidragger v2.1.0 4337 * Draggable base class 4338 * MIT license 4339 */ 4340 4341/*jshint browser: true, unused: true, undef: true, strict: true */ 4342 4343( function( window, factory ) { 4344 // universal module definition 4345 /*jshint strict: false */ /*globals define, module, require */ 4346 4347 if ( typeof define == 'function' && define.amd ) { 4348 // AMD 4349 define( [ 4350 'unipointer/unipointer' 4351 ], function( Unipointer ) { 4352 return factory( window, Unipointer ); 4353 }); 4354 } else if ( typeof module == 'object' && module.exports ) { 4355 // CommonJS 4356 module.exports = factory( 4357 window, 4358 require('unipointer') 4359 ); 4360 } else { 4361 // browser global 4362 window.Unidragger = factory( 4363 window, 4364 window.Unipointer 4365 ); 4366 } 4367 4368}( window, function factory( window, Unipointer ) { 4369 4370'use strict'; 4371 4372// ----- ----- // 4373 4374function noop() {} 4375 4376// -------------------------- Unidragger -------------------------- // 4377 4378function Unidragger() {} 4379 4380// inherit Unipointer & EvEmitter 4381var proto = Unidragger.prototype = Object.create( Unipointer.prototype ); 4382 4383// ----- bind start ----- // 4384 4385proto.bindHandles = function() { 4386 this._bindHandles( true ); 4387}; 4388 4389proto.unbindHandles = function() { 4390 this._bindHandles( false ); 4391}; 4392 4393var navigator = window.navigator; 4394/** 4395 * works as unbinder, as you can .bindHandles( false ) to unbind 4396 * @param {Boolean} isBind - will unbind if falsey 4397 */ 4398proto._bindHandles = function( isBind ) { 4399 // munge isBind, default to true 4400 isBind = isBind === undefined ? true : !!isBind; 4401 // extra bind logic 4402 var binderExtra; 4403 if ( navigator.pointerEnabled ) { 4404 binderExtra = function( handle ) { 4405 // disable scrolling on the element 4406 handle.style.touchAction = isBind ? 'none' : ''; 4407 }; 4408 } else if ( navigator.msPointerEnabled ) { 4409 binderExtra = function( handle ) { 4410 // disable scrolling on the element 4411 handle.style.msTouchAction = isBind ? 'none' : ''; 4412 }; 4413 } else { 4414 binderExtra = noop; 4415 } 4416 // bind each handle 4417 var bindMethod = isBind ? 'addEventListener' : 'removeEventListener'; 4418 for ( var i=0; i < this.handles.length; i++ ) { 4419 var handle = this.handles[i]; 4420 this._bindStartEvent( handle, isBind ); 4421 binderExtra( handle ); 4422 handle[ bindMethod ]( 'click', this ); 4423 } 4424}; 4425 4426// ----- start event ----- // 4427 4428/** 4429 * pointer start 4430 * @param {Event} event 4431 * @param {Event or Touch} pointer 4432 */ 4433proto.pointerDown = function( event, pointer ) { 4434 // dismiss range sliders 4435 if ( event.target.nodeName == 'INPUT' && event.target.type == 'range' ) { 4436 // reset pointerDown logic 4437 this.isPointerDown = false;
4438 delete this.pointerIdentifier; 4439 return; 4440 } 4441 4442 this._dragPointerDown( event, pointer ); 4443 // kludge to blur focused inputs in dragger 4444 var focused = document.activeElement; 4445 if ( focused && focused.blur ) { 4446 focused.blur(); 4447 } 4448 // bind move and end events 4449 this._bindPostStartEvents( event ); 4450 this.emitEvent( 'pointerDown', [ event, pointer ] ); 4451}; 4452 4453// base pointer down logic 4454proto._dragPointerDown = function( event, pointer ) { 4455 // track to see when dragging starts 4456 this.pointerDownPoint = Unipointer.getPointerPoint( pointer ); 4457 4458 var canPreventDefault = this.canPreventDefaultOnPointerDown( event, pointer ); 4459 if ( canPreventDefault ) { 4460 event.preventDefault(); 4461 } 4462}; 4463 4464// overwriteable method so Flickity can prevent for scrolling 4465proto.canPreventDefaultOnPointerDown = function( event ) { 4466 // prevent default, unless touchstart or <select> 4467 return event.target.nodeName != 'SELECT'; 4468}; 4469 4470// ----- move event ----- // 4471 4472/** 4473 * drag move 4474 * @param {Event} event 4475 * @param {Event or Touch} pointer 4476 */ 4477proto.pointerMove = function( event, pointer ) { 4478 var moveVector = this._dragPointerMove( event, pointer ); 4479 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] ); 4480 this._dragMove( event, pointer, moveVector ); 4481}; 4482 4483// base pointer move logic 4484proto._dragPointerMove = function( event, pointer ) { 4485 var movePoint = Unipointer.getPointerPoint( pointer ); 4486 var moveVector = { 4487 x: movePoint.x - this.pointerDownPoint.x, 4488 y: movePoint.y - this.pointerDownPoint.y 4489 }; 4490 // start drag if pointer has moved far enough to start drag 4491 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) { 4492 this._dragStart( event, pointer ); 4493 } 4494 return moveVector; 4495}; 4496 4497// condition if pointer has moved far enough to start drag 4498proto.hasDragStarted = function( moveVector ) { 4499 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3; 4500}; 4501 4502 4503// ----- end event ----- // 4504 4505/** 4506 * pointer up 4507 * @param {Event} event 4508 * @param {Event or Touch} pointer 4509 */ 4510proto.pointerUp = function( event, pointer ) { 4511 this.emitEvent( 'pointerUp', [ event, pointer ] ); 4512 this._dragPointerUp( event, pointer ); 4513}; 4514 4515proto._dragPointerUp = function( event, pointer ) { 4516 if ( this.isDragging ) { 4517 this._dragEnd( event, pointer ); 4518 } else { 4519 // pointer didn't move enough for drag to start 4520 this._staticClick( event, pointer ); 4521 } 4522}; 4523 4524// -------------------------- drag -------------------------- // 4525 4526// dragStart 4527proto._dragStart = function( event, pointer ) { 4528 this.isDragging = true; 4529 this.dragStartPoint = Unipointer.getPointerPoint( pointer ); 4530 // prevent clicks
4531 this.isPreventingClicks = true; 4532 4533 this.dragStart( event, pointer ); 4534}; 4535 4536proto.dragStart = function( event, pointer ) { 4537 this.emitEvent( 'dragStart', [ event, pointer ] ); 4538}; 4539 4540// dragMove 4541proto._dragMove = function( event, pointer, moveVector ) { 4542 // do not drag if not dragging yet 4543 if ( !this.isDragging ) { 4544 return; 4545 } 4546 4547 this.dragMove( event, pointer, moveVector ); 4548}; 4549 4550proto.dragMove = function( event, pointer, moveVector ) { 4551 event.preventDefault(); 4552 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] ); 4553}; 4554 4555// dragEnd 4556proto._dragEnd = function( event, pointer ) { 4557 // set flags 4558 this.isDragging = false; 4559 // re-enable clicking async 4560 setTimeout( function() { 4561 delete this.isPreventingClicks; 4562 }.bind( this ) ); 4563 4564 this.dragEnd( event, pointer ); 4565}; 4566 4567proto.dragEnd = function( event, pointer ) { 4568 this.emitEvent( 'dragEnd', [ event, pointer ] ); 4569}; 4570 4571// ----- onclick ----- // 4572 4573// handle all clicks and prevent clicks when dragging 4574proto.onclick = function( event ) { 4575 if ( this.isPreventingClicks ) { 4576 event.preventDefault(); 4577 } 4578}; 4579 4580// ----- staticClick ----- // 4581 4582// triggered after pointer down & up with no/tiny movement 4583proto._staticClick = function( event, pointer ) { 4584 // ignore emulated mouse up clicks 4585 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { 4586 return; 4587 } 4588 4589 // allow click in <input>s and <textarea>s 4590 var nodeName = event.target.nodeName; 4591 if ( nodeName == 'INPUT' || nodeName == 'TEXTAREA' ) { 4592 event.target.focus(); 4593 } 4594 this.staticClick( event, pointer ); 4595 4596 // set flag for emulated clicks 300ms after touchend 4597 if ( event.type != 'mouseup' ) { 4598 this.isIgnoringMouseUp = true; 4599 // reset flag after 300ms 4600 setTimeout( function() { 4601 delete this.isIgnoringMouseUp; 4602 }.bind( this ), 400 ); 4603 } 4604}; 4605 4606proto.staticClick = function( event, pointer ) { 4607 this.emitEvent( 'staticClick', [ event, pointer ] ); 4608}; 4609 4610// ----- utils ----- // 4611 4612Unidragger.getPointerPoint = Unipointer.getPointerPoint; 4613 4614// ----- ----- // 4615 4616return Unidragger; 4617 4618})); 4619 4620},{"unipointer":6}],6:[function(require,module,exports){ 4621/*! 4622 * Unipointer v2.1.0 4623 * base class for doing one thing with pointer event 4624 * MIT license 4625 */ 4626 4627/*jshint browser: true, undef: true, unused: true, strict: true */ 4628 4629( function( window, factory ) { 4630 // universal module definition 4631 /* jshint strict: false */ /*global define, module, require */ 4632 if ( typeof define == 'function' && define.amd ) { 4633 // AMD 4634 define( [ 4635 'ev-emitter/ev-emitter' 4636 ], function( EvEmitter ) { 4637 return factory( window, EvEmitter ); 4638 }); 4639 } else if ( typeof module == 'object' && module.exports ) { 4640 // CommonJS 4641 module.exports = factory( 4642 window, 4643 require('ev-emitter') 4644 ); 4645 } else { 4646 // browser global 4647 window.Unipointer = factory( 4648 window, 4649 window.EvEmitter 4650 ); 4651 } 4652 4653}( window, function factory( window, EvEmitter ) { 4654 4655'use strict'; 4656 4657function noop() {} 4658 4659function Unipointer() {} 4660 4661// inherit EvEmitter 4662var proto = Unipointer.prototype = Object.create( EvEmitter.prototype ); 4663 4664proto.bindStartEvent = function( elem ) { 4665 this._bindStartEvent( elem, true ); 4666}; 4667 4668proto.unbindStartEvent = function( elem ) { 4669 this._bindStartEvent( elem, false ); 4670}; 4671 4672/** 4673 * works as unbinder, as you can ._bindStart( false ) to unbind 4674 * @param {Boolean} isBind - will unbind if falsey 4675 */ 4676proto._bindStartEvent = function( elem, isBind ) { 4677 // munge isBind, default to true 4678 isBind = isBind === undefined ? true : !!isBind; 4679 var bindMethod = isBind ? 'addEventListener' : 'removeEventListener'; 4680 4681 if ( window.navigator.pointerEnabled ) { 4682 // W3C Pointer Events, IE11. See https://coderwall.com/p/mfreca 4683 elem[ bindMethod ]( 'pointerdown', this ); 4684 } else if ( window.navigator.msPointerEnabled ) { 4685 // IE10 Pointer Events 4686 elem[ bindMethod ]( 'MSPointerDown', this ); 4687 } else { 4688 // listen for both, for devices like Chrome Pixel 4689 elem[ bindMethod ]( 'mousedown', this ); 4690 elem[ bindMethod ]( 'touchstart', this ); 4691 } 4692}; 4693 4694// trigger handler methods for events 4695proto.handleEvent = function( event ) { 4696 var method = 'on' + event.type; 4697 if ( this[ method ] ) { 4698 this[ method ]( event ); 4699 } 4700}; 4701 4702// returns the touch that we're keeping track of 4703proto.getTouch = function( touches ) { 4704 for ( var i=0; i < touches.length; i++ ) { 4705 var touch = touches[i]; 4706 if ( touch.identifier == this.pointerIdentifier ) { 4707 return touch; 4708 } 4709 } 4710}; 4711 4712// ----- start event ----- // 4713 4714proto.onmousedown = function( event ) { 4715 // dismiss clicks from right or middle buttons 4716 var button = event.button; 4717 if ( button && ( button !== 0 && button !== 1 ) ) { 4718 return; 4719 } 4720 this._pointerDown( event, event ); 4721}; 4722 4723proto.ontouchstart = function( event ) { 4724 this._pointerDown( event, event.changedTouches[0] ); 4725}; 4726 4727proto.onMSPointerDown = 4728proto.onpointerdown = function( event ) { 4729 this._pointerDown( event, event ); 4730}; 4731 4732/** 4733 * pointer start 4734 * @param {Event} event 4735 * @param {Event or Touch} pointer 4736 */ 4737proto._pointerDown = function( event, pointer ) { 4738 // dismiss other pointers 4739 if ( this.isPointerDown ) { 4740 return; 4741 } 4742 4743 this.isPointerDown = true; 4744 // save pointer identifier to match up touch events 4745 this.pointerIdentifier = pointer.pointerId !== undefined ? 4746 // pointerId for pointer events, touch.indentifier for touch events 4747 pointer.pointerId : pointer.identifier; 4748 4749 this.pointerDown( event, pointer ); 4750}; 4751 4752proto.pointerDown = function( event, pointer ) { 4753 this._bindPostStartEvents( event ); 4754 this.emitEvent( 'pointerDown', [ event, pointer ] ); 4755}; 4756 4757// hash of events to be bound after start event 4758var postStartEvents = {
4759 mousedown: [ 'mousemove', 'mouseup' ], 4760 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], 4761 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], 4762 MSPointerDown: [ 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel' ] 4763}; 4764 4765proto._bindPostStartEvents = function( event ) { 4766 if ( !event ) { 4767 return; 4768 } 4769 // get proper events to match start event 4770 var events = postStartEvents[ event.type ]; 4771 // bind events to node 4772 events.forEach( function( eventName ) { 4773 window.addEventListener( eventName, this ); 4774 }, this ); 4775 // save these arguments 4776 this._boundPointerEvents = events; 4777}; 4778 4779proto._unbindPostStartEvents = function() { 4780 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug) 4781 if ( !this._boundPointerEvents ) { 4782 return; 4783 } 4784 this._boundPointerEvents.forEach( function( eventName ) { 4785 window.removeEventListener( eventName, this ); 4786 }, this ); 4787 4788 delete this._boundPointerEvents; 4789}; 4790 4791// ----- move event ----- // 4792 4793proto.onmousemove = function( event ) { 4794 this._pointerMove( event, event ); 4795}; 4796 4797proto.onMSPointerMove = 4798proto.onpointermove = function( event ) { 4799 if ( event.pointerId == this.pointerIdentifier ) { 4800 this._pointerMove( event, event ); 4801 } 4802}; 4803 4804proto.ontouchmove = function( event ) { 4805 var touch = this.getTouch( event.changedTouches ); 4806 if ( touch ) { 4807 this._pointerMove( event, touch ); 4808 } 4809}; 4810 4811/** 4812 * pointer move 4813 * @param {Event} event 4814 * @param {Event or Touch} pointer 4815 * @private 4816 */ 4817proto._pointerMove = function( event, pointer ) { 4818 this.pointerMove( event, pointer ); 4819}; 4820 4821// public 4822proto.pointerMove = function( event, pointer ) { 4823 this.emitEvent( 'pointerMove', [ event, pointer ] ); 4824}; 4825 4826// ----- end event ----- // 4827 4828 4829proto.onmouseup = function( event ) { 4830 this._pointerUp( event, event ); 4831}; 4832 4833proto.onMSPointerUp = 4834proto.onpointerup = function( event ) { 4835 if ( event.pointerId == this.pointerIdentifier ) { 4836 this._pointerUp( event, event ); 4837 } 4838}; 4839 4840proto.ontouchend = function( event ) { 4841 var touch = this.getTouch( event.changedTouches ); 4842 if ( touch ) { 4843 this._pointerUp( event, touch ); 4844 } 4845}; 4846 4847/** 4848 * pointer up 4849 * @param {Event} event 4850 * @param {Event or Touch} pointer 4851 * @private 4852 */ 4853proto._pointerUp = function( event, pointer ) { 4854 this._pointerDone(); 4855 this.pointerUp( event, pointer ); 4856}; 4857 4858// public 4859proto.pointerUp = function( event, pointer ) { 4860 this.emitEvent( 'pointerUp', [ event, pointer ] ); 4861}; 4862 4863// ----- pointer done ----- // 4864 4865// triggered on pointer up & pointer cancel 4866proto._pointerDone = function() { 4867 // reset properties 4868 this.isPointerDown = false; 4869 delete this.pointerIdentifier; 4870 // remove events 4871 this._unbindPostStartEvents(); 4872 this.pointerDone(); 4873}; 4874 4875proto.pointerDone = noop; 4876 4877// ----- pointer cancel ----- // 4878 4879proto.onMSPointerCancel = 4880proto.onpointercancel = function( event ) { 4881 if ( event.pointerId == this.pointerIdentifier ) { 4882 this._pointerCancel( event, event ); 4883 } 4884}; 4885 4886proto.ontouchcancel = function( event ) { 4887 var touch = this.getTouch( event.changedTouches ); 4888 if ( touch ) { 4889 this._pointerCancel( event, touch ); 4890 } 4891}; 4892 4893/** 4894 * pointer cancel 4895 * @param {Event} event 4896 * @param {Event or Touch} pointer 4897 * @private 4898 */ 4899proto._pointerCancel = function( event, pointer ) { 4900 this._pointerDone(); 4901 this.pointerCancel( event, pointer ); 4902}; 4903 4904// public 4905proto.pointerCancel = function( event, pointer ) { 4906 this.emitEvent( 'pointerCancel', [ event, pointer ] ); 4907}; 4908 4909// ----- ----- // 4910 4911// utility function for getting x/y coords from event 4912Unipointer.getPointerPoint = function( pointer ) { 4913 return { 4914 x: pointer.pageX, 4915 y: pointer.pageY 4916 }; 4917}; 4918 4919// ----- ----- // 4920 4921return Unipointer; 4922 4923})); 4924 4925},{"ev-emitter":3}],7:[function(require,module,exports){ 4926'use strict'; 4927 4928var Alert = function() { 4929 4930 var a = this; 4931 4932 a.config = { 4933 'alert' : $('.alert'), 4934 'close' : $('.alert__close') 4935 } 4936 4937 var $alert = a.config.alert, 4938 $close = a.config.close; 4939 4940 4941 a.closeClickHandler = function() { 4942 4943 $close.on('click', function(e) { 4944 e.preventDefault(); 4945 4946 var $this = $(this), 4947 $parentAlert = $this.parent($alert); 4948 4949 a.hideAlert($parentAlert); 4950 }) 4951 } 4952 4953 4954 a.hideAlert = function($alert) { 4955 4956 $alert 4957 .velocity( 4958 { 4959 opacity: 0 4960 } 4961 ) 4962 .velocity( 4963 "slideUp", 4964 { 4965 duration: 600,
4966 easing: "easeOutQuart" 4967 } 4968 ); 4969 } 4970 4971 4972 a.init = function() { 4973 4974 a.closeClickHandler(); 4975 } 4976}; 4977 4978module.exports = Alert; 4979 4980},{}],8:[function(require,module,exports){ 4981'use strict'; 4982 4983var Constants = function() { 4984 4985 var c = this; 4986 4987 c.MEDIA_QUERIES = { 4988 'TINY' : '(min-width: 480px)', 4989 'SMALLER' : '(min-width: 640px)', 4990 'SMALL' : '(min-width: 768px)', 4991 'TABLET' : '(min-width: 900px)', 4992 'MEDIUM' : '(min-width: 1100px)', 4993 'LARGE' : '(min-width: 1200px)', 4994 'XLARGE' : '(min-width: 1400px)', 4995 'XXLARGE' : '(min-width: 1600px)' 4996 } 4997}; 4998 4999module.exports = Constants; 5000},{}],9:[function(require,module,exports){ 5001'use strict'; 5002 5003var PhotoActions = function() { 5004 5005 var SearchResultThumbnail = require('../search-result-thumbnail/search-result-thumbnail'); 5006 var Constants = require('../constants/constants'); 5007 var self = this; 5008 5009 self.config = { 5010 'bar' : $('.photo-actions2'), 5011 'thumbs' : $('.photo-actions__thumbs2'), 5012 'thumbTemplate' : function(url, image) { 5013 var template = '<div data-small-thumb-url="' + url + '" style="background-image: url(' + image + ');" class="photo-action__thumb"><div class="photo-action__thumb-remove"><span class="icon icon--slideshow-close"></span></div></div>'; 5014 return template; 5015 }, 5016 'counter' : $('.photo-actions__counter'), 5017 'add' : $('.photo-actions__add-button'), 5018 'options' : $('.photo-actions__options'), 5019 'optionsLink' : $('.photo-actions__options-list-link').not('.photo-actions__options-list-link--new-gallery'), 5020 'newGalleryLink' : $('.photo-actions__options-list-link--new-gallery'), 5021 'newGalleryInput' : $('.photo-actions__new-gallery') 5022 } 5023 5024 var $bar = self.config.bar, 5025 $thumbs = self.config.thumbs, 5026 thumbTemplate = self.config.thumbTemplate, 5027 $counter = self.config.counter, 5028 $add = self.config.add, 5029 $options = self.config.options, 5030 $optionsLink = self.config.optionsLink, 5031 $newGalleryLink = self.config.newGalleryLink, 5032 $newGalleryInput = self.config.newGalleryInput; 5033 5034 5035 self.showBar = function() { 5036 5037 if (! $bar.hasClass('is-visible')) { 5038 5039 $bar.velocity( 5040 { 5041 translateY: ['0', '100%'] 5042 }, 5043 { 5044 easing: 'easeOutQuint', 5045 complete: function() { 5046 $bar.addClass('is-visible'); 5047 } 5048 } 5049 ); 5050 } 5051 } 5052 5053 5054 self.hideBar = function() { 5055 5056 var delay = 0; 5057 5058 if ($options.hasClass('is-visible')) { 5059 delay = 300; 5060 self.hideOptions(); 5061 } 5062 5063 $bar.velocity( 5064 { 5065 translateY: ['100%', '0'] 5066 }, 5067 { 5068 delay: delay, 5069 easing: 'easeOutQuint', 5070 duration: '300ms', 5071 complete: function() { 5072 $bar.removeClass('is-visible'); 5073 } 5074 } 5075 ); 5076 } 5077 5078 5079 self.setCounter = function(dir) { 5080 5081 var val = $counter.attr('data-count'); 5082 5083 if (dir === '+') { 5084 val++; 5085 } 5086 5087 if (dir === '-') { 5088 val--; 5089 } 5090 5091 $counter.attr('data-count', val); 5092 $counter.text('(' + val + ')'); 5093 } 5094 5095 5096 self.addThumb = function(url, image) { 5097 5098 var $newThumb = $(thumbTemplate(url, image)); 5099 5100 $thumbs.append($newThumb); 5101 5102 self.showThumb($newThumb); 5103 self.thumbClickHandler($newThumb); 5104 self.thumbsCountHandler(); 5105 self.setCounter('+'); 5106 } 5107 5108 5109 self.removeThumb = function($thumb, url) { 5110 5111 /*$thumb.remove(); 5112 self.thumbsCountHandler(); 5113 self.setCounter('-');*/ 5114 } 5115 5116 5117 self.showThumb = function($thumb) { 5118 5119 $thumb.velocity( 5120 { 5121 scale: ['1', '0'] 5122 }, 5123 { 5124 easing: 'easeOutQuint', 5125 duration: '250ms' 5126 } 5127 ); 5128 } 5129 5130 5131 self.hideThumb = function(url, $thumb, unmarkLargeThumb, remove) { 5132 5133 if (! $thumb) { 5134 5135 $thumb = $('[data-small-thumb-url="' + url + '"]'); 5136 } 5137 5138 $thumb 5139 .velocity( 5140 { 5141 translateY: '80%', 5142 opacity: '0' 5143 }, 5144 { 5145 easing: 'easeOutQuint', 5146 duration: '250ms' 5147 } 5148 ) 5149 .velocity( 5150 { 5151 marginRight: '0', 5152 width: '0' 5153 }, 5154 { 5155 easing: 'easeOutQuint', 5156 duration: '400ms', 5157 complete: function() { 5158 5159 /*if (remove) { 5160 self.removeThumb($thumb, url); 5161 }*/ 5162 } 5163 } 5164 ); 5165 5166 if (unmarkLargeThumb) { 5167 5168 new SearchResultThumbnail().unmarkItem(url); 5169 } 5170 } 5171 5172 5173 self.thumbClickHandler = function($thumb) { 5174 5175 $thumb.on('click', function() { 5176 5177 var $this = $(this), 5178 url = $this.attr('data-small-thumb-url'); 5179 5180 self.hideThumb(url, $this, true, true); 5181 }); 5182 } 5183 5184 5185 self.foldThumb = function($thumb) { 5186 5187 if (! $thumb.hasClass('is-folded')) { 5188 5189 $thumb 5190 .velocity( 5191 { 5192 translateX: '-100%', 5193 opacity: '0' 5194 } 5195 ) 5196 .velocity( 5197 { 5198 marginRight: '0', 5199 width: '0' 5200 } 5201 ); 5202 5203 $thumb.addClass('is-folded'); 5204 } 5205 } 5206 5207 5208 self.unfoldThumb = function($thumb) { 5209 5210 if ($thumb.hasClass('is-folded')) { 5211 5212 $thumb 5213 .velocity( 5214 { 5215 translateX: ['0', '-100%'], 5216 opacity: '1', 5217 marginRight: '1rem', 5218 width: '60px' 5219 } 5220 ); 5221 5222 $thumb.removeClass('is-folded'); 5223 } 5224 } 5225 5226 5227 self.thumbsCountHandler = function() { 5228 5229 if ($thumbs.children().length === 0) { 5230 5231 self.hideBar(); 5232 } else { 5233 5234 if ($thumbs.children().not('.is-folded').length > 5) { 5235 5236 var $first = $($thumbs.children().not('.is-folded')[0]); 5237 5238 self.foldThumb($first); 5239 } else { 5240 5241 var $folded = $thumbs.find('.is-folded'); 5242 5243 if ($folded) { 5244 5245 var $foldedLast = $folded.last(); 5246 5247 self.unfoldThumb($foldedLast); 5248 } 5249 } 5250 } 5251 } 5252 5253 5254 self.addClickHandler = function() { 5255 5256 $add.on('click', function() { 5257 5258 self.optionsVisibility(); 5259 }); 5260 5261 $(document).on('click', function(e) { 5262 5263 if (!$(e.target).closest($options).length) { 5264 5265 if ($options.hasClass('is-visible')) { 5266 5267 self.hideOptions(); 5268 }; 5269 }; 5270 }); 5271 } 5272 5273 5274 self.optionsVisibility = function() { 5275 5276 if ($options.hasClass('is-visible')) { 5277 self.hideOptions(); 5278 } else { 5279 self.showOptions(); 5280 } 5281 } 5282 5283 5284 self.showOptions = function() { 5285 5286 $add.addClass('is-active'); 5287 $options.velocity( 5288 { 5289 height: '300px' 5290 }, 5291 { 5292 display: 'block', 5293 easing: 'easeOutQuint', 5294 complete: function() { 5295 $options.addClass('is-visible'); 5296 } 5297 } 5298 ) 5299 } 5300 5301 5302 self.hideOptions = function() { 5303 5304 $add.removeClass('is-active'); 5305 $options.velocity( 5306 { 5307 height: '0' 5308 }, 5309 { 5310 display: 'none', 5311 duration: '300ms', 5312 easing: 'easeOutQuint', 5313 complete: function() { 5314 $options.removeClass('is-visible'); 5315 } 5316 } 5317 ) 5318 } 5319 5320 5321 self.addToGallery = function() { 5322 5323 $optionsLink.on('click', function(e) { 5324 e.preventDefault(); 5325 5326 var $this = $(this), 5327 href = $this.attr('href'), 5328 imageUrlArr = []; 5329 5330 $thumbs.children().each(function() { 5331 5332 var $this = $(this), 5333 imageUrl = $this.attr('data-small-thumb-url'); 5334 5335 imageUrlArr.push(imageUrl); 5336 }); 5337 5338 $this.addClass('is-done'); 5339 addToGallery(imageUrlArr, href); 5340 }); 5341 } 5342 5343 5344 self.newGallery = function() { 5345 5346 $newGalleryLink.on('click', function() { 5347 5348 if (! $newGalleryInput.is(':visible')) { 5349 $newGalleryInput.velocity('slideDown'); 5350 } 5351 }); 5352 } 5353 5354 5355 self.init = function() { 5356 self.addClickHandler(); 5357 self.addToGallery(); 5358 self.newGallery(); 5359 } 5360}; 5361 5362module.exports = PhotoActions; 5363 5364},{"../constants/constants":8,"../search-result-thumbnail/search-result-thumbnail":12}],10:[function(require,module,exports){ 5365'use strict'; 5366 5367var Cropper = require('cropperjs'); 5368 5369var ProfileCover = function() { 5370 5371 var pc = this; 5372 5373 pc.config = { 5374 'modal': $('#profileCoverCropImageModal'), 5375 'button': $('#confirmCrop'), 5376 'image': document.getElementById('profileCoverOriginalImage') 5377 } 5378 5379 var $modal = pc.config.modal, 5380 $button = pc.config.button, 5381 image = pc.config.image, 5382 x, 5383 y, 5384 width, 5385 height; 5386 5387 5388 pc.modalListener = function() { 5389 5390 $modal.on('modal-is-open', function() { 5391 pc.buildCropper(); 5392 pc.clickHandler(); 5393 }); 5394 } 5395 5396 5397 pc.buildCropper = function() { 5398 5399 var cropper = new Cropper(image, { 5400 aspectRatio: 76 / 23, 5401 autoCropArea: 1, 5402 scalable: false, 5403 rotatable: false, 5404 zoomable: false, 5405 crop: function(e) { 5406 5407 x = e.detail.x; 5408 y = e.detail.y; 5409 width = e.detail.width; 5410 height = e.detail.height; 5411 } 5412 }); 5413 } 5414 5415 5416 pc.clickHandler = function() { 5417 5418 $button.on('click', function(e) { 5419 e.preventDefault(); 5420 5421 cropCoverImage(x, y, width, height); 5422 }); 5423 } 5424 5425 5426 pc.init = function() { 5427 pc.modalListener(); 5428 } 5429}; 5430 5431module.exports = ProfileCover; 5432 5433},{"cropperjs":1}],11:[function(require,module,exports){ 5434'use strict'; 5435 5436var Constants = require('../constants/constants'); 5437var Tutorial = require('../tutorial/tutorial'); 5438 5439var ProfileToolbar = function() { 5440 5441 var self = this; 5442 5443 self.config = { 5444 'options' : $('.profile-toolbar__options'), 5445 'toggle' : $('.profile-toolbar__toggle-options'), 5446 'tutorialItem' : $('[data-toolbar-tutorial-item]') 5447 } 5448 5449 var $options = self.config.options, 5450 $toggle = self.config.toggle, 5451 $tutorialItem = self.config.tutorialItem; 5452 5453 5454 self.matchMediaHandler = function() { 5455 5456 var mq = new Constants().MEDIA_QUERIES.SMALLER, 5457 mql = window.matchMedia(mq); 5458 5459 mql.addListener(self.mediaQueryHandler); 5460 5461 self.mediaQueryHandler(mql); 5462 } 5463 5464 5465 self.mediaQueryHandler = function(mql) { 5466 5467 if (mql.matches) { 5468 // new Tutorial().initTutorial(); 5469 5470 if ($tutorialItem.length > 0) { 5471 new Tutorial().initTutorial(); 5472 } 5473 5474 self.clickHandler(); 5475 } else { 5476 5477 var cb; 5478 5479 if ($tutorialItem.length > 0) { 5480 cb = new Tutorial().initTutorial; 5481 } 5482 5483 self.clickHandler(cb); 5484 } 5485 } 5486 5487 5488 self.clickHandler = function(cb) { 5489 5490 $toggle.on('click', function(e) { 5491 e.preventDefault(); 5492 5493 self.elVisibility($options, cb); 5494 self.elVisibility($toggle); 5495 }); 5496 } 5497 5498 self.elVisibility = function($el, cb) { 5499
5500 if ($el.is(':visible')) { 5501 5502 self.hideEl($el); 5503 } else { 5504 5505 self.showEl($el, cb); 5506 } 5507 } 5508 5509 5510 self.showEl = function($el, cb) { 5511 $el 5512 .velocity( 5513 'slideDown' 5514 ) 5515 .velocity( 5516 { 5517 opacity: 1 5518 }, 5519 { 5520 complete: function() { 5521 if (typeof cb === 'function') { 5522 cb(); 5523 } 5524 } 5525 } 5526 ); 5527 } 5528 5529 5530 self.hideEl = function($el) { 5531 $el.velocity({opacity: 0}).velocity('slideUp'); 5532 } 5533 5534 5535 self.addFormItem = function() { 5536 5537 } 5538 5539 5540 self.init = function() { 5541 5542 // self.clickHandler(); 5543 self.matchMediaHandler(); 5544 } 5545}; 5546 5547module.exports = ProfileToolbar; 5548 5549},{"../constants/constants":8,"../tutorial/tutorial":13}],12:[function(require,module,exports){ 5550(function (global){ 5551'use strict'; 5552 5553var _ = (typeof window !== "undefined" ? window['_'] : typeof global !== "undefined" ? global['_'] : null); 5554var UserGalleryGrid = require('../user-gallery-grid/user-gallery-grid'); 5555 5556var SearchResultThumbnail = function() { 5557 5558 var PhotoActions = require('../photo-actions/photo-actions'); 5559 var self = this; 5560 5561 self.config = { 5562 'parentEl' : '.search-result-thumbnail', 5563 'addButton' : $('.search-result-thumbnail__add-to-gallery').not('.is-disabled'), 5564 'addIcons' : '.search-result-thumbnail__add-icon-group', 5565 'addedIcon' : '.search-result-thumbnail__added-icon', 5566 'setCoverClass' : 'search-result-thumbnail--set-cover', 5567 'removeButton' : $('.search-result-thumbnail__remove-from-gallery'), 5568 'removeConfirm' : $('#removeImageConfirm'), 5569 'gridColParent' : '.user-gallery-grid__item' 5570 } 5571 5572 var parentEl = self.config.parentEl; 5573 var $addButton = self.config.addButton; 5574 var addIcons = self.config.addIcons; 5575 var addedIcon = self.config.addedIcon; 5576 var setCoverClass = self.config.setCoverClass; 5577 var $removeButton = self.config.removeButton; 5578 var $removeConfirm = self.config.removeConfirm; 5579 var gridColParent = self.config.gridColParent; 5580 5581 5582 self.clickHandler = function() { 5583 5584 if ($addButton.length > 0) { 5585 5586 $addButton.on('click', _.throttle(function(e) { 5587 5588 e.preventDefault(); 5589 5590 var $this = $(this), 5591 $parentEl = $this.parents(parentEl); 5592 5593 if ($parentEl.hasClass('is-selected')) { 5594 5595 self.unmarkItem(null, $this); 5596 } else { 5597 5598 self.markItem($this); 5599 } 5600 }, 500)); 5601 } 5602 } 5603 5604 5605 self.markItem = function($button) { 5606 5607 var $parentEl = $button.parents(parentEl), 5608 $addIcons = $button.find($(addIcons)), 5609 $addedIcon = $button.find($(addedIcon)); 5610 5611 $parentEl.addClass('is-selected'); 5612 5613 //self.hideIcon($addIcons, self.showIcon($addedIcon, self.addItem($parentEl))); 5614 } 5615 5616 5617 self.unmarkItem = function(url, $button) { 5618 5619 if ($button === undefined) { 5620 5621 $button = $('[data-large-thumb-url="' + url + '"]'); 5622 } 5623 5624 var $parentEl = $button.parents(parentEl), 5625 $addIcons = $button.find($(addIcons)), 5626 $addedIcon = $button.find($(addedIcon)); 5627 5628 $parentEl.removeClass('is-selected'); 5629 5630 //self.hideIcon($addedIcon, self.showIcon($addIcons, self.removeItem($parentEl))); 5631 } 5632 5633 5634 self.hideIcon = function($icon, cb) { 5635 5636 $icon.velocity( 5637 { 5638 scale: [0, 1] 5639 }, 5640 { 5641 complete: function() { 5642 5643 if (cb) { 5644 cb(); 5645 } 5646 } 5647 } 5648 ); 5649 } 5650 5651 5652 self.showIcon = function($icon, cb) { 5653 5654 $icon.velocity( 5655 { 5656 scale: [1, 0] 5657 }, 5658 { 5659 complete: function() { 5660 5661 if (cb) { 5662 cb(); 5663 } 5664 } 5665 } 5666 ); 5667 } 5668 5669 5670 self.addItem = function($thumb) { 5671 5672 var url = $thumb.find('a').attr('href'), 5673 image = $thumb.find('img').attr('src'); 5674 5675 if ($thumb.hasClass(setCoverClass)) { 5676 5677 //setCoverImage(url); 5678 5679 $(parentEl).not($thumb).each(function() { 5680 var $this = $(this), 5681 $button = $this.find($addButton); 5682 5683 if ($this.hasClass('is-selected')) { 5684 self.unmarkItem(null, $button); 5685 } 5686 }); 5687 5688 } else { 5689 5690 new PhotoActions().showBar(); 5691 new PhotoActions().addThumb(url, image); 5692 } 5693 } 5694 5695 5696 self.removeItem = function($thumb) { 5697 5698 var url = $thumb.find('a').attr('href'); 5699 5700 new PhotoActions().hideThumb(url, null, false, true); 5701 } 5702 5703 5704 self.removeFromMyGallery = function() { 5705 5706 $removeButton.on('click', function() { 5707 5708 var $this = $(this); 5709 var url = $this.attr('data-thumb-url'); 5710 var $parent = $this.parents(parentEl); 5711 5712 self.setConfirmAttr(url, $parent); 5713 }) 5714 } 5715 5716 5717 self.setConfirmAttr = function(url, $thumb) { 5718 5719 $removeConfirm.attr('data-remove-image', url); 5720 self.confirmClickHandler(url, $thumb) 5721 } 5722 5723 5724 self.confirmClickHandler = function(url, $thumb) { 5725 5726 $removeConfirm.on('click', function() { 5727 removeImage(url); 5728 self.removeFromMyGalleryAnimation($thumb) 5729 }); 5730 } 5731 5732 5733 self.removeFromMyGalleryAnimation = function($thumb) { 5734 5735 var $gridColParent = $thumb.parents(gridColParent); 5736 5737 $thumb.velocity( 5738 { 5739 scale: '0.25', 5740 opacity: '0' 5741 }, 5742 { 5743 easing: 'easeInExpo', 5744 duration: 150, 5745 complete: function() { 5746 $gridColParent.remove(); 5747 $(window).trigger('updateGridLayout'); 5748 } 5749 } 5750 ); 5751 } 5752 5753 5754 self.confirm 5755 5756 5757 self.init = function() { 5758 5759 self.clickHandler(); 5760 self.removeFromMyGallery(); 5761 } 5762}; 5763 5764module.exports = SearchResultThumbnail; 5765 5766}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 5767 5768},{"../photo-actions/photo-actions":9,"../user-gallery-grid/user-gallery-grid":15}],13:[function(require,module,exports){ 5769'use strict'; 5770 5771require('./vendors/joyride'); 5772// var Constants = require('../constants/constants'); 5773var UserGalleryGrid = require('../user-gallery-grid/user-gallery-grid'); 5774 5775var Tutorial = function() { 5776 5777 var self = this; 5778 5779 self.config = { 5780 'tutorial' : $('#tutorial'), 5781 'item' : $('.tutorial__item'), 5782 'grid' : $('.user-gallery-grid.is-draggable') 5783 } 5784 5785 var $tutorial = self.config.tutorial, 5786 $item = self.config.item, 5787 $grid = self.config.grid; 5788 5789 /* 5790 self.matchMediaHandler = function() { 5791 5792 var mq = new Constants().MEDIA_QUERIES.SMALLER, 5793 mql = window.matchMedia(mq); 5794 5795 mql.addListener(self.mediaQueryHandler); 5796 5797 self.mediaQueryHandler(mql); 5798 } 5799 5800 5801 self.mediaQueryHandler = function(mql) { 5802 5803 if (mql.matches) { 5804 5805 } else { 5806 5807 } 5808 } 5809 */ 5810 5811 self.initTutorial = function() { 5812 5813 var initFunctions = function() { 5814 Foundation.Joyride.defaults.scrollOffset = 120; 5815 Foundation.Joyride.defaults.vOffset = 12; 5816 Foundation.Joyride.defaults.autostart = true; 5817 Foundation.Joyride.defaults.nextText = 'Següent'; 5818 Foundation.Joyride.defaults.prevText = 'Anterior';
5819 Foundation.Joyride.defaults.closeText = 'Tancar'; 5820 5821 $(document).foundation(); 5822 5823 self.clickOutsideHandler(); 5824 }; 5825 5826 if ($grid.length > 0) { 5827 new UserGalleryGrid().layoutCompleteListener(initFunctions); 5828 } else { 5829 initFunctions(); 5830 } 5831 } 5832 5833 self.clickOutsideHandler = function() { 5834 5835 /* 5836 5837 data-yeti-box="p96mfx-tooltip" 5838 data-toggle="p96mfx-tooltip" 5839 data-resize="p96mfx-tooltip" 5840 data-tooltip="kjni08-tooltip"> 5841 5842 */ 5843 5844 var $tooltip = $('.tooltip.joyride'); 5845 5846 $(document).on('click', function(e) { 5847 5848 if (! $(e.target).closest($tooltip).length) { 5849 5850 $tooltip.addClass('is-hidden'); 5851 $tooltip.attr('aria-hidden', true); 5852 $tooltip.attr('data-is-active', false); 5853 $tooltip.attr('data-is-focus', false); 5854 5855 $('[data-yeti-box]').removeAttr('data-yeti-box'); 5856 $('[data-toggle]').removeAttr('data-toggle'); 5857 $('[data-resize]').removeAttr('data-resize'); 5858 $('[data-tooltip]').removeAttr('data-tooltip'); 5859 5860 // $tooltip.remove(); 5861 } 5862 }); 5863 } 5864 5865 self.init = function() { 5866 5867 if ($tutorial.length > 0 && ! $item[0].hasAttribute('data-toolbar-tutorial-item')) { 5868 5869 $(window).load(function() { 5870 5871 self.initTutorial(); 5872 }); 5873 } 5874 } 5875}; 5876 5877module.exports = Tutorial; 5878 5879},{"../user-gallery-grid/user-gallery-grid":15,"./vendors/joyride":14}],14:[function(require,module,exports){ 5880(function (global){ 5881!function ($) { 5882 5883 "use strict"; 5884 5885 var FOUNDATION_VERSION = '6.2.0'; 5886 5887 // Global Foundation object 5888 // This is attached to the window, or used as a module for AMD/Browserify 5889 var Foundation = { 5890 version: FOUNDATION_VERSION, 5891 5892 /** 5893 * Stores initialized plugins. 5894 */ 5895 _plugins: {}, 5896 5897 /** 5898 * Stores generated unique ids for plugin instances 5899 */ 5900 _uuids: [], 5901 5902 /** 5903 * Returns a boolean for RTL support 5904 */ 5905 rtl: function () { 5906 return $('html').attr('dir') === 'rtl'; 5907 }, 5908 /** 5909 * Defines a Foundation plugin, adding it to the `Foundation` namespace and the list of plugins to initialize when reflowing. 5910 * @param {Object} plugin - The constructor of the plugin. 5911 */ 5912 plugin: function (plugin, name) { 5913 // Object key to use when adding to global Foundation object 5914 // Examples: Foundation.Reveal, Foundation.OffCanvas 5915 var className = name || functionName(plugin); 5916 // Object key to use when storing the plugin, also used to create the identifying data attribute for the plugin 5917 // Examples: data-reveal, data-off-canvas 5918 var attrName = hyphenate(className); 5919 5920 // Add to the Foundation object and the plugins list (for reflowing) 5921 this._plugins[attrName] = this[className] = plugin; 5922 }, 5923 /** 5924 * @function 5925 * Populates the _uuids array with pointers to each individual plugin instance. 5926 * Adds the `zfPlugin` data-attribute to programmatically created plugins to allow use of $(selector).foundation(method) calls. 5927 * Also fires the initialization event for each plugin, consolidating repeditive code. 5928 * @param {Object} plugin - an instance of a plugin, usually `this` in context. 5929 * @param {String} name - the name of the plugin, passed as a camelCased string. 5930 * @fires Plugin#init 5931 */ 5932 registerPlugin: function (plugin, name) { 5933 var pluginName = name ? hyphenate(name) : functionName(plugin.constructor).toLowerCase(); 5934 plugin.uuid = this.GetYoDigits(6, pluginName); 5935 5936 if (!plugin.$element.attr('data-' + pluginName)) { 5937 plugin.$element.attr('data-' + pluginName, plugin.uuid); 5938 } 5939 if (!plugin.$element.data('zfPlugin')) { 5940 plugin.$element.data('zfPlugin', plugin); 5941 } 5942 /** 5943 * Fires when the plugin has initialized. 5944 * @event Plugin#init 5945 */ 5946 plugin.$element.trigger('init.zf.' + pluginName); 5947 5948 this._uuids.push(plugin.uuid); 5949 5950 return; 5951 }, 5952 /** 5953 * @function 5954 * Removes the plugins uuid from the _uuids array. 5955 * Removes the zfPlugin data attribute, as well as the data-plugin-name attribute. 5956 * Also fires the destroyed event for the plugin, consolidating repeditive code. 5957 * @param {Object} plugin - an instance of a plugin, usually `this` in context. 5958 * @fires Plugin#destroyed 5959 */ 5960 unregisterPlugin: function (plugin) { 5961 var pluginName = hyphenate(functionName(plugin.$element.data('zfPlugin').constructor)); 5962 5963 this._uuids.splice(this._uuids.indexOf(plugin.uuid), 1); 5964 plugin.$element.removeAttr('data-' + pluginName).removeData('zfPlugin') 5965 /** 5966 * Fires when the plugin has been destroyed. 5967 * @event Plugin#destroyed 5968 */ 5969 .trigger('destroyed.zf.' + pluginName); 5970 for (var prop in plugin) { 5971 plugin[prop] = null; //clean up script to prep for garbage collection. 5972 } 5973 return; 5974 }, 5975 5976 /** 5977 * @function 5978 * Causes one or more active plugins to re-initialize, resetting event listeners, recalculating positions, etc. 5979 * @param {String} plugins - optional string of an individual plugin key, attained by calling `$(element).data('pluginName')`, or string of a plugin class i.e. `'dropdown'` 5980 * @default If no argument is passed, reflow all currently active plugins. 5981 */ 5982 reInit: function (plugins) { 5983 var isJQ = plugins instanceof $;
5984 try { 5985 if (isJQ) { 5986 plugins.each(function () { 5987 $(this).data('zfPlugin')._init(); 5988 }); 5989 } else { 5990 var type = typeof plugins, 5991 _this = this, 5992 fns = { 5993 'object': function (plgs) { 5994 plgs.forEach(function (p) { 5995 p = hyphenate(p); 5996 $('[data-' + p + ']').foundation('_init'); 5997 }); 5998 }, 5999 'string': function () { 6000 plugins = hyphenate(plugins); 6001 $('[data-' + plugins + ']').foundation('_init'); 6002 }, 6003 'undefined': function () { 6004 this['object'](Object.keys(_this._plugins)); 6005 } 6006 }; 6007 fns[type](plugins); 6008 } 6009 } catch (err) { 6010 console.error(err); 6011 } finally { 6012 return plugins; 6013 } 6014 }, 6015 6016 /** 6017 * returns a random base-36 uid with namespacing 6018 * @function 6019 * @param {Number} length - number of random base-36 digits desired. Increase for more random strings. 6020 * @param {String} namespace - name of plugin to be incorporated in uid, optional. 6021 * @default {String} '' - if no plugin name is provided, nothing is appended to the uid. 6022 * @returns {String} - unique id 6023 */ 6024 GetYoDigits: function (length, namespace) { 6025 length = length || 6; 6026 return Math.round(Math.pow(36, length + 1) - Math.random() * Math.pow(36, length)).toString(36).slice(1) + (namespace ? '-' + namespace : ''); 6027 }, 6028 /** 6029 * Initialize plugins on any elements within `elem` (and `elem` itself) that aren't already initialized. 6030 * @param {Object} elem - jQuery object containing the element to check inside. Also checks the element itself, unless it's the `document` object. 6031 * @param {String|Array} plugins - A list of plugins to initialize. Leave this out to initialize everything. 6032 */ 6033 reflow: function (elem, plugins) { 6034 6035 // If plugins is undefined, just grab everything 6036 if (typeof plugins === 'undefined') { 6037 plugins = Object.keys(this._plugins); 6038 } 6039 // If plugins is a string, convert it to an array with one item 6040 else if (typeof plugins === 'string') { 6041 plugins = [plugins]; 6042 } 6043 6044 var _this = this; 6045 6046 // Iterate through each plugin 6047 $.each(plugins, function (i, name) { 6048 // Get the current plugin 6049 var plugin = _this._plugins[name]; 6050 6051 // Localize the search to all elements inside elem, as well as elem itself, unless elem === document 6052 var $elem = $(elem).find('[data-' + name + ']').addBack('[data-' + name + ']'); 6053 6054 // For each plugin found, initialize it 6055 $elem.each(function () { 6056 var $el = $(this), 6057 opts = {}; 6058 // Don't double-dip on plugins 6059 if ($el.data('zfPlugin')) { 6060 // console.warn("Tried to initialize " + name + " on an element that already has a Foundation plugin."); 6061 return; 6062 } 6063 6064 if ($el.attr('data-options')) { 6065 var thing = $el.attr('data-options').split(';').forEach(function (e, i) { 6066 var opt = e.split(':').map(function (el) { 6067 return el.trim(); 6068 }); 6069 if (opt[0]) opts[opt[0]] = parseValue(opt[1]); 6070 }); 6071 } 6072 try { 6073 $el.data('zfPlugin', new plugin($(this), opts)); 6074 } catch (er) { 6075 console.error(er); 6076 } finally { 6077 return; 6078 } 6079 }); 6080 }); 6081 }, 6082 getFnName: functionName, 6083 transitionend: function ($elem) { 6084 var transitions = { 6085 'transition': 'transitionend', 6086 'WebkitTransition': 'webkitTransitionEnd', 6087 'MozTransition': 'transitionend', 6088 'OTransition': 'otransitionend' 6089 }; 6090 var elem = document.createElement('div'), 6091 end; 6092 6093 for (var t in transitions) { 6094 if (typeof elem.style[t] !== 'undefined') { 6095 end = transitions[t]; 6096 } 6097 } 6098 if (end) { 6099 return end; 6100 } else { 6101 end = setTimeout(function () { 6102 $elem.triggerHandler('transitionend', [$elem]); 6103 }, 1); 6104 return 'transitionend'; 6105 } 6106 } 6107 }; 6108 6109 Foundation.util = { 6110 /** 6111 * Function for applying a debounce effect to a function call. 6112 * @function 6113 * @param {Function} func - Function to be called at end of timeout. 6114 * @param {Number} delay - Time in ms to delay the call of `func`. 6115 * @returns function 6116 */ 6117 throttle: function (func, delay) { 6118 var timer = null; 6119 6120 return function () { 6121 var context = this, 6122 args = arguments; 6123 6124 if (timer === null) { 6125 timer = setTimeout(function () {
6126 func.apply(context, args); 6127 timer = null; 6128 }, delay); 6129 } 6130 }; 6131 } 6132 }; 6133 6134 // TODO: consider not making this a jQuery function 6135 // TODO: need way to reflow vs. re-initialize 6136 /** 6137 * The Foundation jQuery method. 6138 * @param {String|Array} method - An action to perform on the current jQuery object. 6139 */ 6140 var foundation = function (method) { 6141 var type = typeof method, 6142 $meta = $('meta.foundation-mq'), 6143 $noJS = $('.no-js'); 6144 6145 if (!$meta.length) { 6146 $('<meta class="foundation-mq">').appendTo(document.head); 6147 } 6148 if ($noJS.length) { 6149 $noJS.removeClass('no-js'); 6150 } 6151 6152 if (type === 'undefined') { 6153 //needs to initialize the Foundation object, or an individual plugin. 6154 Foundation.MediaQuery._init(); 6155 Foundation.reflow(this); 6156 } else if (type === 'string') { 6157 //an individual method to invoke on a plugin or group of plugins 6158 var args = Array.prototype.slice.call(arguments, 1); //collect all the arguments, if necessary 6159 var plugClass = this.data('zfPlugin'); //determine the class of plugin 6160 6161 if (plugClass !== undefined && plugClass[method] !== undefined) { 6162 //make sure both the class and method exist 6163 if (this.length === 1) { 6164 //if there's only one, call it directly. 6165 plugClass[method].apply(plugClass, args); 6166 } else { 6167 this.each(function (i, el) { 6168 //otherwise loop through the jQuery collection and invoke the method on each 6169 plugClass[method].apply($(el).data('zfPlugin'), args); 6170 }); 6171 } 6172 } else { 6173 //error for no class or no method 6174 throw new ReferenceError("We're sorry, '" + method + "' is not an available method for " + (plugClass ? functionName(plugClass) : 'this element') + '.'); 6175 } 6176 } else { 6177 //error for invalid argument type 6178 throw new TypeError('We\'re sorry, ' + type + ' is not a valid parameter. You must use a string representing the method you wish to invoke.'); 6179 } 6180 return this; 6181 }; 6182 6183 window.Foundation = Foundation; 6184 $.fn.foundation = foundation; 6185 6186 // Polyfill for requestAnimationFrame 6187 (function () { 6188 if (!Date.now || !window.Date.now) window.Date.now = Date.now = function () { 6189 return new Date().getTime(); 6190 }; 6191 6192 var vendors = ['webkit', 'moz']; 6193 for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { 6194 var vp = vendors[i]; 6195 window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; 6196 window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame']; 6197 } 6198 if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) { 6199 var lastTime = 0; 6200 window.requestAnimationFrame = function (callback) { 6201 var now = Date.now(); 6202 var nextTime = Math.max(lastTime + 16, now); 6203 return setTimeout(function () { 6204 callback(lastTime = nextTime); 6205 }, nextTime - now); 6206 }; 6207 window.cancelAnimationFrame = clearTimeout; 6208 } 6209 /** 6210 * Polyfill for performance.now, required by rAF 6211 */ 6212 if (!window.performance || !window.performance.now) { 6213 window.performance = { 6214 start: Date.now(), 6215 now: function () { 6216 return Date.now() - this.start; 6217 } 6218 }; 6219 } 6220 })(); 6221 if (!Function.prototype.bind) { 6222 Function.prototype.bind = function (oThis) { 6223 if (typeof this !== 'function') { 6224 // closest thing possible to the ECMAScript 5 6225 // internal IsCallable function 6226 throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); 6227 } 6228 6229 var aArgs = Array.prototype.slice.call(arguments, 1), 6230 fToBind = this, 6231 fNOP = function () {}, 6232 fBound = function () { 6233 return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); 6234 }; 6235 6236 if (this.prototype) { 6237 // native functions don't have a prototype 6238 fNOP.prototype = this.prototype; 6239 } 6240 fBound.prototype = new fNOP(); 6241 6242 return fBound; 6243 }; 6244 } 6245 // Polyfill to get the name of a function in IE9 6246 function functionName(fn) { 6247 if (Function.prototype.name === undefined) { 6248 var funcNameRegex = /function\s([^(]{1,})\(/; 6249 var results = funcNameRegex.exec(fn.toString()); 6250 return results && results.length > 1 ? results[1].trim() : ""; 6251 } else if (fn.prototype === undefined) { 6252 return fn.constructor.name; 6253 } else { 6254 return fn.prototype.constructor.name; 6255 } 6256 } 6257 function parseValue(str) { 6258 if (/true/.test(str)) return true;else if (/false/.test(str)) return false;else if (!isNaN(str * 1)) return parseFloat(str); 6259 return str; 6260 } 6261 // Convert PascalCase to kebab-case 6262 // Thank you: http://stackoverflow.com/a/8955580 6263 function hyphenate(str) { 6264 return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); 6265 } 6266}(jQuery); 6267'use strict'; 6268 6269!function ($) { 6270 6271 Foundation.Box = { 6272 ImNotTouchingYou: ImNotTouchingYou, 6273 GetDimensions: GetDimensions, 6274 GetOffsets: GetOffsets 6275 }; 6276 6277 /** 6278 * Compares the dimensions of an element to a container and determines collision events with container. 6279 * @function 6280 * @param {jQuery} element - jQuery object to test for collisions. 6281 * @param {jQuery}
6281 parent - jQuery object to use as bounding container. 6282 * @param {Boolean} lrOnly - set to true to check left and right values only. 6283 * @param {Boolean} tbOnly - set to true to check top and bottom values only. 6284 * @default if no parent object passed, detects collisions with `window`. 6285 * @returns {Boolean} - true if collision free, false if a collision in any direction. 6286 */ 6287 function ImNotTouchingYou(element, parent, lrOnly, tbOnly) { 6288 var eleDims = GetDimensions(element), 6289 top, 6290 bottom, 6291 left, 6292 right; 6293 6294 if (parent) { 6295 var parDims = GetDimensions(parent); 6296 6297 bottom = eleDims.offset.top + eleDims.height <= parDims.height + parDims.offset.top; 6298 top = eleDims.offset.top >= parDims.offset.top; 6299 left = eleDims.offset.left >= parDims.offset.left; 6300 right = eleDims.offset.left + eleDims.width <= parDims.width; 6301 } else { 6302 bottom = eleDims.offset.top + eleDims.height <= eleDims.windowDims.height + eleDims.windowDims.offset.top; 6303 top = eleDims.offset.top >= eleDims.windowDims.offset.top; 6304 left = eleDims.offset.left >= eleDims.windowDims.offset.left; 6305 right = eleDims.offset.left + eleDims.width <= eleDims.windowDims.width; 6306 } 6307 6308 var allDirs = [bottom, top, left, right]; 6309 6310 if (lrOnly) { 6311 return left === right === true; 6312 } 6313 6314 if (tbOnly) { 6315 return top === bottom === true; 6316 } 6317 6318 return allDirs.indexOf(false) === -1; 6319 }; 6320 6321 /** 6322 * Uses native methods to return an object of dimension values. 6323 * @function 6324 * @param {jQuery || HTML} element - jQuery object or DOM element for which to get the dimensions. Can be any element other that document or window. 6325 * @returns {Object} - nested object of integer pixel values 6326 * TODO - if element is window, return only those values. 6327 */ 6328 function GetDimensions(elem, test) { 6329 elem = elem.length ? elem[0] : elem; 6330 6331 if (elem === window || elem === document) { 6332 throw new Error("I'm sorry, Dave. I'm afraid I can't do that."); 6333 } 6334 6335 var rect = elem.getBoundingClientRect(), 6336 parRect = elem.parentNode.getBoundingClientRect(), 6337 winRect = document.body.getBoundingClientRect(), 6338 winY = window.pageYOffset, 6339 winX = window.pageXOffset; 6340 6341 return { 6342 width: rect.width, 6343 height: rect.height, 6344 offset: { 6345 top: rect.top + winY, 6346 left: rect.left + winX 6347 }, 6348 parentDims: { 6349 width: parRect.width, 6350 height: parRect.height, 6351 offset: { 6352 top: parRect.top + winY, 6353 left: parRect.left + winX 6354 } 6355 }, 6356 windowDims: { 6357 width: winRect.width, 6358 height: winRect.height, 6359 offset: { 6360 top: winY, 6361 left: winX 6362 } 6363 } 6364 }; 6365 } 6366 6367 /** 6368 * Returns an object of top and left integer pixel values for dynamically rendered elements, 6369 * such as: Tooltip, Reveal, and Dropdown 6370 * @function 6371 * @param {jQuery} element - jQuery object for the element being positioned. 6372 * @param {jQuery} anchor - jQuery object for the element's anchor point. 6373 * @param {String} position - a string relating to the desired position of the element, relative to it's anchor 6374 * @param {Number} vOffset - integer pixel value of desired vertical separation between anchor and element. 6375 * @param {Number} hOffset - integer pixel value of desired horizontal separation between anchor and element. 6376 * @param {Boolean} isOverflow - if a collision event is detected, sets to true to default the element to full width - any desired offset. 6377 * TODO alter/rewrite to work with `em` values as well/instead of pixels 6378 */ 6379 function GetOffsets(element, anchor, position, vOffset, hOffset, isOverflow) { 6380 var $eleDims = GetDimensions(element), 6381 $anchorDims = anchor ? GetDimensions(anchor) : null; 6382 6383 switch (position) { 6384 case 'top': 6385 return { 6386 left: Foundation.rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left, 6387 top: $anchorDims.offset.top - ($eleDims.height + vOffset) 6388 }; 6389 break; 6390 case 'left': 6391 return { 6392 left: $anchorDims.offset.left - ($eleDims.width + hOffset), 6393 top: $anchorDims.offset.top 6394 }; 6395 break; 6396 case 'right': 6397 return { 6398 left: $anchorDims.offset.left + $anchorDims.width + hOffset, 6399 top: $anchorDims.offset.top 6400 }; 6401 break; 6402 case 'center top': 6403 return { 6404 left: $anchorDims.offset.left + $anchorDims.width / 2 - $eleDims.width / 2,
6405 top: $anchorDims.offset.top - ($eleDims.height + vOffset) 6406 }; 6407 break; 6408 case 'center bottom': 6409 return { 6410 left: isOverflow ? hOffset : $anchorDims.offset.left + $anchorDims.width / 2 - $eleDims.width / 2, 6411 top: $anchorDims.offset.top + $anchorDims.height + vOffset 6412 }; 6413 break; 6414 case 'center left': 6415 return { 6416 left: $anchorDims.offset.left - ($eleDims.width + hOffset), 6417 top: $anchorDims.offset.top + $anchorDims.height / 2 - $eleDims.height / 2 6418 }; 6419 break; 6420 case 'center right': 6421 return { 6422 left: $anchorDims.offset.left + $anchorDims.width + hOffset + 1, 6423 top: $anchorDims.offset.top + $anchorDims.height / 2 - $eleDims.height / 2 6424 }; 6425 break; 6426 case 'center': 6427 return { 6428 left: $eleDims.windowDims.offset.left + $eleDims.windowDims.width / 2 - $eleDims.width / 2, 6429 top: $eleDims.windowDims.offset.top + $eleDims.windowDims.height / 2 - $eleDims.height / 2 6430 }; 6431 break; 6432 case 'reveal': 6433 return { 6434 left: ($eleDims.windowDims.width - $eleDims.width) / 2, 6435 top: $eleDims.windowDims.offset.top + vOffset 6436 }; 6437 case 'reveal full': 6438 return { 6439 left: $eleDims.windowDims.offset.left, 6440 top: $eleDims.windowDims.offset.top 6441 }; 6442 break; 6443 default: 6444 return { 6445 left: Foundation.rtl() ? $anchorDims.offset.left - $eleDims.width + $anchorDims.width : $anchorDims.offset.left, 6446 top: $anchorDims.offset.top + $anchorDims.height + vOffset 6447 }; 6448 } 6449 } 6450}(jQuery); 6451/******************************************* 6452 * * 6453 * This util was created by Marius Olbertz * 6454 * Please thank Marius on GitHub /owlbertz * 6455 * or the web http://www.mariusolbertz.de/ * 6456 * * 6457 ******************************************/ 6458 6459'use strict'; 6460 6461!function ($) { 6462 6463 var keyCodes = { 6464 9: 'TAB', 6465 13: 'ENTER', 6466 27: 'ESCAPE', 6467 32: 'SPACE', 6468 37: 'ARROW_LEFT', 6469 38: 'ARROW_UP', 6470 39: 'ARROW_RIGHT', 6471 40: 'ARROW_DOWN' 6472 }; 6473 6474 var commands = {}; 6475 6476 var Keyboard = { 6477 keys: getKeyCodes(keyCodes), 6478 6479 /** 6480 * Parses the (keyboard) event and returns a String that represents its key 6481 * Can be used like Foundation.parseKey(event) === Foundation.keys.SPACE 6482 * @param {Event} event - the event generated by the event handler 6483 * @return String key - String that represents the key pressed 6484 */ 6485 parseKey: function (event) { 6486 var key = keyCodes[event.which || event.keyCode] || String.fromCharCode(event.which).toUpperCase(); 6487 if (event.shiftKey) key = 'SHIFT_' + key; 6488 if (event.ctrlKey) key = 'CTRL_' + key; 6489 if (event.altKey) key = 'ALT_' + key; 6490 return key; 6491 }, 6492 6493 6494 /** 6495 * Handles the given (keyboard) event 6496 * @param {Event} event - the event generated by the event handler 6497 * @param {String} component - Foundation component's name, e.g. Slider or Reveal 6498 * @param {Objects} functions - collection of functions that are to be executed 6499 */ 6500 handleKey: function (event, component, functions) { 6501 var commandList = commands[component], 6502 keyCode = this.parseKey(event), 6503 cmds, 6504 command, 6505 fn; 6506 6507 if (!commandList) return console.warn('Component not defined!'); 6508 6509 if (typeof commandList.ltr === 'undefined') { 6510 // this component does not differentiate between ltr and rtl 6511 cmds = commandList; // use plain list 6512 } else { 6513 // merge ltr and rtl: if document is rtl, rtl overwrites ltr and vice versa 6514 if (Foundation.rtl()) cmds = $.extend({}, commandList.ltr, commandList.rtl);else cmds = $.extend({}, commandList.rtl, commandList.ltr); 6515 } 6516 command = cmds[keyCode]; 6517 6518 fn = functions[command]; 6519 if (fn && typeof fn === 'function') { 6520 // execute function if exists 6521 fn.apply(); 6522 if (functions.handled || typeof functions.handled === 'function') { 6523 // execute function when event was handled 6524 functions.handled.apply(); 6525 } 6526 } else { 6527 if (functions.unhandled || typeof functions.unhandled === 'function') { 6528 // execute function when event was not handled 6529 functions.unhandled.apply(); 6530 } 6531 } 6532 }, 6533 6534 6535 /** 6536 * Finds all focusable elements within the given `$element` 6537 * @param {jQuery} $element - jQuery object to search within 6538 * @return {jQuery} $focusable - all focusable elements within `$element` 6539 */ 6540 findFocusable: function ($element) {
6541 return $element.find('a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]').filter(function () { 6542 if (!$(this).is(':visible') || $(this).attr('tabindex') < 0) { 6543 return false; 6544 } //only have visible elements and those that have a tabindex greater or equal 0 6545 return true; 6546 }); 6547 }, 6548 6549 6550 /** 6551 * Returns the component name name 6552 * @param {Object} component - Foundation component, e.g. Slider or Reveal 6553 * @return String componentName 6554 */ 6555 6556 register: function (componentName, cmds) { 6557 commands[componentName] = cmds; 6558 } 6559 }; 6560 6561 /* 6562 * Constants for easier comparing. 6563 * Can be used like Foundation.parseKey(event) === Foundation.keys.SPACE 6564 */ 6565 function getKeyCodes(kcs) { 6566 var k = {}; 6567 for (var kc in kcs) { 6568 k[kcs[kc]] = kcs[kc]; 6569 }return k; 6570 } 6571 6572 Foundation.Keyboard = Keyboard; 6573}(jQuery); 6574'use strict'; 6575 6576!function ($) { 6577 6578 // Default set of media queries 6579 var defaultQueries = { 6580 'default': 'only screen', 6581 landscape: 'only screen and (orientation: landscape)', 6582 portrait: 'only screen and (orientation: portrait)', 6583 retina: 'only screen and (-webkit-min-device-pixel-ratio: 2),' + 'only screen and (min--moz-device-pixel-ratio: 2),' + 'only screen and (-o-min-device-pixel-ratio: 2/1),' + 'only screen and (min-device-pixel-ratio: 2),' + 'only screen and (min-resolution: 192dpi),' + 'only screen and (min-resolution: 2dppx)' 6584 }; 6585 6586 var MediaQuery = { 6587 queries: [], 6588 6589 current: '', 6590 6591 /** 6592 * Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher. 6593 * @function 6594 * @private 6595 */ 6596 _init: function () { 6597 var self = this; 6598 var extractedStyles = $('.foundation-mq').css('font-family'); 6599 var namedQueries; 6600 6601 namedQueries = parseStyleToObject(extractedStyles); 6602 6603 for (var key in namedQueries) { 6604 self.queries.push({ 6605 name: key, 6606 value: 'only screen and (min-width: ' + namedQueries[key] + ')' 6607 }); 6608 } 6609 6610 this.current = this._getCurrentSize(); 6611 6612 this._watcher(); 6613 }, 6614 6615 6616 /** 6617 * Checks if the screen is at least as wide as a breakpoint. 6618 * @function 6619 * @param {String} size - Name of the breakpoint to check. 6620 * @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller. 6621 */ 6622 atLeast: function (size) { 6623 var query = this.get(size); 6624 6625 if (query) { 6626 return window.matchMedia(query).matches; 6627 } 6628 6629 return false; 6630 }, 6631 6632 6633 /** 6634 * Gets the media query of a breakpoint. 6635 * @function 6636 * @param {String} size - Name of the breakpoint to get. 6637 * @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist. 6638 */ 6639 get: function (size) { 6640 for (var i in this.queries) { 6641 var query = this.queries[i]; 6642 if (size === query.name) return query.value; 6643 } 6644 6645 return null; 6646 }, 6647 6648 6649 /** 6650 * Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one). 6651 * @function 6652 * @private 6653 * @returns {String} Name of the current breakpoint. 6654 */ 6655 _getCurrentSize: function () { 6656 var matched; 6657 6658 for (var i in this.queries) { 6659 var query = this.queries[i]; 6660 6661 if (window.matchMedia(query.value).matches) { 6662 matched = query; 6663 } 6664 } 6665 6666 if (typeof matched === 'object') { 6667 return matched.name; 6668 } else { 6669 return matched; 6670 } 6671 }, 6672 6673 6674 /** 6675 * Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes. 6676 * @function 6677 * @private 6678 */ 6679 _watcher: function () { 6680 var _this = this; 6681 6682 $(window).on('resize.zf.mediaquery', function () { 6683 var newSize = _this._getCurrentSize(); 6684 6685 if (newSize !== _this.current) { 6686 // Broadcast the media query change on the window 6687 $(window).trigger('changed.zf.mediaquery', [newSize, _this.current]); 6688 6689 // Change the current media query 6690 _this.current = newSize; 6691 } 6692 }); 6693 } 6694 }; 6695 6696 Foundation.MediaQuery = MediaQuery; 6697 6698 // matchMedia() polyfill - Test a CSS media type/query in JS. 6699 // Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas, David Knight. Dual MIT/BSD license 6700 window.matchMedia || (window.matchMedia = function () { 6701 'use strict'; 6702 6703 // For browsers that support matchMedium api such as IE 9 and webkit 6704 6705 var styleMedia = window.styleMedia || window.media; 6706 6707 // For those that don't support matchMedium 6708 if (!styleMedia) { 6709 var style = document.createElement('style'), 6710 script = document.getElementsByTagName('script')[0], 6711 info = null; 6712 6713 style.type = 'text/css'; 6714 style.id = 'matchmediajs-test'; 6715 6716 script.parentNode.insertBefore(style, script); 6717 6718 // 'style.currentStyle' is used by IE <= 8 and 'window.getComputedStyle' for all other browsers 6719 info = 'getComputedStyle' in window && window.getComputedStyle(style, null) || style.currentStyle; 6720 6721 styleMedia = { 6722 matchMedium: function (media) { 6723 var text = '@media ' + media + '{ #matchmediajs-test { width: 1px; } }'; 6724 6725 // 'style.styleSheet' is used by IE <= 8 and 'style.textContent' for all other browsers 6726 if (style.styleSheet) { 6727 style.styleSheet.cssText = text; 6728 } else { 6729 style.textContent = text; 6730 } 6731 6732 // Test if media query is true or false 6733 return info.width === '1px'; 6734 } 6735 }; 6736 } 6737 6738 return function (media) { 6739 return { 6740 matches: styleMedia.matchMedium(media || 'all'), 6741 media: media || 'all' 6742 }; 6743 }; 6744 }()); 6745 6746 // Thank you: https://github.com/sindresorhus/query-string 6747 function parseStyleToObject(str) { 6748 var styleObject = {}; 6749 6750 if (typeof str !== 'string') { 6751 return styleObject; 6752 } 6753 6754 str = str.trim().slice(1, -1);
6754 // browsers re-quote string style values 6755 6756 if (!str) { 6757 return styleObject; 6758 } 6759 6760 styleObject = str.split('&').reduce(function (ret, param) { 6761 var parts = param.replace(/\+/g, ' ').split('='); 6762 var key = parts[0]; 6763 var val = parts[1]; 6764 key = decodeURIComponent(key); 6765 6766 // missing `=` should be `null`: 6767 // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters 6768 val = val === undefined ? null : decodeURIComponent(val); 6769 6770 if (!ret.hasOwnProperty(key)) { 6771 ret[key] = val; 6772 } else if (Array.isArray(ret[key])) { 6773 ret[key].push(val); 6774 } else { 6775 ret[key] = [ret[key], val]; 6776 } 6777 return ret; 6778 }, {}); 6779 6780 return styleObject; 6781 } 6782 6783 Foundation.MediaQuery = MediaQuery; 6784}(jQuery); 6785'use strict'; 6786 6787!function ($) { 6788 6789 /** 6790 * Motion module. 6791 * @module foundation.motion 6792 */ 6793 6794 var initClasses = ['mui-enter', 'mui-leave']; 6795 var activeClasses = ['mui-enter-active', 'mui-leave-active']; 6796 6797 var Motion = { 6798 animateIn: function (element, animation, cb) { 6799 animate(true, element, animation, cb); 6800 }, 6801 6802 animateOut: function (element, animation, cb) { 6803 animate(false, element, animation, cb); 6804 } 6805 }; 6806 6807 function Move(duration, elem, fn) { 6808 var anim, 6809 prog, 6810 start = null; 6811 // console.log('called'); 6812 6813 function move(ts) { 6814 if (!start) start = window.performance.now(); 6815 // console.log(start, ts); 6816 prog = ts - start; 6817 fn.apply(elem); 6818 6819 if (prog < duration) { 6820 anim = window.requestAnimationFrame(move, elem); 6821 } else { 6822 window.cancelAnimationFrame(anim); 6823 elem.trigger('finished.zf.animate', [elem]).triggerHandler('finished.zf.animate', [elem]); 6824 } 6825 } 6826 anim = window.requestAnimationFrame(move); 6827 } 6828 6829 /** 6830 * Animates an element in or out using a CSS transition class. 6831 * @function 6832 * @private 6833 * @param {Boolean} isIn - Defines if the animation is in or out. 6834 * @param {Object} element - jQuery or HTML object to animate. 6835 * @param {String} animation - CSS class to use. 6836 * @param {Function} cb - Callback to run when animation is finished. 6837 */ 6838 function animate(isIn, element, animation, cb) { 6839 element = $(element).eq(0); 6840 6841 if (!element.length) return; 6842 6843 var initClass = isIn ? initClasses[0] : initClasses[1]; 6844 var activeClass = isIn ? activeClasses[0] : activeClasses[1]; 6845 6846 // Set up the animation 6847 reset(); 6848 6849 element.addClass(animation).css('transition', 'none'); 6850 6851 requestAnimationFrame(function () { 6852 element.addClass(initClass); 6853 if (isIn) element.show(); 6854 }); 6855 6856 // Start the animation 6857 requestAnimationFrame(function () { 6858 element[0].offsetWidth; 6859 element.css('transition', '').addClass(activeClass); 6860 }); 6861 6862 // Clean up the animation when it finishes 6863 element.one(Foundation.transitionend(element), finish); 6864 6865 // Hides the element (for out animations), resets the element, and runs a callback 6866 function finish() { 6867 if (!isIn) element.hide(); 6868 reset(); 6869 if (cb) cb.apply(element); 6870 } 6871 6872 // Resets transitions and removes motion-specific classes 6873 function reset() { 6874 element[0].style.transitionDuration = 0; 6875 element.removeClass(initClass + ' ' + activeClass + ' ' + animation); 6876 } 6877 } 6878 6879 Foundation.Move = Move; 6880 Foundation.Motion = Motion; 6881}(jQuery); 6882'use strict'; 6883 6884!function ($) { 6885 6886 var Nest = { 6887 Feather: function (menu) { 6888 var type = arguments.length <= 1 || arguments[1] === undefined ? 'zf' : arguments[1]; 6889 6890 menu.attr('role', 'menubar'); 6891 6892 var items = menu.find('li').attr({ 'role': 'menuitem' }), 6893 subMenuClass = 'is-' + type + '-submenu', 6894 subItemClass = subMenuClass + '-item', 6895 hasSubClass = 'is-' + type + '-submenu-parent'; 6896 6897 menu.find('a:first').attr('tabindex', 0); 6898 6899 items.each(function () { 6900 var $item = $(this), 6901 $sub = $item.children('ul'); 6902 6903 if ($sub.length) { 6904 $item.addClass(hasSubClass).attr({ 6905 'aria-haspopup': true, 6906 'aria-expanded': false, 6907 'aria-label': $item.children('a:first').text() 6908 }); 6909 6910 $sub.addClass('submenu ' + subMenuClass).attr({ 6911 'data-submenu': '', 6912 'aria-hidden': true, 6913 'role': 'menu' 6914 }); 6915 } 6916 6917 if ($item.parent('[data-submenu]').length) { 6918 $item.addClass('is-submenu-item ' + subItemClass); 6919 } 6920 }); 6921 6922 return; 6923 }, 6924 Burn: function (menu, type) { 6925 var items = menu.find('li').removeAttr('tabindex'), 6926 subMenuClass = 'is-' + type + '-submenu', 6927 subItemClass = subMenuClass + '-item', 6928 hasSubClass = 'is-' + type + '-submenu-parent'; 6929 6930 menu.find('*').removeClass(subMenuClass + ' ' + subItemClass + ' ' + hasSubClass + ' is-submenu-item submenu is-active').removeAttr('data-submenu').css('display', ''); 6931 6932 // console.log( menu.find('.' + subMenuClass + ', .' + subItemClass + ', .has-submenu, .is-submenu-item, .submenu, [data-submenu]') 6933 // .removeClass(subMenuClass + ' ' + subItemClass + ' has-submenu is-submenu-item submenu') 6934 // .removeAttr('data-submenu')); 6935 // items.each(function(){ 6936 // var $item = $(this), 6937 // $sub = $item.children('ul'); 6938 // if($item.parent('[data-submenu]').length){ 6939 // $item.removeClass('is-submenu-item ' + subItemClass); 6940 // } 6941 // if($sub.length){ 6942 // $item.removeClass('has-submenu'); 6943 // $sub.removeClass('submenu ' + subMenuClass).removeAttr('data-submenu'); 6944 // } 6945 // }); 6946 } 6947 }; 6948 6949 Foundation.Nest = Nest; 6950}(jQuery); 6951'use strict'; 6952 6953!function ($) { 6954 6955 function Timer(elem, options, cb) { 6956 var _this = this, 6957 duration = options.duration, 6958 //options is an object for easily adding features later. 6959 nameSpace = Object.keys(elem.data())[0] || 'timer', 6960 remain = -1, 6961 start, 6962 timer; 6963 6964 this.isPaused = false;
6965 6966 this.restart = function () { 6967 remain = -1; 6968 clearTimeout(timer); 6969 this.start(); 6970 }; 6971 6972 this.start = function () { 6973 this.isPaused = false; 6974 // if(!elem.data('paused')){ return false; }//maybe implement this sanity check if used for other things. 6975 clearTimeout(timer); 6976 remain = remain <= 0 ? duration : remain; 6977 elem.data('paused', false); 6978 start = Date.now(); 6979 timer = setTimeout(function () { 6980 if (options.infinite) { 6981 _this.restart(); //rerun the timer. 6982 } 6983 cb(); 6984 }, remain); 6985 elem.trigger('timerstart.zf.' + nameSpace); 6986 }; 6987 6988 this.pause = function () { 6989 this.isPaused = true; 6990 //if(elem.data('paused')){ return false; }//maybe implement this sanity check if used for other things. 6991 clearTimeout(timer); 6992 elem.data('paused', true); 6993 var end = Date.now(); 6994 remain = remain - (end - start); 6995 elem.trigger('timerpaused.zf.' + nameSpace); 6996 }; 6997 } 6998 6999 /** 7000 * Runs a callback function when images are fully loaded. 7001 * @param {Object} images - Image(s) to check if loaded. 7002 * @param {Func} callback - Function to execute when image is fully loaded. 7003 */ 7004 function onImagesLoaded(images, callback) { 7005 var self = this, 7006 unloaded = images.length; 7007 7008 if (unloaded === 0) { 7009 callback(); 7010 } 7011 7012 images.each(function () { 7013 if (this.complete) { 7014 singleImageLoaded(); 7015 } else if (typeof this.naturalWidth !== 'undefined' && this.naturalWidth > 0) { 7016 singleImageLoaded(); 7017 } else { 7018 $(this).one('load', function () { 7019 singleImageLoaded(); 7020 }); 7021 } 7022 }); 7023 7024 function singleImageLoaded() { 7025 unloaded--; 7026 if (unloaded === 0) { 7027 callback(); 7028 } 7029 } 7030 } 7031 7032 Foundation.Timer = Timer; 7033 Foundation.onImagesLoaded = onImagesLoaded; 7034}(jQuery); 7035//************************************************** 7036//**Work inspired by multiple jquery swipe plugins** 7037//**Done by Yohai Ararat *************************** 7038//************************************************** 7039(function ($) { 7040 7041 $.spotSwipe = { 7042 version: '1.0.0', 7043 enabled: 'ontouchstart' in document.documentElement, 7044 preventDefault: false, 7045 moveThreshold: 75, 7046 timeThreshold: 200 7047 }; 7048 7049 var startPosX, 7050 startPosY, 7051 startTime, 7052 elapsedTime, 7053 isMoving = false; 7054 7055 function onTouchEnd() { 7056 // alert(this); 7057 this.removeEventListener('touchmove', onTouchMove); 7058 this.removeEventListener('touchend', onTouchEnd); 7059 isMoving = false; 7060 } 7061 7062 function onTouchMove(e) { 7063 if ($.spotSwipe.preventDefault) { 7064 e.preventDefault(); 7065 } 7066 if (isMoving) { 7067 var x = e.touches[0].pageX; 7068 var y = e.touches[0].pageY; 7069 var dx = startPosX - x; 7070 var dy = startPosY - y; 7071 var dir; 7072 elapsedTime = new Date().getTime() - startTime; 7073 if (Math.abs(dx) >= $.spotSwipe.moveThreshold && elapsedTime <= $.spotSwipe.timeThreshold) { 7074 dir = dx > 0 ? 'left' : 'right'; 7075 } 7076 // else if(Math.abs(dy) >= $.spotSwipe.moveThreshold && elapsedTime <= $.spotSwipe.timeThreshold) { 7077 // dir = dy > 0 ? 'down' : 'up'; 7078 // } 7079 if (dir) { 7080 e.preventDefault(); 7081 onTouchEnd.call(this); 7082 $(this).trigger('swipe', dir).trigger('swipe' + dir); 7083 } 7084 } 7085 } 7086 7087 function onTouchStart(e) { 7088 if (e.touches.length == 1) { 7089 startPosX = e.touches[0].pageX; 7090 startPosY = e.touches[0].pageY; 7091 isMoving = true; 7092 startTime = new Date().getTime(); 7093 this.addEventListener('touchmove', onTouchMove, false); 7094 this.addEventListener('touchend', onTouchEnd, false); 7095 } 7096 } 7097 7098 function init() { 7099 this.addEventListener && this.addEventListener('touchstart', onTouchStart, false); 7100 } 7101 7102 function teardown() { 7103 this.removeEventListener('touchstart', onTouchStart); 7104 } 7105 7106 $.event.special.swipe = { setup: init }; 7107 7108 $.each(['left', 'up', 'down', 'right'], function () { 7109 $.event.special['swipe' + this] = { setup: function () { 7110 $(this).on('swipe', $.noop); 7111 } }; 7112 }); 7113})(jQuery); 7114/**************************************************** 7115 * Method for adding psuedo drag events to elements * 7116 ***************************************************/ 7117!function ($) { 7118 $.fn.addTouch = function () { 7119 this.each(function (i, el) { 7120 $(el).bind('touchstart touchmove touchend touchcancel', function () { 7121 //we pass the original event object because the jQuery event 7122 //object is normalized to w3c specs and does not provide the TouchList 7123 handleTouch(event); 7124 }); 7125 }); 7126 7127 var handleTouch = function (event) { 7128 var touches = event.changedTouches, 7129 first = touches[0], 7130 eventTypes = { 7131 touchstart: 'mousedown', 7132 touchmove: 'mousemove', 7133 touchend: 'mouseup' 7134 }, 7135 type = eventTypes[event.type], 7136 simulatedEvent; 7137 7138 if ('MouseEvent' in window && typeof window.MouseEvent === 'function') { 7139 simulatedEvent = window.MouseEvent(type, { 7140 'bubbles': true, 7141 'cancelable': true, 7142 'screenX': first.screenX, 7143 'screenY': first.screenY, 7144 'clientX': first.clientX, 7145 'clientY': first.clientY 7146 }); 7147 } else { 7148 simulatedEvent = document.createEvent('MouseEvent'); 7149 simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0 /*left*/, null); 7150 } 7151 first.target.dispatchEvent(simulatedEvent); 7152 }; 7153 }; 7154}(jQuery); 7155 7156//********************************** 7157//**From the jQuery Mobile Library** 7158//**need to recreate functionality**
7159//**and try to improve if possible** 7160//********************************** 7161 7162/* Removing the jQuery function **** 7163************************************ 7164 7165(function( $, window, undefined ) { 7166 7167 var $document = $( document ), 7168 // supportTouch = $.mobile.support.touch, 7169 touchStartEvent = 'touchstart'//supportTouch ? "touchstart" : "mousedown", 7170 touchStopEvent = 'touchend'//supportTouch ? "touchend" : "mouseup", 7171 touchMoveEvent = 'touchmove'//supportTouch ? "touchmove" : "mousemove"; 7172 7173 // setup new event shortcuts 7174 $.each( ( "touchstart touchmove touchend " + 7175 "swipe swipeleft swiperight" ).split( " " ), function( i, name ) { 7176 7177 $.fn[ name ] = function( fn ) { 7178 return fn ? this.bind( name, fn ) : this.trigger( name ); 7179 }; 7180 7181 // jQuery < 1.8 7182 if ( $.attrFn ) { 7183 $.attrFn[ name ] = true; 7184 } 7185 }); 7186 7187 function triggerCustomEvent( obj, eventType, event, bubble ) { 7188 var originalType = event.type; 7189 event.type = eventType; 7190 if ( bubble ) { 7191 $.event.trigger( event, undefined, obj ); 7192 } else { 7193 $.event.dispatch.call( obj, event ); 7194 } 7195 event.type = originalType; 7196 } 7197 7198 // also handles taphold 7199 7200 // Also handles swipeleft, swiperight 7201 $.event.special.swipe = { 7202 7203 // More than this horizontal displacement, and we will suppress scrolling. 7204 scrollSupressionThreshold: 30, 7205 7206 // More time than this, and it isn't a swipe. 7207 durationThreshold: 1000, 7208 7209 // Swipe horizontal displacement must be more than this. 7210 horizontalDistanceThreshold: window.devicePixelRatio >= 2 ? 15 : 30, 7211 7212 // Swipe vertical displacement must be less than this. 7213 verticalDistanceThreshold: window.devicePixelRatio >= 2 ? 15 : 30, 7214 7215 getLocation: function ( event ) { 7216 var winPageX = window.pageXOffset, 7217 winPageY = window.pageYOffset, 7218 x = event.clientX, 7219 y = event.clientY; 7220 7221 if ( event.pageY === 0 && Math.floor( y ) > Math.floor( event.pageY ) || 7222 event.pageX === 0 && Math.floor( x ) > Math.floor( event.pageX ) ) { 7223 7224 // iOS4 clientX/clientY have the value that should have been 7225 // in pageX/pageY. While pageX/page/ have the value 0 7226 x = x - winPageX; 7227 y = y - winPageY; 7228 } else if ( y < ( event.pageY - winPageY) || x < ( event.pageX - winPageX ) ) { 7229 7230 // Some Android browsers have totally bogus values for clientX/Y 7231 // when scrolling/zooming a page. Detectable since clientX/clientY 7232 // should never be smaller than pageX/pageY minus page scroll 7233 x = event.pageX - winPageX; 7234 y = event.pageY - winPageY; 7235 } 7236 7237 return { 7238 x: x, 7239 y: y 7240 }; 7241 }, 7242 7243 start: function( event ) { 7244 var data = event.originalEvent.touches ? 7245 event.originalEvent.touches[ 0 ] : event, 7246 location = $.event.special.swipe.getLocation( data ); 7247 return { 7248 time: ( new Date() ).getTime(), 7249 coords: [ location.x, location.y ], 7250 origin: $( event.target ) 7251 }; 7252 }, 7253 7254 stop: function( event ) { 7255 var data = event.originalEvent.touches ? 7256 event.originalEvent.touches[ 0 ] : event, 7257 location = $.event.special.swipe.getLocation( data ); 7258 return { 7259 time: ( new Date() ).getTime(), 7260 coords: [ location.x, location.y ] 7261 }; 7262 }, 7263 7264 handleSwipe: function( start, stop, thisObject, origTarget ) { 7265 if ( stop.time - start.time < $.event.special.swipe.durationThreshold && 7266 Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold && 7267 Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) { 7268 var direction = start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight"; 7269 7270 triggerCustomEvent( thisObject, "swipe", $.Event( "swipe", { target: origTarget, swipestart: start, swipestop: stop }), true ); 7271 triggerCustomEvent( thisObject, direction,$.Event( direction, { target: origTarget, swipestart: start, swipestop: stop } ), true ); 7272 return true; 7273 } 7274 return false; 7275 7276 }, 7277 7278 // This serves as a flag to ensure that at most one swipe event event is 7279 // in work at any given time 7280 eventInProgress: false, 7281 7282 setup: function() { 7283 var events, 7284 thisObject = this, 7285 $this = $( thisObject ), 7286 context = {}; 7287 7288 // Retrieve the events data for this element and add the swipe context 7289 events = $.data( this, "mobile-events" ); 7290 if ( !events ) { 7291 events = { length: 0 }; 7292 $.data( this, "mobile-events", events ); 7293 } 7294 events.length++; 7295 events.swipe = context; 7296 7297 context.start = function( event ) { 7298 7299 // Bail if we're already working on a swipe event 7300 if ( $.event.special.swipe.eventInProgress ) { 7301 return; 7302 } 7303 $.event.special.swipe.eventInProgress = true; 7304 7305 var stop, 7306 start = $.event.special.swipe.start( event ), 7307 origTarget = event.target, 7308 emitted = false;
7309 7310 context.move = function( event ) { 7311 if ( !start || event.isDefaultPrevented() ) { 7312 return; 7313 } 7314 7315 stop = $.event.special.swipe.stop( event ); 7316 if ( !emitted ) { 7317 emitted = $.event.special.swipe.handleSwipe( start, stop, thisObject, origTarget ); 7318 if ( emitted ) { 7319 7320 // Reset the context to make way for the next swipe event 7321 $.event.special.swipe.eventInProgress = false; 7322 } 7323 } 7324 // prevent scrolling 7325 if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) { 7326 event.preventDefault(); 7327 } 7328 }; 7329 7330 context.stop = function() { 7331 emitted = true; 7332 7333 // Reset the context to make way for the next swipe event 7334 $.event.special.swipe.eventInProgress = false; 7335 $document.off( touchMoveEvent, context.move ); 7336 context.move = null; 7337 }; 7338 7339 $document.on( touchMoveEvent, context.move ) 7340 .one( touchStopEvent, context.stop ); 7341 }; 7342 $this.on( touchStartEvent, context.start ); 7343 }, 7344 7345 teardown: function() { 7346 var events, context; 7347 7348 events = $.data( this, "mobile-events" ); 7349 if ( events ) { 7350 context = events.swipe; 7351 delete events.swipe; 7352 events.length--; 7353 if ( events.length === 0 ) { 7354 $.removeData( this, "mobile-events" ); 7355 } 7356 } 7357 7358 if ( context ) { 7359 if ( context.start ) { 7360 $( this ).off( touchStartEvent, context.start ); 7361 } 7362 if ( context.move ) { 7363 $document.off( touchMoveEvent, context.move ); 7364 } 7365 if ( context.stop ) { 7366 $document.off( touchStopEvent, context.stop ); 7367 } 7368 } 7369 } 7370 }; 7371 $.each({ 7372 swipeleft: "swipe.left", 7373 swiperight: "swipe.right" 7374 }, function( event, sourceEvent ) { 7375 7376 $.event.special[ event ] = { 7377 setup: function() { 7378 $( this ).bind( sourceEvent, $.noop ); 7379 }, 7380 teardown: function() { 7381 $( this ).unbind( sourceEvent ); 7382 } 7383 }; 7384 }); 7385})( jQuery, this ); 7386*/ 7387'use strict'; 7388 7389!function ($) { 7390 7391 var MutationObserver = function () { 7392 var prefixes = ['WebKit', 'Moz', 'O', 'Ms', '']; 7393 for (var i = 0; i < prefixes.length; i++) { 7394 if (prefixes[i] + 'MutationObserver' in window) { 7395 return window[prefixes[i] + 'MutationObserver']; 7396 } 7397 } 7398 return false; 7399 }(); 7400 7401 var triggers = function (el, type) { 7402 el.data(type).split(' ').forEach(function (id) { 7403 $('#' + id)[type === 'close' ? 'trigger' : 'triggerHandler'](type + '.zf.trigger', [el]); 7404 }); 7405 }; 7406 // Elements with [data-open] will reveal a plugin that supports it when clicked. 7407 $(document).on('click.zf.trigger', '[data-open]', function () { 7408 triggers($(this), 'open'); 7409 }); 7410 7411 // Elements with [data-close] will close a plugin that supports it when clicked. 7412 // If used without a value on [data-close], the event will bubble, allowing it to close a parent component. 7413 $(document).on('click.zf.trigger', '[data-close]', function () { 7414 var id = $(this).data('close'); 7415 if (id) { 7416 triggers($(this), 'close'); 7417 } else { 7418 $(this).trigger('close.zf.trigger'); 7419 } 7420 }); 7421 7422 // Elements with [data-toggle] will toggle a plugin that supports it when clicked. 7423 $(document).on('click.zf.trigger', '[data-toggle]', function () { 7424 triggers($(this), 'toggle'); 7425 }); 7426 7427 // Elements with [data-closable] will respond to close.zf.trigger events. 7428 $(document).on('close.zf.trigger', '[data-closable]', function (e) { 7429 e.stopPropagation(); 7430 var animation = $(this).data('closable'); 7431 7432 if (animation !== '') { 7433 Foundation.Motion.animateOut($(this), animation, function () { 7434 $(this).trigger('closed.zf'); 7435 }); 7436 } else { 7437 $(this).fadeOut().trigger('closed.zf'); 7438 } 7439 }); 7440 7441 $(document).on('focus.zf.trigger blur.zf.trigger', '[data-toggle-focus]', function () { 7442 var id = $(this).data('toggle-focus'); 7443 $('#' + id).triggerHandler('toggle.zf.trigger', [$(this)]); 7444 }); 7445 7446 /** 7447 * Fires once after all other scripts have loaded 7448 * @function 7449 * @private 7450 */ 7451 $(window).load(function () { 7452 checkListeners(); 7453 }); 7454 7455 function checkListeners() { 7456 eventsListener(); 7457 resizeListener(); 7458 scrollListener(); 7459 closemeListener(); 7460 } 7461 7462 //******** only fires this function once on load, if there's something to watch ******** 7463 function closemeListener(pluginName) { 7464 var yetiBoxes = $('[data-yeti-box]'), 7465 plugNames = ['dropdown', 'tooltip', 'reveal']; 7466 7467 if (pluginName) { 7468 if (typeof pluginName === 'string') { 7469 plugNames.push(pluginName); 7470 } else if (typeof pluginName === 'object' && typeof pluginName[0] === 'string') { 7471 plugNames.concat(pluginName); 7472 } else { 7473 console.error('Plugin names must be strings'); 7474 } 7475 } 7476 if (yetiBoxes.length) { 7477 var listeners = plugNames.map(function (name) { 7478 return 'closeme.zf.' + name; 7479 }).join(' '); 7480 7481 $(window).off(listeners).on(listeners, function (e, pluginId) { 7482 var plugin = e.namespace.split('.')[0]; 7483 var plugins = $('[data-' + plugin + ']').not('[data-yeti-box="' + pluginId + '"]'); 7484 7485 plugins.each(function () { 7486 var _this = $(this); 7487 7488 _this.triggerHandler('close.zf.trigger', [_this]); 7489 }); 7490 }); 7491 } 7492 } 7493 7494 function resizeListener(debounce) { 7495 var timer = undefined, 7496 $nodes = $('[data-resize]'); 7497 if ($nodes.length) { 7498 $(window).off('resize.zf.trigger').on('resize.zf.trigger', function (e) { 7499 if (timer) { 7500 clearTimeout(timer); 7501 } 7502 7503 timer = setTimeout(function () { 7504 7505 if (!MutationObserver) { 7506 //fallback for IE 9 7507 $nodes.each(function () { 7508 $(this).triggerHandler('resizeme.zf.trigger'); 7509 }); 7510 } 7511 //trigger all listening elements and signal a resize event 7512 $nodes.attr('data-events', "resize"); 7513 }, debounce || 10); //default time to emit resize event 7514 }); 7515 } 7516 } 7517 7518 function scrollListener(debounce) { 7519 var timer = undefined, 7520 $nodes = $('[data-scroll]'); 7521 if ($nodes.length) { 7522 $(window).off('scroll.zf.trigger').on('scroll.zf.trigger', function (e) { 7523 if (timer) { 7524 clearTimeout(timer); 7525 } 7526 7527 timer = setTimeout(function () { 7528 7529 if (!MutationObserver) { 7530 //fallback for IE 9 7531 $nodes.each(function () { 7532 $(this).triggerHandler('scrollme.zf.trigger'); 7533 }); 7534 } 7535 //trigger all listening elements and signal a scroll event 7536 $nodes.attr('data-events', "scroll"); 7537 }, debounce || 10); //default time to emit scroll event 7538 }); 7539 } 7540 } 7541 7542 function eventsListener() { 7543 if (!MutationObserver) { 7544 return false;
7545 } 7546 var nodes = document.querySelectorAll('[data-resize], [data-scroll], [data-mutate]'); 7547 7548 //element callback 7549 var listeningElementsMutation = function (mutationRecordsList) { 7550 var $target = $(mutationRecordsList[0].target); 7551 //trigger the event handler for the element depending on type 7552 switch ($target.attr("data-events")) { 7553 7554 case "resize": 7555 $target.triggerHandler('resizeme.zf.trigger', [$target]); 7556 break; 7557 7558 case "scroll": 7559 $target.triggerHandler('scrollme.zf.trigger', [$target, window.pageYOffset]); 7560 break; 7561 7562 // case "mutate" : 7563 // console.log('mutate', $target); 7564 // $target.triggerHandler('mutate.zf.trigger'); 7565 // 7566 // //make sure we don't get stuck in an infinite loop from sloppy codeing 7567 // if ($target.index('[data-mutate]') == $("[data-mutate]").length-1) { 7568 // domMutationObserver(); 7569 // } 7570 // break; 7571 7572 default: 7573 return false; 7574 //nothing 7575 } 7576 }; 7577 7578 if (nodes.length) { 7579 //for each element that needs to listen for resizing, scrolling, (or coming soon mutation) add a single observer 7580 for (var i = 0; i <= nodes.length - 1; i++) { 7581 var elementObserver = new MutationObserver(listeningElementsMutation); 7582 elementObserver.observe(nodes[i], { attributes: true, childList: false, characterData: false, subtree: false, attributeFilter: ["data-events"] }); 7583 } 7584 } 7585 } 7586 7587 // ------------------------------------ 7588 7589 // [PH] 7590 // Foundation.CheckWatchers = checkWatchers; 7591 Foundation.IHearYou = checkListeners; 7592 // Foundation.ISeeYou = scrollListener; 7593 // Foundation.IFeelYou = closemeListener; 7594}(jQuery); 7595 7596// function domMutationObserver(debounce) { 7597// // !!! This is coming soon and needs more work; not active !!! // 7598// var timer, 7599// nodes = document.querySelectorAll('[data-mutate]'); 7600// // 7601// if (nodes.length) { 7602// // var MutationObserver = (function () { 7603// // var prefixes = ['WebKit', 'Moz', 'O', 'Ms', '']; 7604// // for (var i=0; i < prefixes.length; i++) { 7605// // if (prefixes[i] + 'MutationObserver' in window) { 7606// // return window[prefixes[i] + 'MutationObserver']; 7607// // } 7608// // } 7609// // return false; 7610// // }()); 7611// 7612// 7613// //for the body, we need to listen for all changes effecting the style and class attributes 7614// var bodyObserver = new MutationObserver(bodyMutation); 7615// bodyObserver.observe(document.body, { attributes: true, childList: true, characterData: false, subtree:true, attributeFilter:["style", "class"]}); 7616// 7617// 7618// //body callback 7619// function bodyMutation(mutate) { 7620// //trigger all listening elements and signal a mutation event 7621// if (timer) { clearTimeout(timer); } 7622// 7623// timer = setTimeout(function() { 7624// bodyObserver.disconnect(); 7625// $('[data-mutate]').attr('data-events',"mutate"); 7626// }, debounce || 150); 7627// } 7628// } 7629// } 7630'use strict'; 7631 7632var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 7633 7634function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7635 7636!function ($) { 7637 7638 /** 7639 * Tooltip module. 7640 * @module foundation.tooltip 7641 * @requires foundation.util.box 7642 * @requires foundation.util.triggers 7643 */ 7644 7645 var Tooltip = function () { 7646 /** 7647 * Creates a new instance of a Tooltip. 7648 * @class 7649 * @fires Tooltip#init 7650 * @param {jQuery} element - jQuery object to attach a tooltip to. 7651 * @param {Object} options - object to extend the default configuration. 7652 */ 7653 7654 function Tooltip(element, options) { 7655 _classCallCheck(this, Tooltip); 7656 7657 this.$element = element; 7658 this.options = $.extend({}, Tooltip.defaults, this.$element.data(), options); 7659 7660 this.isActive = false;
7661 this.isClick = false; 7662 this._init(); 7663 7664 Foundation.registerPlugin(this, 'Tooltip'); 7665 } 7666 7667 /** 7668 * Initializes the tooltip by setting the creating the tip element, adding it's text, setting private variables and setting attributes on the anchor. 7669 * @private 7670 */ 7671 7672 7673 _createClass(Tooltip, [{ 7674 key: '_init', 7675 value: function _init() { 7676 var elemId = this.$element.attr('aria-describedby') || Foundation.GetYoDigits(6, 'tooltip'); 7677 7678 this.options.positionClass = this._getPositionClass(this.$element); 7679 this.options.tipText = this.options.tipText || this.$element.attr('title'); 7680 this.template = this.options.template ? $(this.options.template) : this._buildTemplate(elemId); 7681 7682 this.template.appendTo(document.body).text(this.options.tipText).hide(); 7683 7684 this.$element.attr({ 7685 'title': '', 7686 'aria-describedby': elemId, 7687 'data-yeti-box': elemId, 7688 'data-toggle': elemId, 7689 'data-resize': elemId 7690 }).addClass(this.triggerClass); 7691 7692 //helper variables to track movement on collisions 7693 this.usedPositions = []; 7694 this.counter = 4; 7695 this.classChanged = false; 7696 7697 this._events(); 7698 } 7699 7700 /** 7701 * Grabs the current positioning class, if present, and returns the value or an empty string. 7702 * @private 7703 */ 7704 7705 }, { 7706 key: '_getPositionClass', 7707 value: function _getPositionClass(element) { 7708 if (!element) { 7709 return ''; 7710 } 7711 // var position = element.attr('class').match(/top|left|right/g); 7712 var position = element[0].className.match(/\b(top|left|right)\b/g); 7713 position = position ? position[0] : ''; 7714 return position; 7715 } 7716 }, { 7717 key: '_buildTemplate', 7718 7719 /** 7720 * builds the tooltip element, adds attributes, and returns the template. 7721 * @private 7722 */ 7723 value: function _buildTemplate(id) { 7724 var templateClasses = (this.options.tooltipClass + ' ' + this.options.positionClass + ' ' + this.options.templateClasses).trim(); 7725 var $template = $('<div></div>').addClass(templateClasses).attr({ 7726 'role': 'tooltip', 7727 'aria-hidden': true, 7728 'data-is-active': false, 7729 'data-is-focus': false, 7730 'id': id 7731 }); 7732 return $template; 7733 } 7734 7735 /** 7736 * Function that gets called if a collision event is detected. 7737 * @param {String} position - positioning class to try 7738 * @private 7739 */ 7740 7741 }, { 7742 key: '_reposition', 7743 value: function _reposition(position) { 7744 this.usedPositions.push(position ? position : 'bottom'); 7745 7746 //default, try switching to opposite side 7747 if (!position && this.usedPositions.indexOf('top') < 0) { 7748 this.template.addClass('top'); 7749 } else if (position === 'top' && this.usedPositions.indexOf('bottom') < 0) { 7750 this.template.removeClass(position); 7751 } else if (position === 'left' && this.usedPositions.indexOf('right') < 0) { 7752 this.template.removeClass(position).addClass('right'); 7753 } else if (position === 'right' && this.usedPositions.indexOf('left') < 0) { 7754 this.template.removeClass(position).addClass('left'); 7755 } 7756 7757 //if default change didn't work, try bottom or left first 7758 else if (!position && this.usedPositions.indexOf('top') > -1 && this.usedPositions.indexOf('left') < 0) { 7759 this.template.addClass('left'); 7760 } else if (position === 'top' && this.usedPositions.indexOf('bottom') > -1 && this.usedPositions.indexOf('left') < 0) { 7761 this.template.removeClass(position).addClass('left'); 7762 } else if (position === 'left' && this.usedPositions.indexOf('right') > -1 && this.usedPositions.indexOf('bottom') < 0) { 7763 this.template.removeClass(position); 7764 } else if (position === 'right' && this.usedPositions.indexOf('left') > -1 && this.usedPositions.indexOf('bottom') < 0) { 7765 this.template.removeClass(position); 7766 } 7767 //if nothing cleared, set to bottom 7768 else { 7769 this.template.removeClass(position); 7770 } 7771 this.classChanged = true; 7772 this.counter--; 7773 } 7774 7775 /** 7776 * sets the position class of an element and recursively calls itself until there are no more possible positions to attempt, or the tooltip element is no longer colliding. 7777 * if the tooltip is larger than the screen width, default to full width - any user selected margin 7778 * @private 7779 */ 7780 7781 }, {
7782 key: '_setPosition', 7783 value: function _setPosition() { 7784 var position = this._getPositionClass(this.template), 7785 $tipDims = Foundation.Box.GetDimensions(this.template), 7786 $anchorDims = Foundation.Box.GetDimensions(this.$element), 7787 direction = position === 'left' ? 'left' : position === 'right' ? 'left' : 'top', 7788 param = direction === 'top' ? 'height' : 'width', 7789 offset = param === 'height' ? this.options.vOffset : this.options.hOffset, 7790 _this = this; 7791 7792 if ($tipDims.width >= $tipDims.windowDims.width || !this.counter && !Foundation.Box.ImNotTouchingYou(this.template)) { 7793 this.template.offset(Foundation.Box.GetOffsets(this.template, this.$element, 'center bottom', this.options.vOffset, this.options.hOffset, true)).css({ 7794 // this.$element.offset(Foundation.GetOffsets(this.template, this.$element, 'center bottom', this.options.vOffset, this.options.hOffset, true)).css({ 7795 'width': $anchorDims.windowDims.width - this.options.hOffset * 2, 7796 'height': 'auto' 7797 }); 7798 return false; 7799 } 7800 7801 this.template.offset(Foundation.Box.GetOffsets(this.template, this.$element, 'center ' + (position || 'bottom'), this.options.vOffset, this.options.hOffset)); 7802 7803 while (!Foundation.Box.ImNotTouchingYou(this.template) && this.counter) { 7804 this._reposition(position); 7805 this._setPosition(); 7806 } 7807 } 7808 7809 /** 7810 * reveals the tooltip, and fires an event to close any other open tooltips on the page 7811 * @fires Tooltip#closeme 7812 * @fires Tooltip#show 7813 * @function 7814 */ 7815 7816 }, { 7817 key: 'show', 7818 value: function show() { 7819 if (this.options.showOn !== 'all' && !Foundation.MediaQuery.atLeast(this.options.showOn)) { 7820 // console.error('The screen is too small to display this tooltip'); 7821 return false; 7822 } 7823 7824 var _this = this; 7825 this.template.css('visibility', 'hidden').show(); 7826 this._setPosition(); 7827 7828 /** 7829 * Fires to close all other open tooltips on the page 7830 * @event Closeme#tooltip 7831 */ 7832 this.$element.trigger('closeme.zf.tooltip', this.template.attr('id')); 7833 7834 this.template.attr({ 7835 'data-is-active': true, 7836 'aria-hidden': false 7837 }); 7838 _this.isActive = true; 7839 // console.log(this.template); 7840 this.template.stop().hide().css('visibility', '').fadeIn(this.options.fadeInDuration, function () { 7841 //maybe do stuff? 7842 }); 7843 /** 7844 * Fires when the tooltip is shown 7845 * @event Tooltip#show 7846 */ 7847 this.$element.trigger('show.zf.tooltip'); 7848 } 7849 7850 /** 7851 * Hides the current tooltip, and resets the positioning class if it was changed due to collision 7852 * @fires Tooltip#hide 7853 * @function 7854 */ 7855 7856 }, { 7857 key: 'hide', 7858 value: function hide() { 7859 // console.log('hiding', this.$element.data('yeti-box')); 7860 var _this = this; 7861 this.template.stop().attr({ 7862 'aria-hidden': true, 7863 'data-is-active': false 7864 }).fadeOut(this.options.fadeOutDuration, function () { 7865 _this.isActive = false; 7866 _this.isClick = false; 7867 if (_this.classChanged) { 7868 _this.template.removeClass(_this._getPositionClass(_this.template)).addClass(_this.options.positionClass); 7869 7870 _this.usedPositions = []; 7871 _this.counter = 4; 7872 _this.classChanged = false; 7873 } 7874 }); 7875 /** 7876 * fires when the tooltip is hidden 7877 * @event Tooltip#hide 7878 */ 7879 this.$element.trigger('hide.zf.tooltip'); 7880 } 7881 7882 /** 7883 * adds event listeners for the tooltip and its anchor
7884 * TODO combine some of the listeners like focus and mouseenter, etc. 7885 * @private 7886 */ 7887 7888 }, { 7889 key: '_events', 7890 value: function _events() { 7891 var _this = this; 7892 var $template = this.template; 7893 var isFocus = false; 7894 7895 if (!this.options.disableHover) { 7896 7897 this.$element.on('mouseenter.zf.tooltip', function (e) { 7898 if (!_this.isActive) { 7899 _this.timeout = setTimeout(function () { 7900 _this.show(); 7901 }, _this.options.hoverDelay); 7902 } 7903 }).on('mouseleave.zf.tooltip', function (e) { 7904 clearTimeout(_this.timeout); 7905 if (!isFocus || !_this.isClick && _this.options.clickOpen) { 7906 _this.hide(); 7907 } 7908 }); 7909 } 7910 7911 if (this.options.clickOpen) { 7912 this.$element.on('mousedown.zf.tooltip', function (e) { 7913 e.stopImmediatePropagation(); 7914 if (_this.isClick) { 7915 _this.hide(); 7916 // _this.isClick = false; 7917 } else { 7918 _this.isClick = true; 7919 if ((_this.options.disableHover || !_this.$element.attr('tabindex')) && !_this.isActive) { 7920 _this.show(); 7921 } 7922 } 7923 }); 7924 } 7925 7926 if (!this.options.disableForTouch) { 7927 this.$element.on('tap.zf.tooltip touchend.zf.tooltip', function (e) { 7928 _this.isActive ? _this.hide() : _this.show(); 7929 }); 7930 } 7931 7932 this.$element.on({ 7933 // 'toggle.zf.trigger': this.toggle.bind(this), 7934 // 'close.zf.trigger': this.hide.bind(this) 7935 'close.zf.trigger': this.hide.bind(this) 7936 }); 7937 7938 this.$element.on('focus.zf.tooltip', function (e) { 7939 isFocus = true; 7940 // console.log(_this.isClick); 7941 if (_this.isClick) { 7942 return false; 7943 } else { 7944 // $(window) 7945 _this.show(); 7946 } 7947 }).on('focusout.zf.tooltip', function (e) { 7948 isFocus = false; 7949 _this.isClick = false; 7950 _this.hide(); 7951 }).on('resizeme.zf.trigger', function () { 7952 if (_this.isActive) { 7953 _this._setPosition(); 7954 } 7955 }); 7956 } 7957 7958 /** 7959 * adds a toggle method, in addition to the static show() & hide() functions 7960 * @function 7961 */ 7962 7963 }, { 7964 key: 'toggle', 7965 value: function toggle() { 7966 if (this.isActive) { 7967 this.hide(); 7968 } else { 7969 this.show(); 7970 } 7971 } 7972 7973 /** 7974 * Destroys an instance of tooltip, removes template element from the view. 7975 * @function 7976 */ 7977 7978 }, { 7979 key: 'destroy', 7980 value: function destroy() { 7981 this.$element.attr('title', this.template.text()).off('.zf.trigger .zf.tootip') 7982 // .removeClass('has-tip') 7983 .removeAttr('aria-describedby').removeAttr('data-yeti-box').removeAttr('data-toggle').removeAttr('data-resize'); 7984 7985 this.template.remove(); 7986 7987 Foundation.unregisterPlugin(this); 7988 } 7989 }]); 7990 7991 return Tooltip; 7992 }(); 7993 7994 Tooltip.defaults = { 7995 disableForTouch: false, 7996 /** 7997 * Time, in ms, before a tooltip should open on hover. 7998 * @option 7999 * @example 200 8000 */ 8001 hoverDelay: 200, 8002 /** 8003 * Time, in ms, a tooltip should take to fade into view. 8004 * @option 8005 * @example 150 8006 */ 8007 fadeInDuration: 150, 8008 /** 8009 * Time, in ms, a tooltip should take to fade out of view. 8010 * @option 8011 * @example 150 8012 */ 8013 fadeOutDuration: 150, 8014 /** 8015 * Disables hover events from opening the tooltip if set to true 8016 * @option 8017 * @example false 8018 */ 8019 disableHover: false, 8020 /** 8021 * Optional addtional classes to apply to the tooltip template on init. 8022 * @option 8023 * @example 'my-cool-tip-class' 8024 */ 8025 templateClasses: '', 8026 /** 8027 * Non-optional class added to tooltip templates. Foundation default is 'tooltip'. 8028 * @option 8029 * @example 'tooltip' 8030 */ 8031 tooltipClass: 'tooltip', 8032 /** 8033 * Class applied to the tooltip anchor element. 8034 * @option 8035 * @example 'has-tip' 8036 */ 8037 triggerClass: 'has-tip', 8038 /** 8039 * Minimum breakpoint size at which to open the tooltip. 8040 * @option 8041 * @example 'small' 8042 */ 8043 showOn: 'small', 8044 /** 8045 * Custom template to be used to generate markup for tooltip. 8046 * @option 8047 * @example '<div class="tooltip"></div>' 8048 */ 8049 template: '', 8050 /** 8051 * Text displayed in the tooltip template on open. 8052 * @option 8053 * @example 'Some cool space fact here.' 8054 */ 8055 tipText: '', 8056 touchCloseText: 'Tap to close.', 8057 /** 8058 * Allows the tooltip to remain open if triggered with a click or touch event. 8059 * @option 8060 * @example true 8061 */ 8062 clickOpen: true, 8063 /** 8064 * Additional positioning classes, set by the JS 8065 * @option 8066 * @example 'top' 8067 */ 8068 positionClass: '', 8069 /** 8070 * Distance, in pixels, the template should push away from the anch
8070or on the Y axis. 8071 * @option 8072 * @example 10 8073 */ 8074 vOffset: 10, 8075 /** 8076 * Distance, in pixels, the template should push away from the anchor on the X axis, if aligned to a side. 8077 * @option 8078 * @example 12 8079 */ 8080 hOffset: 12 8081 }; 8082 8083 /** 8084 * TODO utilize resize event trigger 8085 */ 8086 8087 // Window exports 8088 Foundation.plugin(Tooltip, 'Tooltip'); 8089}(jQuery); 8090'use strict'; 8091 8092var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 8093 8094function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8095 8096!function ($) { 8097 8098 /** 8099 * Reveal module. 8100 * @module foundation.reveal 8101 * @requires foundation.util.keyboard 8102 * @requires foundation.util.box 8103 * @requires foundation.util.triggers 8104 * @requires foundation.util.mediaQuery 8105 * @requires foundation.util.motion if using animations 8106 */ 8107 8108 var Reveal = function () { 8109 /** 8110 * Creates a new instance of Reveal. 8111 * @class 8112 * @param {jQuery} element - jQuery object to use for the modal. 8113 * @param {Object} options - optional parameters. 8114 */ 8115 8116 function Reveal(element, options) { 8117 _classCallCheck(this, Reveal); 8118 8119 this.$element = element; 8120 this.options = $.extend({}, Reveal.defaults, this.$element.data(), options); 8121 this._init(); 8122 8123 Foundation.registerPlugin(this, 'Reveal'); 8124 Foundation.Keyboard.register('Reveal', { 8125 'ENTER': 'open', 8126 'SPACE': 'open', 8127 'ESCAPE': 'close', 8128 'TAB': 'tab_forward', 8129 'SHIFT_TAB': 'tab_backward' 8130 }); 8131 } 8132 8133 /** 8134 * Initializes the modal by adding the overlay and close buttons, (if selected). 8135 * @private 8136 */ 8137 8138 8139 _createClass(Reveal, [{ 8140 key: '_init', 8141 value: function _init() { 8142 this.id = this.$element.attr('id'); 8143 this.isActive = false; 8144 this.cached = { mq: Foundation.MediaQuery.current }; 8145 this.isiOS = iPhoneSniff(); 8146 8147 if (this.isiOS) { 8148 this.$element.addClass('is-ios'); 8149 } 8150 8151 this.$anchor = $('[data-open="' + this.id + '"]').length ? $('[data-open="' + this.id + '"]') : $('[data-toggle="' + this.id + '"]'); 8152 8153 if (this.$anchor.length) { 8154 var anchorId = this.$anchor[0].id || Foundation.GetYoDigits(6, 'reveal'); 8155 8156 this.$anchor.attr({ 8157 'aria-controls': this.id, 8158 'id': anchorId, 8159 'aria-haspopup': true, 8160 'tabindex': 0 8161 }); 8162 this.$element.attr({ 'aria-labelledby': anchorId }); 8163 } 8164 8165 if (this.options.fullScreen || this.$element.hasClass('full')) { 8166 this.options.fullScreen = true; 8167 this.options.overlay = false; 8168 } 8169 if (this.options.overlay && !this.$overlay) { 8170 this.$overlay = this._makeOverlay(this.id); 8171 } 8172 8173 this.$element.attr({ 8174 'role': 'dialog', 8175 'aria-hidden': true, 8176 'data-yeti-box': this.id, 8177 'data-resize': this.id 8178 }); 8179 8180 if (this.$overlay) { 8181 this.$element.detach().appendTo(this.$overlay); 8182 } else { 8183 this.$element.detach().appendTo($('body')); 8184 this.$element.addClass('without-overlay'); 8185 } 8186 this._events(); 8187 if (this.options.deepLink && window.location.hash === '#' + this.id) { 8188 $(window).one('load.zf.reveal', this.open.bind(this)); 8189 } 8190 } 8191 8192 /** 8193 * Creates an overlay div to display behind the modal. 8194 * @private 8195 */ 8196 8197 }, { 8198 key: '_makeOverlay', 8199 value: function _makeOverlay(id) {
8200 var $overlay = $('<div></div>').addClass('reveal-overlay').attr({ 'tabindex': -1, 'aria-hidden': true }).appendTo('body'); 8201 return $overlay; 8202 } 8203 8204 /** 8205 * Updates position of modal 8206 * TODO: Figure out if we actually need to cache these values or if it doesn't matter 8207 * @private 8208 */ 8209 8210 }, { 8211 key: '_updatePosition', 8212 value: function _updatePosition() { 8213 var width = this.$element.outerWidth(); 8214 var outerWidth = $(window).width(); 8215 var height = this.$element.outerHeight(); 8216 var outerHeight = $(window).height(); 8217 var left = parseInt((outerWidth - width) / 2, 10); 8218 var top; 8219 if (height > outerHeight) { 8220 top = parseInt(Math.min(100, outerHeight / 10), 10); 8221 } else { 8222 top = parseInt((outerHeight - height) / 4, 10); 8223 } 8224 this.$element.css({ top: top + 'px' }); 8225 // only worry about left if we don't have an overlay, otherwise we're perfectly in the middle 8226 if (!this.$overlay) { 8227 this.$element.css({ left: left + 'px' }); 8228 } 8229 } 8230 8231 /** 8232 * Adds event handlers for the modal. 8233 * @private 8234 */ 8235 8236 }, { 8237 key: '_events', 8238 value: function _events() { 8239 var _this = this; 8240 8241 this.$element.on({ 8242 'open.zf.trigger': this.open.bind(this), 8243 'close.zf.trigger': this.close.bind(this), 8244 'toggle.zf.trigger': this.toggle.bind(this), 8245 'resizeme.zf.trigger': function () { 8246 _this._updatePosition(); 8247 } 8248 }); 8249 8250 if (this.$anchor.length) { 8251 this.$anchor.on('keydown.zf.reveal', function (e) { 8252 if (e.which === 13 || e.which === 32) { 8253 e.stopPropagation(); 8254 e.preventDefault(); 8255 _this.open(); 8256 } 8257 }); 8258 } 8259 8260 if (this.options.closeOnClick && this.options.overlay) { 8261 this.$overlay.off('.zf.reveal').on('click.zf.reveal', function (e) { 8262 if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target)) { 8263 return; 8264 } 8265 _this.close(); 8266 }); 8267 } 8268 if (this.options.deepLink) { 8269 $(window).on('popstate.zf.reveal:' + this.id, this._handleState.bind(this)); 8270 } 8271 } 8272 8273 /** 8274 * Handles modal methods on back/forward button clicks or any other event that triggers popstate. 8275 * @private 8276 */ 8277 8278 }, { 8279 key: '_handleState', 8280 value: function _handleState(e) { 8281 if (window.location.hash === '#' + this.id && !this.isActive) { 8282 this.open(); 8283 } else { 8284 this.close(); 8285 } 8286 } 8287 8288 /** 8289 * Opens the modal controlled by `this.$anchor`, and closes all others by default. 8290 * @function 8291 * @fires Reveal#closeme 8292 * @fires Reveal#open 8293 */ 8294 8295 }, { 8296 key: 'open', 8297 value: function open() { 8298 var _this2 = this; 8299 8300 if (this.options.deepLink) { 8301 var hash = '#' + this.id; 8302 8303 if (window.history.pushState) { 8304 window.history.pushState(null, null, hash); 8305 } else { 8306 window.location.hash = hash; 8307 } 8308 } 8309 8310 this.isActive = true; 8311 8312 // Make elements invisible, but remove display: none so we can get size and positioning 8313 this.$element.css({ 'visibility': 'hidden' }).show().scrollTop(0); 8314 if (this.options.overlay) { 8315 this.$overlay.css({ 'visibility': 'hidden' }).show(); 8316 } 8317 8318 this._updatePosition(); 8319 8320 this.$element.hide().css({ 'visibility': '' }); 8321 8322 if (this.$overlay) { 8323 this.$overlay.css({ 'visibility': '' }).hide(); 8324 } 8325 8326 if (!this.options.multipleOpened) { 8327 /** 8328 * Fires immediately before the modal opens. 8329 * Closes any other modals that are currently open 8330 * @event Reveal#closeme 8331 */ 8332 this.$element.trigger('closeme.zf.reveal', this.id); 8333 } 8334 8335 // Motion UI method of reveal 8336 if (this.options.animationIn) { 8337 if (this.options.overlay) { 8338 Foundation.Motion.animateIn(this.$overlay, 'fade-in'); 8339 } 8340 Foundation.Motion.animateIn(this.$element, this.options.animationIn, function () { 8341 this.focusableElements = Foundation.Keyboard.findFocusable(this.$element); 8342 }); 8343 } 8344 // jQuery method of reveal 8345 else { 8346 if (this.options.overlay) { 8347 this.$overlay.show(0); 8348 } 8349 this.$element.show(this.options.showDelay); 8350 } 8351 8352 // handle accessibility 8353 this.$element.attr({ 8354 'aria-hidden': false, 8355 'tabindex': -1 8356 }).focus(); 8357 8358 /** 8359 * Fires when the modal has successfully opened. 8360 * @event Reveal#open 8361 */ 8362 this.$element.trigger('open.zf.reveal'); 8363 8364 if (this.isiOS) { 8365 var scrollPos = window.pageYOffset; 8366 $('html, body').addClass('is-reveal-open').scrollTop(scrollPos); 8367 } else { 8368 $('body').addClass('is-reveal-open'); 8369 } 8370 8371 $('body').addClass('is-reveal-open').attr('aria-hidden', this.options.overlay || this.options.fullScreen ? true : false); 8372 8373 setTimeout(function () { 8374 _this2._extraHandlers(); 8375 }, 0); 8376 } 8377 8378 /** 8379 * Adds extra event handlers for the body and window if necessary. 8380 * @private 8381 */ 8382 8383 }, {
8384 key: '_extraHandlers', 8385 value: function _extraHandlers() { 8386 var _this = this; 8387 this.focusableElements = Foundation.Keyboard.findFocusable(this.$element); 8388 8389 if (!this.options.overlay && this.options.closeOnClick && !this.options.fullScreen) { 8390 $('body').on('click.zf.reveal', function (e) { 8391 if (e.target === _this.$element[0] || $.contains(_this.$element[0], e.target)) { 8392 return; 8393 } 8394 _this.close(); 8395 }); 8396 } 8397 8398 if (this.options.closeOnEsc) { 8399 $(window).on('keydown.zf.reveal', function (e) { 8400 Foundation.Keyboard.handleKey(e, 'Reveal', { 8401 close: function () { 8402 if (_this.options.closeOnEsc) { 8403 _this.close(); 8404 _this.$anchor.focus(); 8405 } 8406 } 8407 }); 8408 if (_this.focusableElements.length === 0) { 8409 // no focusable elements inside the modal at all, prevent tabbing in general 8410 e.preventDefault(); 8411 } 8412 }); 8413 } 8414 8415 // lock focus within modal while tabbing 8416 this.$element.on('keydown.zf.reveal', function (e) { 8417 var $target = $(this); 8418 // handle keyboard event with keyboard util 8419 Foundation.Keyboard.handleKey(e, 'Reveal', { 8420 tab_forward: function () { 8421 if (_this.$element.find(':focus').is(_this.focusableElements.eq(-1))) { 8422 // left modal downwards, setting focus to first element 8423 _this.focusableElements.eq(0).focus(); 8424 e.preventDefault(); 8425 } 8426 }, 8427 tab_backward: function () { 8428 if (_this.$element.find(':focus').is(_this.focusableElements.eq(0)) || _this.$element.is(':focus')) { 8429 // left modal upwards, setting focus to last element 8430 _this.focusableElements.eq(-1).focus(); 8431 e.preventDefault(); 8432 } 8433 }, 8434 open: function () { 8435 if (_this.$element.find(':focus').is(_this.$element.find('[data-close]'))) { 8436 setTimeout(function () { 8437 // set focus back to anchor if close button has been activated 8438 _this.$anchor.focus(); 8439 }, 1); 8440 } else if ($target.is(_this.focusableElements)) { 8441 // dont't trigger if acual element has focus (i.e. inputs, links, ...) 8442 _this.open(); 8443 } 8444 }, 8445 close: function () { 8446 if (_this.options.closeOnEsc) { 8447 _this.close(); 8448 _this.$anchor.focus(); 8449 } 8450 } 8451 }); 8452 }); 8453 } 8454 8455 /** 8456 * Closes the modal. 8457 * @function 8458 * @fires Reveal#closed 8459 */ 8460 8461 }, { 8462 key: 'close', 8463 value: function close() { 8464 if (!this.isActive || !this.$element.is(':visible')) { 8465 return false; 8466 } 8467 var _this = this; 8468 8469 // Motion UI method of hiding 8470 if (this.options.animationOut) { 8471 if (this.options.overlay) { 8472 Foundation.Motion.animateOut(this.$overlay, 'fade-out', finishUp); 8473 } else { 8474 finishUp(); 8475 } 8476 8477 Foundation.Motion.animateOut(this.$element, this.options.animationOut); 8478 } 8479 // jQuery method of hiding 8480 else { 8481 if (this.options.overlay) { 8482 this.$overlay.hide(0, finishUp); 8483 } else { 8484 finishUp(); 8485 } 8486 8487 this.$element.hide(this.options.hideDelay); 8488 } 8489 8490 // Conditionals to remove extra event listeners added on open 8491 if (this.options.closeOnEsc) { 8492 $(window).off('keydown.zf.reveal'); 8493 } 8494 8495 if (!this.options.overlay && this.options.closeOnClick) { 8496 $('body').off('click.zf.reveal'); 8497 } 8498 8499 this.$element.off('keydown.zf.reveal'); 8500 8501 function finishUp() { 8502 if (_this.isiOS) { 8503 $('html, body').removeClass('is-reveal-open'); 8504 } else { 8505 $('body').removeClass('is-reveal-open'); 8506 } 8507 8508 $('body').attr({ 8509 'aria-hidden': false, 8510 'tabindex': '' 8511 }); 8512 8513 _this.$element.attr('aria-hidden', true); 8514 8515 /** 8516 * Fires when the modal is done closing. 8517 * @event Reveal#closed 8518 */ 8519 _this.$element.trigger('closed.zf.reveal'); 8520 } 8521 8522 /** 8523 * Resets the modal content 8524 * This prevents a running video to keep going in the background 8525 */ 8526 if (this.options.resetOnClose) { 8527 this.$element.html(this.$element.html()); 8528 } 8529 8530 this.isActive = false;
8531 if (_this.options.deepLink) { 8532 if (window.history.replaceState) { 8533 window.history.replaceState("", document.title, window.location.pathname); 8534 } else { 8535 window.location.hash = ''; 8536 } 8537 } 8538 } 8539 8540 /** 8541 * Toggles the open/closed state of a modal. 8542 * @function 8543 */ 8544 8545 }, { 8546 key: 'toggle', 8547 value: function toggle() { 8548 if (this.isActive) { 8549 this.close(); 8550 } else { 8551 this.open(); 8552 } 8553 } 8554 }, { 8555 key: 'destroy', 8556 8557 8558 /** 8559 * Destroys an instance of a modal. 8560 * @function 8561 */ 8562 value: function destroy() { 8563 if (this.options.overlay) { 8564 this.$overlay.hide().off().remove(); 8565 } 8566 this.$element.hide().off(); 8567 this.$anchor.off('.zf'); 8568 $(window).off('.zf.reveal:' + this.id); 8569 8570 Foundation.unregisterPlugin(this); 8571 } 8572 }]); 8573 8574 return Reveal; 8575 }(); 8576 8577 Reveal.defaults = { 8578 /** 8579 * Motion-UI class to use for animated elements. If none used, defaults to simple show/hide. 8580 * @option 8581 * @example 'slide-in-left' 8582 */ 8583 animationIn: '', 8584 /** 8585 * Motion-UI class to use for animated elements. If none used, defaults to simple show/hide. 8586 * @option 8587 * @example 'slide-out-right' 8588 */ 8589 animationOut: '', 8590 /** 8591 * Time, in ms, to delay the opening of a modal after a click if no animation used. 8592 * @option 8593 * @example 10 8594 */ 8595 showDelay: 0, 8596 /** 8597 * Time, in ms, to delay the closing of a modal after a click if no animation used. 8598 * @option 8599 * @example 10 8600 */ 8601 hideDelay: 0, 8602 /** 8603 * Allows a click on the body/overlay to close the modal. 8604 * @option 8605 * @example true 8606 */ 8607 closeOnClick: true, 8608 /** 8609 * Allows the modal to close if the user presses the `ESCAPE` key. 8610 * @option 8611 * @example true 8612 */ 8613 closeOnEsc: true, 8614 /** 8615 * If true, allows multiple modals to be displayed at once. 8616 * @option 8617 * @example false 8618 */ 8619 multipleOpened: false, 8620 /** 8621 * Distance, in pixels, the modal should push down from the top of the screen. 8622 * @option 8623 * @example 100 8624 */ 8625 vOffset: 100, 8626 /** 8627 * Distance, in pixels, the modal should push in from the side of the screen. 8628 * @option 8629 * @example 0 8630 */ 8631 hOffset: 0, 8632 /** 8633 * Allows the modal to be fullscreen, completely blocking out the rest of the view. JS checks for this as well. 8634 * @option 8635 * @example false 8636 */ 8637 fullScreen: false, 8638 /** 8639 * Percentage of screen height the modal should push up from the bottom of the view. 8640 * @option 8641 * @example 10 8642 */ 8643 btmOffsetPct: 10, 8644 /** 8645 * Allows the modal to generate an overlay div, which will cover the view when modal opens. 8646 * @option 8647 * @example true 8648 */ 8649 overlay: true, 8650 /** 8651 * Allows the modal to remove and reinject markup on close. Should be true if using video elements w/o using provider's api, otherwise, videos will continue to play in the background. 8652 * @option 8653 * @example false 8654 */ 8655 resetOnClose: false, 8656 /** 8657 * Allows the modal to alter the url on open/close, and allows the use of the `back` button to close modals. ALSO, allows a modal to auto-maniacally open on page load IF the hash === the modal's user-set id. 8658 * @option 8659 * @example false 8660 */ 8661 deepLink: false 8662 }; 8663 8664 // Window exports 8665 Foundation.plugin(Reveal, 'Reveal'); 8666 8667 function iPhoneSniff() { 8668 return (/iP(ad|hone|od).*OS/.test(window.navigator.userAgent) 8669 ); 8670 } 8671}(jQuery); 8672'use strict'; 8673/******************************************* 8674 * * 8675 * This Ride was created by Marius Olbertz * 8676 * Please thank Marius on GitHub /owlbertz * 8677 * or the web http://www.mariusolbertz.de/ * 8678 * * 8679 ******************************************/ 8680 8681var _createClass = function () { 8682 function defineProperties(target, props) { 8683 for (var i = 0; i < props.length; i++) { 8684 var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor); 8685 } 8686 }return function (Constructor, protoProps, staticProps) { 8687 if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor; 8688 }; 8689}(); 8690 8691function _classCallCheck(instance, Constructor) { 8692 if (!(instance instanceof Constructor)) { 8693 throw new TypeError("Cannot call a class as a function"); 8694 } 8695} 8696 8697!function (root, factory) { 8698 if (typeof define === 'function' && define.amd) { 8699 // AMD 8700 define(['jquery'], factory); 8701 } else if (typeof exports === 'object') { 8702 // Node, CommonJS-like 8703 module.exports = factory((typeof window !== "undefined" ? window['$'] : typeof global !== "undefined" ? global['$'] : null)); 8704 } else { 8705 // Browser globals (root is window) 8706 root.returnExports = factory(root.jQuery); 8707 } 8708}(this, function ($) { 8709 /** 8710 * Joyride module. 8711 * @module foundation.joyride 8712 * @requires foundation.util.keyboard 8713 * @requires foundation.Tooltip 8714 * @requires foundation.Reveal 8715 */ 8716 8717 var Joyride = function () { 8718 /** 8719 * Creates a new instance of Joyride. 8720 * @class 8721 * @param {jQuery} element - jQuery object (list) to be used as the structure. 8722 * @param {Object} options - object to extend the default configuration. 8723 */ 8724 8725 function Joyride(element, options) { 8726 _classCallCheck(this, Joyride); 8727 8728 this.$element = element; 8729 this.options = $.extend({}, Joyride.defaults, this.$element.data(), options); 8730 this._init(); 8731 8732 Foundation.registerPlugin(this, 'Joyride'); 8733 Foundation.Keyboard.register('Joyride', { 8734 'ltr': {
8735 'ARROW_RIGHT': 'next', 8736 'ARROW_LEFT': 'previous', 8737 'ESCAPE': 'close' 8738 }, 8739 'rtl': { 8740 'ARROW_RIGHT': 'previous', 8741 'ARROW_LEFT': 'next' 8742 } 8743 }); 8744 } 8745 8746 /** 8747 * Initializes the joyride by rendering required markup 8748 * @private 8749 */ 8750 8751 _createClass(Joyride, [{ 8752 key: '_init', 8753 value: function _init() { 8754 this.id = this.$element.attr('id') || Foundation.GetYoDigits(6, 'joyride'); 8755 this.current = 0; 8756 this.$items = $([]); // initialize empty collection 8757 this.structure = this._parseList(); 8758 this._render(this.structure); 8759 this._events(); 8760 8761 if (this.options.autostart) { 8762 this.start(); 8763 } 8764 } 8765 8766 /** 8767 * Parses the list of the instance, stored in $element. 8768 * @private 8769 * @return {Array} structure 8770 */ 8771 8772 }, { 8773 key: '_parseList', 8774 value: function _parseList() { 8775 var structure = []; 8776 this.$element.find('li').each(function (i) { 8777 var item = $.extend({}, { 8778 text: $(this).html(), 8779 $target: $($(this).data('target')), 8780 isModal: !!!$($(this).data('target')).length, 8781 closable: Joyride.defaults.closable 8782 }, $(this).data()); 8783 structure.push(item); 8784 }); 8785 return structure; 8786 } 8787 8788 /** 8789 * Creates the markup for the items 8790 * @private 8791 * @param {Array} structure the joyride's structure from _parseList 8792 * @return {Object} markup jQuery representation of the generated markup 8793 */ 8794 8795 }, { 8796 key: '_render', 8797 value: function _render(structure) { 8798 for (var s in structure) { 8799 var options = $.extend({}, this.options, structure[s]), 8800 8801 // if specifc item has config, this should overwrite global settings 8802 $item; 8803 8804 if (options.$target.length) { 8805 // target element exists, create tooltip 8806 var tooltip = new Foundation.Tooltip(structure[s].$target, { 8807 positionClass: options.position, 8808 disableHover: true, 8809 clickOpen: false, 8810 tooltipClass: 'tooltip joyride', 8811 triggerClass: '', 8812 hOffset: this.options.hOffset, 8813 vOffset: this.options.vOffset, 8814 showOn: 'all' // to avoid conflicts with standalone version when MQs are not set up properly. Should be somewhat rewritten 8815 }); 8816 this.structure[s].item = tooltip; 8817 $item = tooltip.template; 8818 } else { 8819 // not target, create modal with Reveal 8820 var modal = new Foundation.Reveal($('<div class="reveal joyride"/>').appendTo($('body'))); 8821 this.structure[s].item = modal; 8822 $item = modal.$element; 8823 } 8824 $item.attr({ 8825 'data-index': s, 8826 'data-joyride-for': structure[s].target 8827 }).html(structure[s].text); 8828 if (options.keyboardAccess) { 8829 $item.attr('tabindex', '-1'); 8830 } 8831 8832 this.$items = this.$items.add($item); 8833 8834 // add buttons 8835 if (structure[s].nextText || options.showNext && s < structure.length - 1 || structure[s].prevText || options.showPrev && s > 0) { 8836 var $buttons = $('<div class="joyride-buttons"/>'); 8837 if (structure[s].prevText || options.showPrev && s > 0) { 8838 $buttons.append($(this.options.prevButton).text(options.prevText)); 8839 } 8840 if (structure[s].nextText || options.showNext && s < structure.length - 1) { 8841 $buttons.append($(this.options.nextButton).text(options.nextText)); 8842 } 8843 $item.append($buttons); 8844 } 8845 8846 // add close button 8847 if (options.closable) { 8848 var $close = $(this.options.closeButton); 8849 $close.find('.show-for-sr').text(this.options.closeText); 8850 $item.prepend($close); 8851 } 8852 } 8853 } 8854 8855 /** 8856 * Shows the item with the given index 8857 * @private 8858 * @param {Number} index of the item to be displayed 8859 */ 8860 8861 }, {
8862 key: '_showItem', 8863 value: function _showItem(index) { 8864 if (this.structure[index].isModal) { 8865 this.structure[index].item.open(); 8866 } else { 8867 this.structure[index].item.show(); 8868 } 8869 // scroll target into view if target exists 8870 if (this.structure[index].$target.length) { 8871 8872 var self = this; 8873 8874 setTimeout(function () { 8875 8876 $('html, body').stop().animate({ 8877 'scrollTop': Math.max(0, self.$items.eq(index).offset().top - self.options.scrollOffset) 8878 }, self.options.scrollSpeed); 8879 8880 }, 100); 8881 8882 } 8883 if (this.options.keyboardAccess) { 8884 this.$items.eq(index).focus(); 8885 } 8886 this.current = index; 8887 } 8888 8889 /** 8890 * Hides the item with the given index 8891 * @private 8892 * @param {Number} index of the item to be hidden 8893 */ 8894 8895 }, { 8896 key: '_hideItem', 8897 value: function _hideItem(index) { 8898 if (this.structure[index].isModal) { 8899 this.structure[index].item.close(); 8900 } else { 8901 this.structure[index].item.hide(); 8902 } 8903 } 8904 8905 /** 8906 * Hides all items 8907 * @private 8908 */ 8909 8910 }, { 8911 key: '_hideAll', 8912 value: function _hideAll() { 8913 for (var s in this.structure) { 8914 this._hideItem(s); 8915 } 8916 } 8917 8918 /** 8919 * Shows the next item in the ride 8920 * @private 8921 */ 8922 8923 }, { 8924 key: 'showNext', 8925 value: function showNext() { 8926 this._hideItem(this.current); 8927 this._showItem(this.current + 1); 8928 } 8929 8930 /** 8931 * Shows the previous item in the ride 8932 * @private 8933 */ 8934 8935 }, { 8936 key: 'showPrev', 8937 value: function showPrev() { 8938 this._hideItem(this.current); 8939 this._showItem(this.current - 1); 8940 } 8941 8942 /** 8943 * Starts the ride 8944 * @private 8945 * @return {Number} index - the index where to start, 0 by default 8946 */ 8947 8948 }, { 8949 key: 'start', 8950 value: function start(index) { 8951 var index = index || 0; 8952 this._hideAll(); 8953 this._showItem(index); 8954 } 8955 8956 /** 8957 * Adds event handlers for the modal. 8958 * @private 8959 */ 8960 8961 }, { 8962 key: '_events', 8963 value: function _events() { 8964 var _this = this; 8965 $('[data-joyride-start="#' + _this.id + '"]').click(function () { 8966 _this.start(); 8967 }); 8968 8969 this.$items.on('click.zf.joyride', '[data-joyride-next]', function (e) { 8970 _this.showNext(); 8971 }).on('click.zf.joyride', '[data-joyride-prev]', function (e) { 8972 _this.showPrev(); 8973 }).on('click.zf.joyride', '[data-joyride-close]', function (e) { 8974 e.preventDefault(); 8975 if (_this.structure[_this.current].closable) { 8976 _this._hideItem(_this.current); 8977 } 8978 }).on('keydown.zf.joyride', function (e) { 8979 var $element = $(this); 8980 Foundation.Keyboard.handleKey(e, 'Joyride', { 8981 next: function () { 8982 if ($element.data('index') < _this.structure.length - 1) { 8983 _this.showNext(); 8984 } 8985 }, 8986 previous: function () { 8987 if ($element.data('index') > 0) { 8988 _this.showPrev(); 8989 } 8990 }, 8991 close: function () { 8992 if (_this.structure[_this.current].closable) { 8993 _this._hideItem(_this.current); 8994 } 8995 }, 8996 handled: function () { 8997 e.preventDefault(); 8998 } 8999 }); 9000 }); 9001 } 9002 9003 /** 9004 * Destroys an instance of a Joyride. 9005 * @fires Joyride#destroyed 9006 */ 9007 9008 }, { 9009 key: 'destroy', 9010 value: function destroy() { 9011 this.$element.hide(); 9012 9013 for (var s in this.structure) { 9014 this.structure[s].item.destroy(); 9015 } 9016 9017 /** 9018 * Fires when the plugin has been destroyed. 9019 * @event Joyride#destroyed 9020 */ 9021 this.$element.trigger('destroyed.zf.joyride'); 9022 9023 Foundation.unregisterPlugin(this); 9024 } 9025 }]); 9026 9027 return Joyride; 9028 }(); 9029 9030 Joyride.defaults = { 9031 /** 9032 * Autostart the joyride on page load. 9033 * @option 9034 * @example false 9035 */ 9036 autostart: false, 9037 /** 9038 * Speed with which the page scrolls to the next stop. 9039 * @option 9040 * @example 1000 9041 */ 9042 scrollSpeed: 1000, 9043 /** 9044 * Enable navigation via keyboard. 9045 * @option 9046 * @example true 9047 */ 9048 keyboardAccess: true, 9049 /** 9050 * If the joyride elements should be closable. 9051 * @option 9052 * @example true 9053 */ 9054 closable: true, 9055 /** 9056 * Text for the next button. 9057 * @option 9058 * @example 'Next' 9059 */ 9060 nextText: 'Next', 9061 /** 9062 * Text for the previous button. 9063 * @option 9064 * @example 'Previous' 9065 */ 9066 prevText: 'Previous', 9067 /** 9068 * Text for the close button (for accessibility purposes). 9069 * @option 9070 * @example 'Close' 9071 */ 9072 closeText: 'Close', 9073 /** 9074 * Whether to show next button. 9075 * @option 9076 * @example true 9077 */ 9078 showNext: true, 9079 /** 9080 * Whether to show previous button. 9081 * @option 9082 * @example true 9083 */ 9084 showPrev: true, 9085 /** 9086 * Vertical offset for tooltips (see tooltip plugin configuration). 9087 * @option 9088 * @example true 9089 */ 9090 vOffset: 10, 9091 /** 9092 * Horizontal offset for tooltips (see tooltip plugin configuration). 9093 * @option 9094 * @example true 9095 */ 9096 hOffset: 12, 9097 /** 9098 * Offset while scrolling the window. 9099 * @option 9100 * @example 50 9101 */ 9102 scrollOffset: 50, 9103 /** 9104 * Position of the tooltips (see tooltip plugin configuration). 9105 * @option 9106 * @example true 9107 */ 9108 position: 'top center', 9109 /** 9110 * Templates for the close button. 9111 * @option 9112 * @example '<a href="#close" class="close" data-joyride-close><span aria-hidden="true">×</span><span class="show-for-sr"></span></a>' 9113 */ 9114 closeButton: '<a href="#close" class="close" data-joyride-close><span aria-hidden="true">×</span><span class="show-for-sr"></span></a>', 9115 /** 9116 * Templates for the next button. 9117 * @option 9118 * @example '<button class="button" data-joyride-next></button>' 9119 */ 9120 nextButton: '<button class="button" data-joyride-next></button>', 9121 /** 9122 * Templates for the pevious button. 9123 * @option 9124 * @example '<button class="button" data-joyride-prev></button>' 9125 */ 9126 prevButton: '<button class="button" data-joyride-prev></button>' 9127 }; 9128 9129 // Window exports 9130 Foundation.plugin(Joyride, 'Joyride'); 9131 9132 return Joyride; 9133}); 9134 9135}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 9136 9137},{}],15:[function(require,module,exports){ 9138'use strict'; 9139 9140var Draggabilly = require('draggabilly'); 9141 9142var UserGalleryGrid = function() { 9143 9144 var self = this; 9145 9146 self.config = { 9147 'grid' : '.user-gallery-grid', 9148 'sizer' : '.user-gallery-grid__sizer', 9149 'item' : '.user-gallery-grid__item' 9150 } 9151 9152 var grid = self.config.grid; 9153 var sizer = self.config.sizer; 9154 var item = self.config.item; 9155 var packery; 9156 9157 9158 self.imagesLoadListener = function() { 9159 9160 $(grid).imagesLoaded().progress(function() { 9161 self.createGrid(); 9162 }); 9163 } 9164 9165 9166 self.createGrid = function() { 9167 9168 /*packery = new Packery(grid, { 9169 columnWidth: sizer, 9170 itemSelector: item 9171 }); 9172 9173 self.draggableConfig(packery); 9174 self.windowResizeHandler(packery);*/ 9175 } 9176 9177 9178 self.windowResizeHandler = function(packery) { 9179 9180 $(window).on('resize updateGridLayout', function() { 9181 9182 setTimeout(function () { 9183 packery.layout(); 9184 }, 1000); 9185 9186 setTimeout(function () { 9187 packery.layout(); 9188 }, 3000); 9189 }); 9190 } 9191 9192 9193 self.draggableConfig = function(packery) { 9194 9195 var $grid = $(grid); 9196 9197 if ($grid.hasClass('is-draggable')) { 9198 9199 $grid.find(item).each(function(i, gridItem) { 9200 9201 var draggie = new Draggabilly(gridItem); 9202 9203 packery.bindDraggabillyEvents(draggie); 9204 }); 9205 9206 9207 // Add order attributes to items 9208 var addIndexAttrs = function(resetLayout) { 9209 9210 var setAttrs = function() { 9211 9212 var itemsArr = []; 9213
9214 packery.getItemElements().forEach(function(itemElem, i) { 9215 9216 var itemData = {}; 9217 9218 itemData.item = $(itemElem).find('a').attr('href'); 9219 itemData.i = i; 9220 9221 itemsArr.push(itemData); 9222 itemElem.setAttribute('data-index', i); 9223 }); 9224 9225 saveItemsIndex(itemsArr); 9226 }; 9227 9228 if (resetLayout) { 9229 9230 setTimeout(function() { 9231 packery.layout(); 9232 packery.on('layoutComplete', function() { 9233 setAttrs(); 9234 }); 9235 }, 700); 9236 9237 } else { 9238 9239 setAttrs(); 9240 } 9241 }; 9242 9243 packery.on('layoutComplete', addIndexAttrs(true)); 9244 packery.on('dragItemPositioned', addIndexAttrs); 9245 } 9246 } 9247 9248 9249 self.layoutCompleteListener = function(cb) { 9250 9251 $(grid).on('layoutComplete', function(e) { 9252 9253 if (typeof cb === 'function') { 9254 9255 cb(); 9256 } 9257 }); 9258 } 9259 9260 9261 self.init = function() { 9262 self.imagesLoadListener(); 9263 } 9264}; 9265 9266module.exports = UserGalleryGrid; 9267 9268},{"draggabilly":2}],16:[function(require,module,exports){ 9269'use strict'; 9270 9271var Constants = require('../constants/constants'); 9272 9273var UserNav = function() { 9274 9275 var self = this; 9276 9277 self.config = { 9278 'button' : $('#userNavOpenActions'), 9279 'nav' : $('#userNavActions'), 9280 'mainHeader' : $('.main-header'), 9281 'navContainer' : $('.user-nav') 9282 } 9283 9284 var $button = self.config.button, 9285 $nav = self.config.nav, 9286 $mainHeader = self.config.mainHeader, 9287 $navContainer = self.config.navContainer; 9288 9289 9290 self.matchMediaHandler = function() { 9291 9292 var mq = new Constants().MEDIA_QUERIES.MEDIUM, 9293 mql = window.matchMedia(mq); 9294 9295 mql.addListener(self.mediaQueryHandler); 9296 9297 self.mediaQueryHandler(mql); 9298 } 9299 9300 9301 self.mediaQueryHandler = function(mql) { 9302 9303 if (mql.matches) { 9304 9305 self.moveNav(true); 9306 } else { 9307 9308 self.moveNav(false); 9309 } 9310 } 9311 9312 9313 self.moveNav = function(outside) { 9314 9315 if (outside === true) { 9316 $nav.detach().appendTo($mainHeader); 9317 } else if (outside === false) { 9318 $nav.detach().appendTo($navContainer); 9319 } 9320 } 9321 9322 9323 self.clickHandler = function() { 9324 9325 $button.on('click', function() { 9326 self.navVisibility(); 9327 }); 9328 } 9329 9330 9331 self.navVisibility = function() { 9332 9333 if ($nav.hasClass('is-visible')) { 9334 self.hideNav(); 9335 } else { 9336 self.showNav(); 9337 } 9338 } 9339 9340 9341 self.hideNav = function() { 9342 9343 if (! $nav.hasClass('velocity-animating')) { 9344 $nav 9345 .velocity('stop') 9346 .velocity( 9347 'slideUp', 9348 { 9349 easing: 'easeOutQuart', 9350 complete: function() { 9351 $nav.removeClass('is-visible'); 9352 } 9353 } 9354 ); 9355 } 9356 } 9357 9358 9359 self.showNav = function() { 9360 9361 if (! $nav.hasClass('velocity-animating')) { 9362 $nav 9363 .velocity('stop') 9364 .velocity( 9365 'slideDown', 9366 { 9367 easing: 'easeOutQuart', 9368 complete: function() { 9369 $nav.addClass('is-visible'); 9370 } 9371 } 9372 ); 9373 } 9374 } 9375 9376 9377 self.forceLoadImages = function() { 9378 $.extend($.lazyLoadXT, { 9379 forceLoad: true 9380 }); 9381 } 9382 9383 9384 self.clickOutsideHandler = function() { 9385 9386 $(document).on('click', function(e) { 9387 9388 if (! $(e.target).closest($nav).length) { 9389 9390 if ($nav.is(':visible')) { 9391 self.hideNav(); 9392 } 9393 } 9394 }); 9395 } 9396 9397 9398 self.init = function() { 9399 self.matchMediaHandler(); 9400 self.clickHandler(); 9401 self.forceLoadImages(); 9402 self.clickOutsideHandler(); 9403 } 9404}; 9405 9406module.exports = UserNav; 9407 9408},{"../constants/constants":8}],17:[function(require,module,exports){ 9409(function (global){ 9410'use strict'; 9411 9412var $ = (typeof window !== "undefined" ? window['$'] : typeof global !== "undefined" ? global['$'] : null); 9413var ProfileToolbar = require('../_modules/profile-toolbar/profile-toolbar'); 9414var UserGalleryGrid = require('../_modules/user-gallery-grid/user-gallery-grid'); 9415var Alert = require('../_modules/alert/alert'); 9416var ProfileCover = require('../_modules/profile-cover/profile-cover'); 9417var SearchResultThumbnail = require('../_modules/search-result-thumbnail/search-result-thumbnail'); 9418var PhotoActions = require('../_modules/photo-actions/photo-actions'); 9419var UserNav = require('../_modules/user-nav/user-nav'); 9420var Tutorial = require('../_modules/tutorial/tutorial'); 9421 9422$(function() { 9423 9424 modal.init(); 9425 9426 new ProfileToolbar().init(); 9427 new UserGalleryGrid().init(); 9428 new Alert().init(); 9429 new ProfileCover().init(); 9430 new SearchResultThumbnail().init(); 9431 new PhotoActions().init(); 9432 new UserNav().init(); 9433 new Tutorial().init(); 9434}); 9435 9436}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 9437 9438},{"../_modules/alert/alert":7,"../_modules/photo-actions/photo-actions":9,"../_modules/profile-cover/profile-cover":10,"../_modules/profile-toolbar/profile-toolbar":11,"../_modules/search-result-thumbnail/search-result-thumbnail":12,"../_modules/tutorial/tutorial":13,"../_modules/user-gallery-grid/user-gallery-grid":15,"../_modules/user-nav/user-nav":16}]},{},[17])
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.