1/*! 2 * ---------------------------- DRAGEND JS ------------------------------------- 3 * 4 * Version: <%= pkg.version %> 5 * https://github.com/Stereobit/dragend 6 * Copyright (c) 2014 Tobias Otte, [email protected] 7 * 8 * Licensed under MIT-style license: 9 * 10 * Permission is hereby granted, free of charge, to any person obtaining a copy 11 * of this software and associated documentation files (the "Software"), to deal 12 * in the Software without restriction, including without limitation the rights 13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 * copies of the Software, and to permit persons to whom the Software is 15 * furnished to do so, subject to the following conditions: 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. 24 * 25 */ 26 27; (function (window) { 28 "use strict"; 29 30 // help the minifier 31 var doc = document, 32 win = window; 33 34 function init($) { 35 36 // Welcome To dragend JS 37 // ===================== 38 // 39 // dragend.js is a touch ready, full responsive, content swipe script. It has no dependencies 40 // It also can, but don't has to, used as a jQuery 41 // (https://github.com/jquery/jquery/) plugin. 42 // 43 // The current version is <%= pkg.version %> 44 // 45 // Usage 46 // ===================== 47 // 48 // To activate dragend JS just call the function on a jQuery element 49 // 50 // $("#swipe-container").dragend(); 51 // 52 // You could rather pass in a options object or a string to bump on of the 53 // following behaviors: "up", "down", "left", "right" for swiping in one of 54 // these directions, "page" with the page number as second argument to go to a 55 // explicit page and without any value to go to the first page 56 // 57 // Settings 58 // ===================== 59 // 60 // You can use the following options: 61 // 62 // * pageClass: classname selector for all elments that should provide a page 63 // * direction: "horizontal" or "vertical" 64 // * minDragDistance: minuimum distance (in pixel) the user has to drag 65 // to trigger swip 66 // * scribe: pixel value for a possible scribe 67 // * onSwipeStart: callback function before the animation 68 // * onSwipeEnd: callback function after the animation 69 // * onDragStart: called on drag start 70 // * onDrag: callback on drag 71 // * onDragEnd: callback on dragend 72 // * borderBetweenPages: if you need space between pages add a pixel value 73 // * duration 74 // * stopPropagation 75 // * afterInitialize called after the pages are size 76 // * preventDrag if want to prevent user interactions and only swipe manualy 77 78 var 79 80 // Default setting 81 defaultSettings = { 82 pageClass: "dragend-page", 83 direction: "horizontal", 84 minDragDistance: "40", 85 onSwipeStart: noop, 86 onSwipeEnd: noop, 87 onDragStart: noop, 88 onDrag: noop, 89 onDragEnd: noop, 90 afterInitialize: noop, 91 keyboardNavigation: false, 92 stopPropagation: false, 93 itemsInPage: 1, 94 scribe: 0, 95 borderBetweenPages: 0, 96 duration: 300, 97 preventDrag: false 98 }, 99 100 isTouch = 'ontouchstart' in win, 101 102 startEvent = isTouch ? 'touchstart' : 'mousedown', 103 moveEvent = isTouch ? 'touchmove' : 'mousemove', 104 endEvent = isTouch ? 'touchend' : 'mouseup', 105 106 keycodes = { 107 37: "left", 108 38: "up", 109 39: "right", 110 40: "down" 111 }, 112 113 errors = {
114 pages: "No pages found" 115 }, 116 117 containerStyles = { 118 overflow: "hidden", 119 padding: 0 120 }, 121 122 supports = (function () { 123 var div = doc.createElement('div'), 124 vendors = 'Khtml Ms O Moz Webkit'.split(' '), 125 len = vendors.length; 126 127 return function (prop) { 128 if (prop in div.style) return true; 129 130 prop = prop.replace(/^[a-z]/, function (val) { 131 return val.toUpperCase(); 132 }); 133 134 while (len--) { 135 if (vendors[len] + prop in div.style) { 136 return true; 137 } 138 } 139 return false; 140 }; 141 })(), 142 143 supportTransform = supports('transform'); 144 145 function noop() { } 146 147 function setStyles(element, styles) { 148 149 var property, 150 value; 151 152 for (property in styles) { 153 154 if (styles.hasOwnProperty(property)) { 155 value = styles[property]; 156 157 switch (property) { 158 case "height": 159 case "width": 160 case "marginLeft": 161 case "marginTop": 162 value += "px"; 163 } 164 165 element.style[property] = value; 166 167 } 168 169 } 170 171 return element; 172 173 } 174 175 function extend(destination, source) { 176 177 var property; 178 179 for (property in source) { 180 destination[property] = source[property]; 181 } 182 183 return destination; 184 185 } 186 187 function proxy(fn, context) { 188 189 return function () { 190 return fn.apply(context, Array.prototype.slice.call(arguments)); 191 }; 192 193 } 194 195 function getElementsByClassName(className, root) { 196 var elements; 197 198 if ($) { 199 elements = $(root).find("." + className); 200 } else { 201 elements = Array.prototype.slice.call(root.getElementsByClassName(className)); 202 } 203 204 return elements; 205 } 206 207 function animate(element, propery, to, speed, callback) { 208 var propertyObj = {}; 209 210 propertyObj[propery] = to; 211 212 if ($) { 213 $(element).animate(propertyObj, speed, callback); 214 } else { 215 setStyles(element, propertyObj); 216 } 217 218 } 219 220 /**
221 * Returns an object containing the co-ordinates for the event, normalising for touch / non-touch. 222 * @param {Object} event 223 * @returns {Object} 224 */ 225 function getCoords(event) { 226 // touch move and touch end have different touch data 227 var touches = event.touches, 228 data = touches && touches.length ? touches : event.changedTouches; 229 230 return { 231 x: isTouch ? data[0].pageX : event.pageX, 232 y: isTouch ? data[0].pageY : event.pageY 233 }; 234 } 235 236 function Dragend(container, settings) { 237 var defaultSettingsCopy = extend({}, defaultSettings); 238 239 this.settings = extend(defaultSettingsCopy, settings); 240 this.container = container; 241 this.pageContainer = doc.createElement("div"); 242 this.scrollBorder = { x: 0, y: 0 }; 243 this.page = 0; 244 this.preventScroll = false; 245 this.pageCssProperties = { 246 margin: 0 247 }; 248 249 // bind events 250 this._onStart = proxy(this._onStart, this); 251 this._onMove = proxy(this._onMove, this); 252 this._onEnd = proxy(this._onEnd, this); 253 this._onKeydown = proxy(this._onKeydown, this); 254 this._sizePages = proxy(this._sizePages, this); 255 this._afterScrollTransform = proxy(this._afterScrollTransform, this); 256 257 this.pageContainer.innerHTML = container.cloneNode(true).innerHTML; 258 container.innerHTML = ""; 259 container.appendChild(this.pageContainer); 260 261 this._scroll = supportTransform ? this._scrollWithTransform : this._scrollWithoutTransform; 262 this._animateScroll = supportTransform ? this._animateScrollWithTransform : this._animateScrollWithoutTransform; 263 264 // Initialisation 265 266 setStyles(container, containerStyles); 267 268 // Give the DOM some time to update ... 269 setTimeout(proxy(function () { 270 this.updateInstance(settings); 271 if (!this.settings.preventDrag) { 272 this._observe(); 273 } 274 this.settings.afterInitialize.call(this); 275 }, this), 10); 276 277 } 278 279 function addEventListener(container, event, callback) { 280 if ($) { 281 $(container).on(event, callback); 282 } else { 283 container.addEventListener(event, callback, false); 284 } 285 } 286 287 function removeEventListener(container, event, callback) { 288 if ($) { 289 $(container).off(event, callback); 290 } else { 291 container.removeEventListener(event, callback, false); 292 } 293 } 294 295 extend(Dragend.prototype, { 296 297 // Private functions 298 // ================= 299 300 // ### Overscroll lookup table 301 // 302 // Checks if its the last or first page to slow down the scrolling if so 303 // 304 // Takes: 305 // Drag event 306 307 _checkOverscroll: function (direction, x, y) { 308 var coordinates = { 309 x: x, 310 y: y, 311 overscroll: true 312 }; 313 314 switch (direction) { 315 316 case "right": 317 if (!this.scrollBorder.x) { 318 coordinates.x = Math.round((x - this.scrollBorder.x) / 5); 319 return coordinates; 320 } 321 break; 322 323 case "left": 324 if ((this.pagesCount - 1) * this.pageDimentions.width <= this.scrollBorder.x) { 325 coordinates.x = Math.round(- ((Math.ceil(this.pagesCount) - 1) * (this.pageDimentions.width + this.settings.borderBetweenPages)) + x / 5); 326 return coordinates; 327 } 328 break; 329 330 case "down": 331 if (!this.scrollBorder.y) { 332 coordinates.y = Math.round((y - this.scrollBorder.y) / 5); 333 return coordinates; 334 } 335 break; 336 337 case "up": 338 if ((this.pagesCount - 1) * this.pageDimentions.height <= this.scrollBorder.y) { 339 coordinates.y = Math.round(- ((Math.ceil(this.pagesCount) - 1) * (this.pageDimentions.height + this.settings.borderBetweenPages)) + y / 5); 340 return coordinates; 341 } 342 break; 343 } 344 345 return { 346 x: x - this.scrollBorder.x, 347 y: y - this.scrollBorder.y, 348 overscroll: false 349 }; 350 }, 351 352 // Observe 353 // 354 // Sets the observers for drag, resize and key events 355 356 _observe: function () { 357 358 addEventListener(this.container, startEvent, this._onStart); 359 360 if (this.settings.keyboardNavigation) { 361 addEventListener(doc.body, "keydown", this._onKeydown); 362 } 363 364 addEventListener(win, "resize", this._sizePages); 365 366 }, 367 368 369 _onStart: function (event) { 370 371 event = event.originalEvent || event; 372 373 if (this.settings.stopPropagation) { 374 event.stopPropagation(); 375 } 376 377 addEventListener(doc.body, moveEvent, this._onMove); 378 addEventListener(doc.body, endEvent, this._onEnd); 379 380 this.startCoords = getCoords(event); 381 382 this.settings.onDragStart.call(this, event); 383 384 }, 385 386 _onMove: function (event) { 387 388 event = event.originalEvent || event; 389 390 // ensure swiping with one touch and not pinching 391 if (event.touches && event.touches.length > 1 || event.scale && event.scale !== 1) return; 392 393 event.preventDefault(); 394 if (this.settings.stopPropagation) { 395 event.stopPropagation(); 396 } 397 398 var parsedEvent = this._parseEvent(event), 399 coordinates = this._checkOverscroll(parsedEvent.direction, - parsedEvent.distanceX, - parsedEvent.distanceY); 400 401 this.settings.onDrag.call(this, this.activeElement, parsedEvent, coordinates.overscroll, event); 402 403 if (!this.preventScroll) { 404 this._scroll(coordinates); 405 } 406 }, 407 408 _onEnd: function (event) { 409 410 event = event.originalEvent || event; 411 412 if (this.settings.stopPropagation) { 413 event.stopPropagation(); 414 } 415 416 var parsedEvent = this._parseEvent(event); 417 418 this.startCoords = { x: 0, y: 0 }; 419 420 if (Math.abs(parsedEvent.distanceX) > this.settings.minDragDistance || Math.abs(parsedEvent.distanceY) > this.settings.minDragDistance) { 421 this.swipe(parsedEvent.direction); 422 } else if (parsedEvent.distanceX > 0 || parsedEvent.distanceX > 0) { 423 this._scrollToPage(); 424 } 425 426 this.settings.onDragEnd.call(this, this.container, this.activeElement, this.page, event); 427 428 removeEventListener(doc.body, moveEvent, this._onMove); 429 removeEventListener(doc.body, endEvent, this._onEnd); 430 431 }, 432 433 _parseEvent: function (event) { 434 var coords = getCoords(event), 435 x = this.startCoords.x - coords.x, 436 y = this.startCoords.y - coords.y; 437 438 return this._addDistanceValues(x, y); 439 }, 440 441 _addDistanceValues: function (x, y) { 442 var eventData = { 443 distanceX: 0, 444 distanceY: 0 445 }; 446 447 if (this.settings.direction === "horizontal") { 448 eventData.distanceX = x; 449 eventData.direction = x > 0 ? "left" : "right"; 450 } else { 451 eventData.distanceY = y; 452 eventData.direction = y > 0 ? "up" : "down"; 453 } 454 455 return eventData; 456 }, 457 458 _onKeydown: function (event) { 459 var direction = keycodes[event.keyCode]; 460 461 if (direction) { 462 this._scrollToPage(direction); 463 } 464 }, 465 466 _setHorizontalContainerCssValues: function () { 467 extend(this.pageCssProperties, { 468 "cssFloat": "left", 469 "overflowY": "auto", 470 "overflowX": "hidden", 471 "padding": 0, 472 "display": "block" 473 }); 474 475 setStyles(this.pageContainer, { 476 "overflow": "hidden", 477 "width": (this.pageDimentions.width + this.settings.borderBetweenPages) * this.pagesCount, 478 "boxSizing": "content-box", 479 "-webkit-backface-visibility": "hidden", 480 "-webkit-perspective": 1000, 481 "margin": 0, 482 "padding": 0 483 }); 484 }, 485 486 _setVerticalContainerCssValues: function () { 487 extend(this.pageCssProperties, { 488 "overflow": "hidden", 489 "padding": 0, 490 "display": "block" 491 }); 492 493 setStyles(this.pageContainer, { 494 "padding-bottom": this.settings.scribe, 495 "boxSizing": "content-box", 496 "-webkit-backface-visibility": "hidden", 497 "-webkit-perspective": 1000, 498 "margin": 0 499 }); 500 }, 501 502 setContainerCssValues: function () { 503 if (this.settings.direction === "horizontal") { 504 this._setHorizontalContainerCssValues();
505 } else { 506 this._setVerticalContainerCssValues(); 507 } 508 }, 509 510 // ### Calculate page dimentions 511 // 512 // Updates the page dimentions values 513 514 _setPageDimentions: function () { 515 var width = this.container.offsetWidth, 516 height = this.container.offsetHeight; 517 518 if (this.settings.direction === "horizontal") { 519 width = width - parseInt(this.settings.scribe, 10); 520 } else { 521 height = height - parseInt(this.settings.scribe, 10); 522 } 523 524 this.pageDimentions = { 525 width: width, 526 height: height 527 }; 528 529 }, 530 531 // ### Size pages 532 533 _sizePages: function () { 534 535 var pagesCount = this.pages.length; 536 537 this._setPageDimentions(); 538 539 this.setContainerCssValues(); 540 541 if (this.settings.direction === "horizontal") { 542 extend(this.pageCssProperties, { 543 height: this.pageDimentions.height, 544 width: this.pageDimentions.width / this.settings.itemsInPage 545 }); 546 } else { 547 extend(this.pageCssProperties, { 548 height: this.pageDimentions.height / this.settings.itemsInPage, 549 width: this.pageDimentions.width 550 }); 551 } 552 553 for (var i = 0; i < pagesCount; i++) { 554 setStyles(this.pages[i], this.pageCssProperties); 555 } 556 557 this._jumpToPage("page", this.page); 558 559 }, 560 561 // ### Callculate new page 562 // 563 // Update global values for specific swipe action 564 // 565 // Takes direction and, if specific page is used the pagenumber 566 567 _calcNewPage: function (direction, pageNumber) { 568 569 var borderBetweenPages = this.settings.borderBetweenPages, 570 height = this.pageDimentions.height, 571 width = this.pageDimentions.width, 572 page = this.page; 573 574 switch (direction) { 575 case "up": 576 if (page < this.pagesCount - 1) { 577 this.scrollBorder.y = this.scrollBorder.y + height + borderBetweenPages; 578 this.page++; 579 } 580 break; 581 582 case "down": 583 if (page > 0) { 584 this.scrollBorder.y = this.scrollBorder.y - height - borderBetweenPages; 585 this.page--; 586 } 587 break; 588 589 case "left": 590 if (page < this.pagesCount - 1) { 591 this.scrollBorder.x = this.scrollBorder.x + width + borderBetweenPages; 592 this.page++; 593 } 594 break; 595 596 case "right": 597 if (page > 0) { 598 this.scrollBorder.x = this.scrollBorder.x - width - borderBetweenPages; 599 this.page--; 600 } 601 break; 602 603 case "page": 604 switch (this.settings.direction) { 605 case "horizontal": 606 this.scrollBorder.x = (width + borderBetweenPages) * pageNumber; 607 break; 608 609 case "vertical": 610 this.scrollBorder.y = (height + borderBetweenPages) * pageNumber; 611 break; 612 } 613 this.page = pageNumber; 614 break; 615 616 default: 617 this.scrollBorder.y = 0; 618 this.scrollBorder.x = 0; 619 this.page = 0; 620 break; 621 } 622 }, 623 624 // ### On swipe end 625 // 626 // Function called after the scroll animation ended 627 628 _onSwipeEnd: function () { 629 this.preventScroll = false; 630 631 this.activeElement = this.pages[this.page * this.settings.itemsInPage]; 632 633 // Call onSwipeEnd callback function 634 this.settings.onSwipeEnd.call(this, this.container, this.activeElement, this.page); 635 }, 636 637 // Jump to page 638 // 639 // Jumps without a animantion to specific page. The page number is only 640 // necessary for the specific page direction 641 // 642 // Takes: 643 // Direction and pagenumber 644 645 _jumpToPage: function (options, pageNumber) { 646 647 if (options) { 648 this._calcNewPage(options, pageNumber); 649 } 650 651 this._scroll({ 652 x: - this.scrollBorder.x, 653 y: - this.scrollBorder.y 654 }); 655 }, 656 657 // Scroll to page 658 // 659 // Scrolls with a animantion to specific page. The page number is only necessary 660 // for the specific page direction 661 // 662 // Takes: 663 // Direction and pagenumber 664 665 _scrollToPage: function (options, pageNumber) { 666 this.preventScroll = true; 667 668 if (options) this._calcNewPage(options, pageNumber); 669 670 this._animateScroll(); 671 }, 672 673 // ### Scroll translate 674 // 675 // Animation when translate is supported 676 // 677 // Takes: 678 // x and y values to go with 679 680 _scrollWithTransform: function (coordinates) { 681 var style = this.settings.direction === "horizontal" ? "translateX("
681+ coordinates.x + "px)" : "translateY(" + coordinates.y + "px)"; 682 683 setStyles(this.pageContainer, { 684 "-webkit-transform": style, 685 "-moz-transform": style, 686 "-ms-transform": style, 687 "-o-transform": style, 688 "transform": style 689 }); 690 691 }, 692 693 // ### Animated scroll with translate support 694 695 _animateScrollWithTransform: function () { 696 697 var style = "transform " + this.settings.duration + "ms ease-out", 698 container = this.container, 699 afterScrollTransform = this._afterScrollTransform; 700 701 setStyles(this.pageContainer, { 702 "-webkit-transition": "-webkit-" + style, 703 "-moz-transition": "-moz-" + style, 704 "-ms-transition": "-ms-" + style, 705 "-o-transition": "-o-" + style, 706 "transition": style 707 }); 708 709 this._scroll({ 710 x: - this.scrollBorder.x, 711 y: - this.scrollBorder.y 712 }); 713 714 addEventListener(this.container, "webkitTransitionEnd", afterScrollTransform); 715 addEventListener(this.container, "oTransitionEnd", afterScrollTransform); 716 addEventListener(this.container, "transitionend", afterScrollTransform); 717 addEventListener(this.container, "transitionEnd", afterScrollTransform); 718 719 }, 720 721 _afterScrollTransform: function () { 722 723 var container = this.container, 724 afterScrollTransform = this._afterScrollTransform; 725 726 this._onSwipeEnd(); 727 728 removeEventListener(container, "webkitTransitionEnd", afterScrollTransform); 729 removeEventListener(container, "oTransitionEnd", afterScrollTransform); 730 removeEventListener(container, "transitionend", afterScrollTransform); 731 removeEventListener(container, "transitionEnd", afterScrollTransform); 732 733 setStyles(this.pageContainer, { 734 "-webkit-transition": "", 735 "-moz-transition": "", 736 "-ms-transition": "", 737 "-o-transition": "", 738 "transition": "" 739 }); 740 741 }, 742 743 // ### Scroll fallback 744 // 745 // Animation lookup table when translate isn't supported 746 // 747 // Takes: 748 // x and y values to go with 749 750 _scrollWithoutTransform: function (coordinates) { 751 var styles = this.settings.direction === "horizontal" ? { "marginLeft": coordinates.x } : { "marginTop": coordinates.y }; 752 753 setStyles(this.pageContainer, styles); 754 }, 755 756 // ### Animated scroll without translate support 757 758 _animateScrollWithoutTransform: function () { 759 var property = this.settings.direction === "horizontal" ? "marginLeft" : "marginTop", 760 value = this.settings.direction === "horizontal" ? - this.scrollBorder.x : - this.scrollBorder.y; 761 762 animate(this.pageContainer, property, value, this.settings.duration, proxy(this._onSwipeEnd, this)); 763 764 }, 765 766 // Public functions 767 // ================ 768 769 swipe: function (direction) { 770 // Call onSwipeStart callback function 771 this.settings.onSwipeStart.call(this, this.container, this.activeElement, this.page); 772 this._scrollToPage(direction); 773 }, 774 775 updateInstance: function (settings) { 776 777 settings = settings || {}; 778 779 if (typeof settings === "object") extend(this.settings, settings); 780 781 this.pages = getElementsByClassName(this.settings.pageClass, this.pageContainer); 782 783 if (this.pages.length) { 784 this.pagesCount = this.pages.length / this.settings.itemsInPage; 785 } else { 786 throw new Error(errors.pages); 787 } 788 789 this.activeElement = this.pages[this.page * this.settings.itemsInPage]; 790 this._sizePages(); 791 792 if (this.settings.jumpToPage) {
793 this.jumpToPage(settings.jumpToPage); 794 delete this.settings.jumpToPage; 795 } 796 797 if (this.settings.scrollToPage) { 798 this.scrollToPage(this.settings.scrollToPage); 799 delete this.settings.scrollToPage; 800 } 801 802 }, 803 804 destroy: function () { 805 806 var container = this.container; 807 808 removeEventListener(container, startEvent); 809 removeEventListener(container, moveEvent); 810 removeEventListener(container, endEvent); 811 removeEventListener(doc.body, "keydown", this._onKeydown); 812 removeEventListener(win, "resize", this._sizePages); 813 814 container.removeAttribute("style"); 815 816 for (var i = 0; i < this.pages.length; i++) { 817 this.pages[i].removeAttribute("style"); 818 } 819 820 container.innerHTML = this.pageContainer.innerHTML; 821 822 }, 823 824 scrollToPage: function (page) { 825 this._scrollToPage("page", page - 1); 826 }, 827 828 jumpToPage: function (page) { 829 this._jumpToPage("page", page - 1); 830 } 831 832 }); 833 834 if ($) { 835 836 // Register jQuery plugin 837 $.fn.dragend = function (settings) { 838 839 settings = settings || {}; 840 841 this.each(function () { 842 var instance = $(this).data("dragend"); 843 844 // check if instance already created 845 if (instance) { 846 instance.updateInstance(settings); 847 } else { 848 instance = new Dragend(this, settings); 849 $(this).data("dragend", instance); 850 } 851 852 // check if should trigger swipe 853 if (typeof settings === "string") instance.swipe(settings); 854 855 }); 856 857 // jQuery functions should always return the instance 858 return this; 859 }; 860 861 } 862 863 return Dragend; 864 865 } 866 867 if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { 868 define(function () { 869 return init(win.jQuery || win.Zepto); 870 }); 871 } else { 872 win.Dragend = init(win.jQuery || win.Zepto); 873 } 874 875})(window);
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.