PageSourceSearch

https://www.fotografiacatalunya.cat/js/user-galleries.js

js fotografiacatalunya.cat collected 2026-10-02 12:29:28 UTC 255,929 bytes, 9,438 lines download raw bytes

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 '&lt;div class="tooltip"&gt;&lt;/div&gt;'
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">&times</span><span class="show-for-sr"></span></a>'
9113     */
9114    closeButton: '<a href="#close" class="close" data-joyride-close><span aria-hidden="true">&times</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.