Advertisement
damienoneill2001

snap.js

Jun 18th, 2015
267
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*!
  2. * Snap.js
  3. *
  4. * Copyright 2013, Jacob Kelley – http://jakiestfu.com/
  5. * Released under the MIT Licence
  6. * http://opensource.org/licenses/MIT
  7. *
  8. * Github: https://github.com/jakiestfu/Snap.js/
  9. * Version: 1.9.3
  10. */
  11. /*jslint browser: true*/
  12. /*global define, module, ender*/
  13. (function(win, doc) {
  14. ‘use strict';
  15. var Snap = Snap || function(userOpts) {
  16. var settings = {
  17. element: null,
  18. dragger: null,
  19. disable: ‘none’,
  20. addBodyClasses: true,
  21. hyperextensible: true,
  22. resistance: 0.5,
  23. flickThreshold: 50,
  24. transitionSpeed: 0.3,
  25. easing: ‘ease’,
  26. maxPosition: 266,
  27. minPosition: -266,
  28. tapToClose: true,
  29. touchToDrag: true,
  30. slideIntent: 40, // degrees
  31. minDragDistance: 5
  32. },
  33. cache = {
  34. simpleStates: {
  35. opening: null,
  36. towards: null,
  37. hyperExtending: null,
  38. halfway: null,
  39. flick: null,
  40. translation: {
  41. absolute: 0,
  42. relative: 0,
  43. sinceDirectionChange: 0,
  44. percentage: 0
  45. }
  46. }
  47. },
  48. eventList = {},
  49. utils = {
  50. hasTouch: (‘ontouchstart’ in doc.documentElement || win.navigator.msPointerEnabled),
  51. eventType: function(action) {
  52. var eventTypes = {
  53. down: (utils.hasTouch ? ‘touchstart’ : ‘mousedown’),
  54. move: (utils.hasTouch ? ‘touchmove’ : ‘mousemove’),
  55. up: (utils.hasTouch ? ‘touchend’ : ‘mouseup’),
  56. out: (utils.hasTouch ? ‘touchcancel’ : ‘mouseout’)
  57. };
  58. return eventTypes[action];
  59. },
  60. page: function(t, e){
  61. return (utils.hasTouch && e.touches.length && e.touches[0]) ? e.touches[0][‘page’+t] : e[‘page’+t];
  62. },
  63. klass: {
  64. has: function(el, name){
  65. return (el.className).indexOf(name) !== -1;
  66. },
  67. add: function(el, name){
  68. if(!utils.klass.has(el, name) && settings.addBodyClasses){
  69. el.className += ” “+name;
  70. }
  71. },
  72. remove: function(el, name){
  73. if(settings.addBodyClasses){
  74. el.className = (el.className).replace(name, “”).replace(/^\s+|\s+$/g, ”);
  75. }
  76. }
  77. },
  78. dispatchEvent: function(type) {
  79. if (typeof eventList[type] === ‘function’) {
  80. return eventList[type].call();
  81. }
  82. },
  83. vendor: function(){
  84. var tmp = doc.createElement(“div”),
  85. prefixes = ‘webkit Moz O ms’.split(‘ ‘),
  86. i;
  87. for (i in prefixes) {
  88. if (typeof tmp.style[prefixes[i] + ‘Transition’] !== ‘undefined’) {
  89. return prefixes[i];
  90. }
  91. }
  92. },
  93. transitionCallback: function(){
  94. return (cache.vendor===’Moz’ || cache.vendor===’ms’) ? ‘transitionend’ : cache.vendor+’TransitionEnd';
  95. },
  96. canTransform: function(){
  97. return typeof settings.element.style[cache.vendor+’Transform’] !==undefined';
  98. },
  99. deepExtend: function(destination, source) {
  100. var property;
  101. for (property in source) {
  102. if (source[property] && source[property].constructor && source[property].constructor === Object) {
  103. destination[property] = destination[property] || {};
  104. utils.deepExtend(destination[property], source[property]);
  105. } else {
  106. destination[property] = source[property];
  107. }
  108. }
  109. return destination;
  110. },
  111. angleOfDrag: function(x, y) {
  112. var degrees, theta;
  113. // Calc Theta
  114. theta = Math.atan2(-(cache.startDragY – y), (cache.startDragX – x));
  115. if (theta < 0) {
  116. theta += 2 * Math.PI;
  117. }
  118. // Calc Degrees
  119. degrees = Math.floor(theta * (180 / Math.PI) – 180);
  120. if (degrees < 0 && degrees > -180) {
  121. degrees = 360 – Math.abs(degrees);
  122. }
  123. return Math.abs(degrees);
  124. },
  125. events: {
  126. addEvent: function addEvent(element, eventName, func) {
  127. if (element.addEventListener) {
  128. return element.addEventListener(eventName, func, false);
  129. } else if (element.attachEvent) {
  130. return element.attachEvent(“on” + eventName, func);
  131. }
  132. },
  133. removeEvent: function addEvent(element, eventName, func) {
  134. if (element.addEventListener) {
  135. return element.removeEventListener(eventName, func, false);
  136. } else if (element.attachEvent) {
  137. return element.detachEvent(“on” + eventName, func);
  138. }
  139. },
  140. prevent: function(e) {
  141. if (e.preventDefault) {
  142. e.preventDefault();
  143. } else {
  144. e.returnValue = false;
  145. }
  146. }
  147. },
  148. parentUntil: function(el, attr) {
  149. var isStr = typeof attr === ‘string';
  150. while (el.parentNode) {
  151. if (isStr && el.getAttribute && el.getAttribute(attr)){
  152. return el;
  153. } else if(!isStr && el === attr){
  154. return el;
  155. }
  156. el = el.parentNode;
  157. }
  158. return null;
  159. }
  160. },
  161. action = {
  162. translate: {
  163. get: {
  164. matrix: function(index) {
  165.  
  166. if( !utils.canTransform() ){
  167. return parseInt(settings.element.style.left, 10);
  168. } else {
  169. var matrix = win.getComputedStyle(settings.element)[cache.vendor+’Transform’].match(/\((.*)\)/),
  170. ieOffset = 8;
  171. if (matrix) {
  172. matrix = matrix[1].split(,);
  173. if(matrix.length===16){
  174. index+=ieOffset;
  175. }
  176. return parseInt(matrix[index], 10);
  177. }
  178. return 0;
  179. }
  180. }
  181. },
  182. easeCallback: function(){
  183. settings.element.style[cache.vendor+’Transition’] =;
  184. cache.translation = action.translate.get.matrix(4);
  185. cache.easing = false;
  186. clearInterval(cache.animatingInterval);
  187.  
  188. if(cache.easingTo===0){
  189. utils.klass.remove(doc.body, ‘snapjs-left’);
  190. utils.klass.remove(doc.body, ‘snapjs-right’);
  191. }
  192.  
  193. utils.dispatchEvent(‘animated’);
  194. utils.events.removeEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback);
  195. },
  196. easeTo: function(n) {
  197.  
  198. if( !utils.canTransform() ){
  199. cache.translation = n;
  200. action.translate.x(n);
  201. } else {
  202. cache.easing = true;
  203. cache.easingTo = n;
  204.  
  205. settings.element.style[cache.vendor+’Transition’] = ‘all ‘ + settings.transitionSpeed + ‘s ‘ + settings.easing;
  206.  
  207. cache.animatingInterval = setInterval(function() {
  208. utils.dispatchEvent(‘animating’);
  209. }, 1);
  210.  
  211. utils.events.addEvent(settings.element, utils.transitionCallback(), action.translate.easeCallback);
  212. action.translate.x(n);
  213. }
  214. if(n===0){
  215. settings.element.style[cache.vendor+’Transform’] =;
  216. }
  217. },
  218. x: function(n) {
  219. if( (settings.disable===’right’ && n>0) ||
  220. (settings.disable===’left’ && n<0)
  221. ){ return; }
  222.  
  223. if( !settings.hyperextensible ){
  224. if( n===settings.maxPosition || n>settings.maxPosition ){
  225. n=settings.maxPosition;
  226. } else if( n===settings.minPosition || n<settings.minPosition ){
  227. n=settings.minPosition;
  228. }
  229. }
  230.  
  231. n = parseInt(n, 10);
  232. if(isNaN(n)){
  233. n = 0;
  234. }
  235.  
  236. if( utils.canTransform() ){
  237. var theTranslate = ‘translate3d(+ n + ‘px, 0,0)';
  238. settings.element.style[cache.vendor+’Transform’] = theTranslate;
  239. } else {
  240. settings.element.style.width = (win.innerWidth || doc.documentElement.clientWidth)+’px';
  241.  
  242. settings.element.style.right = n+’px';
  243. settings.element.style.left = ”;
  244. }
  245. }
  246. },
  247. drag: {
  248. listen: function() {
  249. cache.translation = 0;
  250. cache.easing = false;
  251. utils.events.addEvent(settings.element, utils.eventType(‘down’), action.drag.startDrag);
  252. utils.events.addEvent(settings.element, utils.eventType(‘move’), action.drag.dragging);
  253. utils.events.addEvent(settings.element, utils.eventType(‘up’), action.drag.endDrag);
  254. },
  255. stopListening: function() {
  256. utils.events.removeEvent(settings.element, utils.eventType(‘down’), action.drag.startDrag);
  257. utils.events.removeEvent(settings.element, utils.eventType(‘move’), action.drag.dragging);
  258. utils.events.removeEvent(settings.element, utils.eventType(‘up’), action.drag.endDrag);
  259. },
  260. startDrag: function(e) {
  261. // No drag on ignored elements
  262. var target = e.target ? e.target : e.srcElement,
  263. ignoreParent = utils.parentUntil(target, ‘data-snap-ignore’);
  264.  
  265. if (ignoreParent) {
  266. utils.dispatchEvent(‘ignore’);
  267. return;
  268. }
  269.  
  270. if(settings.dragger){
  271. var dragParent = utils.parentUntil(target, settings.dragger);
  272.  
  273. // Only use dragger if we’re in a closed state
  274. if( !dragParent &&
  275. (cache.translation !== settings.minPosition &&
  276. cache.translation !== settings.maxPosition
  277. )){
  278. return;
  279. }
  280. }
  281.  
  282. utils.dispatchEvent(‘start’);
  283. settings.element.style[cache.vendor+’Transition’] = ”;
  284. cache.isDragging = true;
  285. cache.hasIntent = null;
  286. cache.intentChecked = false;
  287. cache.startDragX = utils.page(‘X’, e);
  288. cache.startDragY = utils.page(‘Y’, e);
  289. cache.dragWatchers = {
  290. current: 0,
  291. last: 0,
  292. hold: 0,
  293. state: ”
  294. };
  295. cache.simpleStates = {
  296. opening: null,
  297. towards: null,
  298. hyperExtending: null,
  299. halfway: null,
  300. flick: null,
  301. translation: {
  302. absolute: 0,
  303. relative: 0,
  304. sinceDirectionChange: 0,
  305. percentage: 0
  306. }
  307. };
  308. },
  309. dragging: function(e) {
  310. if (cache.isDragging && settings.touchToDrag) {
  311.  
  312. var thePageX = utils.page(‘X’, e),
  313. thePageY = utils.page(‘Y’, e),
  314. translated = cache.translation,
  315. absoluteTranslation = action.translate.get.matrix(4),
  316. whileDragX = thePageX – cache.startDragX,
  317. openingLeft = absoluteTranslation > 0,
  318. translateTo = whileDragX,
  319. diff;
  320.  
  321. // Shown no intent already
  322. if((cache.intentChecked && !cache.hasIntent)){
  323. return;
  324. }
  325.  
  326. if(settings.addBodyClasses){
  327. if((absoluteTranslation)>0){
  328. utils.klass.add(doc.body, ‘snapjs-right’);
  329. utils.klass.remove(doc.body, ‘snapjs-left’);
  330. } else if((absoluteTranslation)<0){
  331. utils.klass.add(doc.body, ‘snapjs-left’);
  332. utils.klass.remove(doc.body, ‘snapjs-right’);
  333. }
  334. }
  335.  
  336. if (cache.hasIntent === false || cache.hasIntent === null) {
  337. var deg = utils.angleOfDrag(thePageX, thePageY),
  338. inRightRange = (deg >= 0 && deg <= settings.slideIntent) || (deg <= 360 && deg > (360 – settings.slideIntent)),
  339. inLeftRange = (deg >= 180 && deg <= (180 + settings.slideIntent)) || (deg <= 180 && deg >= (180 – settings.slideIntent));
  340. if (!inLeftRange && !inRightRange) {
  341. cache.hasIntent = false;
  342. } else {
  343. cache.hasIntent = true;
  344. }
  345. cache.intentChecked = true;
  346. }
  347.  
  348. if (
  349. (settings.minDragDistance>=Math.abs(thePageX-cache.startDragX)) || // Has user met minimum drag distance?
  350. (cache.hasIntent === false)
  351. ) {
  352. return;
  353. }
  354.  
  355. utils.events.prevent(e);
  356. utils.dispatchEvent(‘drag’);
  357.  
  358. cache.dragWatchers.current = thePageX;
  359. // Determine which direction we are going
  360. if (cache.dragWatchers.last > thePageX) {
  361. if (cache.dragWatchers.state !== ‘right’) {
  362. cache.dragWatchers.state = ‘right';
  363. cache.dragWatchers.hold = thePageX;
  364. }
  365. cache.dragWatchers.last = thePageX;
  366. } else if (cache.dragWatchers.last < thePageX) {
  367. if (cache.dragWatchers.state !== ‘left’) {
  368. cache.dragWatchers.state = ‘left';
  369. cache.dragWatchers.hold = thePageX;
  370. }
  371. cache.dragWatchers.last = thePageX;
  372. }
  373. if (openingLeft) {
  374. // Pulling too far to the right
  375. if (settings.maxPosition < absoluteTranslation) {
  376. diff = (absoluteTranslation – settings.maxPosition) * settings.resistance;
  377. translateTo = whileDragX – diff;
  378. }
  379. cache.simpleStates = {
  380. opening: ‘left’,
  381. towards: cache.dragWatchers.state,
  382. hyperExtending: settings.maxPosition < absoluteTranslation,
  383. halfway: absoluteTranslation > (settings.maxPosition / 2),
  384. flick: Math.abs(cache.dragWatchers.current – cache.dragWatchers.hold) > settings.flickThreshold,
  385. translation: {
  386. absolute: absoluteTranslation,
  387. relative: whileDragX,
  388. sinceDirectionChange: (cache.dragWatchers.current – cache.dragWatchers.hold),
  389. percentage: (absoluteTranslation/settings.maxPosition)*100
  390. }
  391. };
  392. } else {
  393. // Pulling too far to the left
  394. if (settings.minPosition > absoluteTranslation) {
  395. diff = (absoluteTranslation – settings.minPosition) * settings.resistance;
  396. translateTo = whileDragX – diff;
  397. }
  398. cache.simpleStates = {
  399. opening: ‘right’,
  400. towards: cache.dragWatchers.state,
  401. hyperExtending: settings.minPosition > absoluteTranslation,
  402. halfway: absoluteTranslation < (settings.minPosition / 2),
  403. flick: Math.abs(cache.dragWatchers.current – cache.dragWatchers.hold) > settings.flickThreshold,
  404. translation: {
  405. absolute: absoluteTranslation,
  406. relative: whileDragX,
  407. sinceDirectionChange: (cache.dragWatchers.current – cache.dragWatchers.hold),
  408. percentage: (absoluteTranslation/settings.minPosition)*100
  409. }
  410. };
  411. }
  412. action.translate.x(translateTo + translated);
  413. }
  414. },
  415. endDrag: function(e) {
  416. if (cache.isDragging) {
  417. utils.dispatchEvent(‘end’);
  418. var translated = action.translate.get.matrix(4);
  419.  
  420. // Tap Close
  421. if (cache.dragWatchers.current === 0 && translated !== 0 && settings.tapToClose) {
  422. utils.dispatchEvent(‘close’);
  423. utils.events.prevent(e);
  424. action.translate.easeTo(0);
  425. cache.isDragging = false;
  426. cache.startDragX = 0;
  427. return;
  428. }
  429.  
  430. // Revealing Left
  431. if (cache.simpleStates.opening === ‘right’) {
  432. // Halfway, Flicking, or Too Far Out
  433. if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) {
  434. if (cache.simpleStates.flick && cache.simpleStates.towards === ‘right’) { // Flicking Closed
  435. action.translate.easeTo(0);
  436. } else if (
  437. (cache.simpleStates.flick && cache.simpleStates.towards === ‘left’) || // Flicking Open OR
  438. (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending
  439. ) {
  440. action.translate.easeTo(settings.maxPosition); // Open Left
  441. }
  442. } else {
  443. action.translate.easeTo(0); // Close Left
  444. }
  445. // Revealing Right
  446. } else if (cache.simpleStates.opening === ‘left’) {
  447. // Halfway, Flicking, or Too Far Out
  448. if ((cache.simpleStates.halfway || cache.simpleStates.hyperExtending || cache.simpleStates.flick)) {
  449. if (cache.simpleStates.flick && cache.simpleStates.towards === ‘left’) { // Flicking Closed
  450. action.translate.easeTo(0);
  451. } else if (
  452. (cache.simpleStates.flick && cache.simpleStates.towards === ‘right’) || // Flicking Open OR
  453. (cache.simpleStates.halfway || cache.simpleStates.hyperExtending) // At least halfway open OR hyperextending
  454. ) {
  455. action.translate.easeTo(settings.minPosition); // Open Right
  456. }
  457. } else {
  458. action.translate.easeTo(0); // Close Right
  459. }
  460. }
  461. cache.isDragging = false;
  462. cache.startDragX = utils.page(‘X’, e);
  463. }
  464. }
  465. }
  466. },
  467. init = function(opts) {
  468. if (opts.element) {
  469. utils.deepExtend(settings, opts);
  470. cache.vendor = utils.vendor();
  471. action.drag.listen();
  472. }
  473. };
  474. /*
  475. * Public
  476. */
  477. this.open = function(side) {
  478. utils.dispatchEvent(‘open’);
  479. utils.klass.remove(doc.body, ‘snapjs-expand-right’);
  480. utils.klass.remove(doc.body, ‘snapjs-expand-left’);
  481.  
  482. if (side === ‘right’) {
  483. cache.simpleStates.opening = ‘right';
  484. cache.simpleStates.towards = ‘left';
  485. utils.klass.add(doc.body, ‘snapjs-right’);
  486. utils.klass.remove(doc.body, ‘snapjs-left’);
  487. action.translate.easeTo(settings.maxPosition);
  488. } else if (side === ‘left’) {
  489. cache.simpleStates.opening = ‘left';
  490. cache.simpleStates.towards = ‘right';
  491. utils.klass.remove(doc.body, ‘snapjs-right’);
  492. utils.klass.add(doc.body, ‘snapjs-left’);
  493. action.translate.easeTo(settings.minPosition);
  494. }
  495. };
  496. this.close = function() {
  497. utils.dispatchEvent(‘close’);
  498. action.translate.easeTo(0);
  499. };
  500. this.expand = function(side){
  501. var to = win.innerWidth || doc.documentElement.clientWidth;
  502.  
  503. if(side===’right’){
  504. utils.dispatchEvent(‘expandRight’);
  505. utils.klass.add(doc.body, ‘snapjs-expand-right’);
  506. utils.klass.remove(doc.body, ‘snapjs-expand-left’);
  507. } else {
  508. utils.dispatchEvent(‘expandLeft’);
  509. utils.klass.add(doc.body, ‘snapjs-expand-left’);
  510. utils.klass.remove(doc.body, ‘snapjs-expand-right’);
  511. to *= -1;
  512. }
  513. action.translate.easeTo(to);
  514. };
  515.  
  516. this.on = function(evt, fn) {
  517. eventList[evt] = fn;
  518. return this;
  519. };
  520. this.off = function(evt) {
  521. if (eventList[evt]) {
  522. eventList[evt] = false;
  523. }
  524. };
  525.  
  526. this.enable = function() {
  527. utils.dispatchEvent(‘enable’);
  528. action.drag.listen();
  529. };
  530. this.disable = function() {
  531. utils.dispatchEvent(‘disable’);
  532. action.drag.stopListening();
  533. };
  534.  
  535. this.settings = function(opts){
  536. utils.deepExtend(settings, opts);
  537. };
  538.  
  539. this.state = function() {
  540. var state,
  541. fromLeft = action.translate.get.matrix(4);
  542. if (fromLeft === settings.maxPosition) {
  543. state = ‘right';
  544. } else if (fromLeft === settings.minPosition) {
  545. state = ‘left';
  546. } else {
  547. state = ‘closed';
  548. }
  549. return {
  550. state: state,
  551. info: cache.simpleStates
  552. };
  553. };
  554. init(userOpts);
  555. };
  556. if ((typeof module !==undefined) && module.exports) {
  557. module.exports = Snap;
  558. }
  559. if (typeof ender ===undefined) {
  560. this.Snap = Snap;
  561. }
  562. if ((typeof define ===function) && define.amd) {
  563. define(“snap”, [], function() {
  564. return Snap;
  565. });
  566. }
  567. }).call(this, window, document);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement