PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/js/jquery.ui.butter.js

https://github.com/reobwuein/butter
JavaScript | 774 lines | 2 code | 5 blank | 767 comment | 0 complexity | fcdfbc1e1706c43dd9428c2c086f5c14 MD5 | raw file
  1. /*
  2. * Butter Track Widgter jquery.ui.butter.js
  3. * Version 0.1.0
  4. *
  5. * Developed by Bocoup on behalf of the Mozilla Foundation
  6. * Copyright (c) 2011 Bocoup, LLC
  7. * Authors: Alistair McDonald
  8. * Dual licensed under the MIT and GPL licenses.
  9. * http://code.bocoup.com/license/
  10. *
  11. */
  12. (function( global, document, jQuery, _, Popcorn ) {
  13. /*var auto = 100,
  14. eResize = 101,
  15. wResize = 102,
  16. drag = 103,
  17. mouseModes = {
  18. "auto" : 100,
  19. "e-resize" : 101,
  20. "w-resize" : 102,
  21. "drag" : 103
  22. },
  23. trackCount = -1,
  24. lastMouseDown = {
  25. x: 0,
  26. y: 0
  27. },
  28. range = function(start, stop, step) {
  29. var start = start || 0,
  30. stop = stop || start || 0,
  31. step = step || 1,
  32. len = Math.ceil( (stop - start) / step) || 0 ,
  33. idx = 0,
  34. range = [];
  35. range.length = len;
  36. while ( idx < len ) {
  37. range[ idx++ ] = start;
  38. start += step;
  39. }
  40. return range;
  41. },
  42. styles = {
  43. trackEvent: {
  44. defaults: function( c, x, y, w, h ) {
  45. // `x` seems to come up as NaN occassionally
  46. //// // // console.log( c, x, y, w, h );
  47. //// // // console.log(isNaN(x));
  48. if ( isNaN(x) ) {
  49. return;
  50. }
  51. //document.body.style.cursor="e-resize";
  52. var grad = c.createLinearGradient(0,0,0,h);
  53. grad.addColorStop(0,"rgba( 255, 255, 0, 0.3 )");
  54. grad.addColorStop(1,"rgba( 255, 255, 0, 0.3 )");
  55. c.fillStyle = grad;
  56. c.fillRect(x, 1.5, w, h-1.5);
  57. c.fillStyle = "rgba(255,255,255,.125)";
  58. c.fillRect(x, 0, w, h/2);
  59. c.lineWidth=0.5;
  60. c.fillStyle="#FF0";
  61. c.fillRect(x, 3, 1, h-5);
  62. c.fillRect(x+w-1, 3, 1, h-5);
  63. },
  64. hover: function( c, x, y, w, h ) {
  65. // `x` seems to come up as NaN occassionally
  66. if ( isNaN(x) ) {
  67. return;
  68. }
  69. //document.body.style.cursor="move";
  70. c.fillStyle = "#FF0";
  71. c.fillRect(x, 1.5, w, h-1.5);
  72. var grad = c.createLinearGradient(0,0,0,h);
  73. grad.addColorStop(0,"rgba(255,255,255,.7)");
  74. grad.addColorStop(1,"rgba(0,0,0,.25)");
  75. c.fillStyle = grad;
  76. c.fillRect(x,0, w, h);
  77. c.fillStyle="#FF0";
  78. c.fillRect(x, 0, 1, h);
  79. c.fillRect(x+w-1, 0, 1, h);
  80. c.fillRect(x, h-1.5, w, 2);
  81. c.fillRect(x, 0, w, 1);
  82. },
  83. thumb: {
  84. left: {
  85. defaults: function( c, x, y, w, h ) {
  86. c.fillStyle = "#880";
  87. c.fillRect(x, 0, 8, h);
  88. c.fillStyle = "#FF0";
  89. c.fillRect(x, 0, 1, h);
  90. }
  91. },
  92. right: {
  93. defaults: function( c, x, y, w, h ) {
  94. c.fillStyle = "#880";
  95. c.fillRect(x+w-9, 0, 8, h);
  96. c.fillStyle = "#FF0";
  97. c.fillRect(x+w-1, 0, 1, h);
  98. }
  99. }
  100. }
  101. },
  102. zoomEvent: {
  103. defaults: function( c, x, y, w, h ) {
  104. //document.body.style.cursor="e-resize";
  105. var grad = c.createLinearGradient(0,0,0,h);
  106. grad.addColorStop(0,"rgba( 128, 255, 0, 0.3 )");
  107. grad.addColorStop(1,"rgba( 128, 255, 0, 0.3 )");
  108. c.fillStyle = grad;
  109. c.fillRect(x, 1.5, w, h-1.5);
  110. c.fillStyle = "rgba(255,255,255,.125)";
  111. c.fillRect(x, 0, w, h/2);
  112. c.lineWidth=0.5;
  113. c.fillStyle="#AF0";
  114. c.fillRect(x, 3, 1, h-5);
  115. c.fillRect(x+w-1, 3, 1, h-5);
  116. },
  117. hover: function( c, x, y, w, h ) {
  118. //document.body.style.cursor="move";
  119. c.fillStyle = "#AF0";
  120. c.fillRect(x, 1.5, w, h-1.5);
  121. var grad = c.createLinearGradient(0,0,0,h);
  122. grad.addColorStop(0,"rgba(128,255,0,.7)");
  123. grad.addColorStop(1,"rgba(0,0,0,.25)");
  124. c.fillStyle = grad;
  125. c.fillRect(x,0, w, h);
  126. c.fillStyle="#AF0";
  127. c.fillRect(x, 0, 1, h);
  128. c.fillRect(x+w-1, 0, 1, h);
  129. c.fillRect(x, h-1.5, w, 2);
  130. c.fillRect(x, 0, w, 1);
  131. },
  132. thumb: {
  133. left: {
  134. defaults: function( c, x, y, w, h ) {
  135. c.fillStyle = "#480";
  136. c.fillRect(x, 0, 16, h);
  137. c.fillStyle = "#AF0";
  138. c.fillRect(x, 0, 4, h);
  139. }
  140. },
  141. right: {
  142. defaults: function( c, x, y, w, h ) {
  143. c.fillStyle = "#480";
  144. c.fillRect(x+w-17, 0, 16, h);
  145. c.fillStyle = "#AF0";
  146. c.fillRect(x+w-4, 0, 4, h);
  147. }
  148. }
  149. }
  150. }
  151. };
  152. function TrackEvent( props, parent ) {
  153. //// // // console.log("TrackEvent",props, this);
  154. jQuery.extend(this, props);
  155. //// // console.log( this );
  156. this.parent = parent;
  157. this.oxl = 0;
  158. this.oxr = 0;
  159. this.xl = 0;
  160. this.xr = 0;
  161. this.hovered = false;
  162. this.draw();
  163. return this;
  164. }
  165. TrackEvent.prototype.draw = function( thumbLeft, thumbRight ) {
  166. //e.pageX
  167. //// // console.log( thumbLeft, thumbRight );
  168. var x = this.xl = this.oxl + (this.parent.width / this.parent.options.duration * this.inPoint),
  169. rw = this.parent.width / this.parent.options.duration * (this.outPoint-this.inPoint),
  170. h = this.parent.height,
  171. c = this.parent.context,
  172. type;
  173. //this.xl += this.parent.element.offset().left;
  174. x = x * 100/this.parent.zoomWindow.width-(this.parent.zoomWindow.offsetX*100);
  175. rw = rw * 100/this.parent.zoomWindow.width;
  176. this.xr = x + rw;
  177. //var mouseX = this.parent.mouseX;
  178. type = ( this.parent.options.mode === "smartZoom" ) ? "zoomEvent" : "trackEvent";
  179. if ( this.hovered ) {
  180. styles[type].hover( c, x, null, rw, h );
  181. if ( thumbLeft ) {
  182. styles[type].thumb.left.defaults( c, x, null, rw, h );
  183. }
  184. if ( thumbRight ) {
  185. styles[type].thumb.right.defaults( c, x, null, rw, h );
  186. }
  187. }else{
  188. styles[type].defaults( c, x, null, rw, h );
  189. }
  190. };
  191. jQuery.widget("butter.track", {
  192. options: {
  193. },
  194. _init: function( ) {
  195. console.log("init called");
  196. this.index = trackCount++;
  197. // Contains any trackEvent that overlaps the current view window
  198. this._inView = [];
  199. this.hovering = null;
  200. this._loadedmetadata = function( e ) {
  201. this.options.duration = e.currentTarget.duration;
  202. };
  203. this._playBar = {
  204. position: 0
  205. };
  206. function newCanvas( w, h ) {
  207. var canvas, context;
  208. canvas = document.createElement("canvas");
  209. canvas.width = w;
  210. canvas.height = h;
  211. context = canvas.getContext("2d");
  212. return context;
  213. }
  214. this.width = this.element.width();
  215. this.height = this.element.height();
  216. //// // // console.log(this.width, this.height);
  217. jQuery.extend(this, {
  218. context : newCanvas( this.width, this.height ),
  219. scrubBar : { position: 0, width: 3 },
  220. mouse : { x: 0, y:0, down: false, lastX:0, lastY:0, mode:auto },
  221. range : null,
  222. zoomWindow : { offsetX:0, width:100 }
  223. });
  224. jQuery.extend(this.options, {
  225. style: {
  226. outerBar: {
  227. lineWidth: 1,
  228. strokeStyle: "#888"
  229. },
  230. playBar: {
  231. lineWidth: 1,
  232. strokeStyle: "#f00"
  233. }
  234. }
  235. });
  236. if ( this.options.mode === "smartZoom" ) {
  237. this._inView.push(new TrackEvent({
  238. inPoint : 0,
  239. outPoint : 100,
  240. duration : 100
  241. }, this ));
  242. }
  243. this.element.append( this.context.canvas );
  244. if ( this.options.target ) {
  245. this.options.target.bind( "timeupdate.track", jQuery.proxy( this._timeupdate, this ) );
  246. this.options.target.bind( "loadedmetadata.track", jQuery.proxy( this._loadedmetadata, this ) );
  247. }
  248. this.element.bind( "mousemove.track", jQuery.proxy( this._mousemove, this ) );
  249. this.element.bind( "mousedown.track mouseup.track", jQuery.proxy( this._mouseupdown, this ) );
  250. this.element.bind( "mouseenter.track mouseleave.track", jQuery.proxy( this._hover, this ) );
  251. this._draw();
  252. return this;
  253. },
  254. _style: function( styleObj ) {
  255. var prop;
  256. for ( prop in styleObj ) {
  257. this.context[prop] = styleObj[prop];
  258. }
  259. },
  260. killTrackEvent: function ( props ) {
  261. var ret = [];
  262. _.each( this._inView, function ( track, key ) {
  263. if ( track._id !== props._id ) {
  264. ret.push(track);
  265. }
  266. });
  267. this._inView = ret;
  268. },
  269. addTrackEvent: function( props ) {
  270. return this._inView.push( new TrackEvent( props, this ) );
  271. },
  272. zoom: function( props ) {
  273. this.zoomWindow.offsetX = props.offsetX;
  274. this.zoomWindow.width = props.width;
  275. //this.options.duration = this.options.width;
  276. this._draw();
  277. },
  278. _draw: function( thumbLeft, thumbRight ) {
  279. //// // // console.log( "_draw:thumbLeft, thumbRight", thumbLeft, thumbRight );
  280. jQuery(document).trigger("drawStart.track");
  281. var c = this.context,
  282. e = c.canvas,
  283. w = e.width,
  284. h = e.height,
  285. grad = c.createLinearGradient(0,0,0,h),
  286. i = 0,
  287. len = this._inView.length,
  288. iv;
  289. // // // // console.log("_draw:len", len, this._inView );
  290. grad.addColorStop( 0,"#fff" );
  291. grad.addColorStop( 1,"#B6B6B6" );
  292. //grad.addColorStop(1,"#eee");
  293. c.fillStyle = grad;
  294. c.fillRect(0,0,w,h);
  295. c.strokeStyle = "#9D9D9D";
  296. c.lineWidth = 0;
  297. c.strokeRect(0.5,0.5,w-1,h-1);
  298. for( ; i < len; i++ ) {
  299. iv = this._inView[i];
  300. iv.draw( thumbLeft, thumbRight );
  301. }
  302. jQuery(document).trigger("drawComplete.track");
  303. },
  304. _timeupdate: function( e ) {
  305. //// // // console.log("timeupdate");
  306. this._playBar.position = e.currentTarget.currentTime;
  307. this._draw();
  308. },
  309. _mousemove: function( e ) {
  310. //// // // console.log(e);
  311. e = e.originalEvent;
  312. this.mouse.lastX = this.mouse.x;
  313. this.mouse.lastY = this.mouse.y;
  314. // // // console.log(global);
  315. var scrollX = (global.scrollX !== null && typeof global.scrollX !== "undefined") ? global.scrollX : global.pageXOffset,
  316. scrollY = (global.scrollY !== null && typeof global.scrollY !== "undefined") ? global.scrollY : global.pageYOffset,
  317. thumbLeft, thumbRight,
  318. i = 0,
  319. len = this._inView.length,
  320. iv, linkedTracks, j, diff, bounds;
  321. //// // // console.log("this.element[0].offsetLeft", this.element[0].offsetLeft);
  322. //// // // console.log("this.element[0]", this.element[0]);
  323. //// // // console.log("this.element[0]", { elem: this.element[0].parentNode.scrollLeft });
  324. //// // // console.log(e.clientX, e.clientY);
  325. //// // // console.log(this.mouse.x);
  326. // // // console.log("this.element[0]", {
  327. // p: this.element[0].parentNode
  328. // });
  329. //// // console.log( e.pageX );
  330. //this.mouse.x -= e.pageX;
  331. //this.mouse.y -= e.pageY;
  332. //// // console.log( this.mouse.x );
  333. // STABLE MOUSE OVER POSITIONS
  334. this.mouse.x = e.clientX; // - this.element[0].offsetLeft + scrollX + this.element[0].parentNode.scrollLeft;
  335. this.mouse.y = e.clientY - this.element[0].offsetTop + scrollY;
  336. bounds = this.element[0].getBoundingClientRect();
  337. // CORRECTION TO POSITION
  338. //this.mouse.x = this.mouse.x >= 0 && this.mouse.x || 1;
  339. //// // console.log( "this.element[0].getBoundingClientRect()",this.element[0].getBoundingClientRect() );
  340. //// // console.log( "this.mouse.x", this.mouse.x );
  341. //// // console.log( this.mouse.x, this.mouse.y );
  342. thumbLeft = thumbRight = false;
  343. if ( !this.mouse.down ) {
  344. // console.log( "!this.mouse.down, this.mouse.mode", this.mouse.mode );
  345. this.mouse.hovering = null;
  346. for( ; i < len; i++ ) {
  347. iv = this._inView[i];
  348. //iv.xl += this.element.offset().left;
  349. //iv.xr += this.element.offset().left;
  350. //iv.xl += bounds.left + this.element[0].parentNode.scrollLeft;
  351. //iv.xr += bounds.left + this.element[0].parentNode.scrollLeft;
  352. this.range = range( Math.floor(iv.xl) , Math.floor(iv.xr) );
  353. var mouseX = this.mouse.x - bounds.left;
  354. if ( iv.xl <= mouseX && iv.xr >= mouseX ) {
  355. //if ( this.range.indexOf( this.mouse.x ) != -1 ) {
  356. //// // console.log( "iv.xl <= this.mouse.x && iv.xr >= this.mouse.x", this.mouse.x, iv.xl , iv.xr );
  357. if ( !iv.hovered ) {
  358. iv.hovered = true;
  359. }
  360. this.mouse.hovering = iv;
  361. this.mouse.hovering.grabX = this.mouse.x - this.mouse.hovering.xl + 1;
  362. if ( mouseX >= iv.xl && mouseX <= iv.xl + 8 ) {
  363. document.body.style.cursor="w-resize";
  364. thumbLeft = true;
  365. } else if ( mouseX >= iv.xr-8 && mouseX <= iv.xr ) {
  366. document.body.style.cursor="e-resize";
  367. thumbRight = true;
  368. } else {
  369. document.body.style.cursor="move";
  370. }
  371. } else {
  372. if ( iv.hovered ) {
  373. iv.hovered = false;
  374. this.mouse.hovering = null;
  375. this._draw();
  376. }
  377. }
  378. }
  379. if ( !this.mouse.hovering ) {
  380. this.mouse.mode = auto;
  381. document.body.style.cursor="auto";
  382. return;
  383. }
  384. }
  385. //// // console.log( "continue A to: this.mouse.down", this.mouse.down );
  386. iv = this.mouse.hovering;
  387. //iv.xl += this.element.offset().left;
  388. //iv.xr += this.element.offset().left;
  389. //iv.xl += bounds.left + this.element[0].parentNode.scrollLeft;
  390. if ( this.mouse.down ) {
  391. //this.mouse.x -= bounds.left;
  392. if ( this.mouse.mode === auto && this.mouse.hovering ) {
  393. var xOffset = bounds.left;
  394. var leftSide = iv.xl + xOffset;
  395. var rightSide = iv.xr + xOffset;
  396. if ( this.mouse.x >= leftSide && this.mouse.x <= leftSide + 8 ) {
  397. this.mouse.mode = wResize;
  398. } else if ( this.mouse.x >= rightSide - 8 && this.mouse.x <= rightSide ) {
  399. this.mouse.mode = eResize;
  400. } else if ( this.mouse.x >= leftSide + 8 && this.mouse.x <= rightSide - 8 ) {
  401. //// // // console.log("this.mouse.x", this.mouse.x);
  402. //// // // console.log("iv", iv);
  403. //// // // console.log("iv.xl", iv.xl);
  404. //// // // console.log("dragset");
  405. this.mouse.mode = drag;
  406. }
  407. }
  408. thumbLeft = thumbRight = false;
  409. //this.mouse.mode = mouseModes[ document.body.style.cursor ];
  410. if ( [ eResize, wResize ].indexOf( mouseModes[ document.body.style.cursor ] ) > -1 ) {
  411. // // console.log("dragging handles");
  412. //// // // console.log(this.mouse.hovering.xl, this.mouse.hovering.xr);
  413. var cancelDrag = false;
  414. if ( this.mouse.hovering.xl + 20 > this.mouse.hovering.xr ) {
  415. this.mouse.hovering.popcornEvent.start = this.mouse.hovering.inPoint - 2 ;
  416. cancelDrag = true;
  417. }
  418. if ( this.mouse.hovering.xr - 20 < this.mouse.hovering.xl ) {
  419. this.mouse.hovering.popcornEvent.end = this.mouse.hovering.outPoint + 2 ;
  420. cancelDrag = true;
  421. }
  422. if ( this.mouse.lastX < this.mouse.hovering.xl && this.mouse.mode === 102 ) {
  423. cancelDrag = false;
  424. }
  425. if ( this.mouse.lastX > this.mouse.hovering.xr && this.mouse.mode === 101 ) {
  426. cancelDrag = false;
  427. }
  428. // // console.log( "cancelDrag", cancelDrag );
  429. if ( cancelDrag ) {
  430. this._draw(thumbLeft, thumbRight);
  431. return;
  432. }
  433. }
  434. //// // console.log( "continue B to: this.mouse.down", this.mouse.down );
  435. //// // console.log( "continue to: this.mouse.mode ", this.mouse.mode );
  436. if ( this.mouse.mode === eResize ) {
  437. // // console.log( "eResize" );
  438. thumbRight = true;
  439. document.body.style.cursor="e-resize";
  440. // // console.log( this.mouse.x );
  441. this.mouse.hovering.outPoint = this.options.duration / this.width * (this.mouse.x+4-bounds.left);
  442. if ( this.options.mode !== "smartZoom" ) {
  443. // // console.log( "!smartZoom: this.mouse.hovering.outPoint ", this.mouse.hovering.outPoint );
  444. this.mouse.hovering.popcornEvent.end = this.mouse.hovering.outPoint;
  445. } else {
  446. linkedTracks = this.options.linkedTracks;
  447. for( j in linkedTracks ) {
  448. linkedTracks[j].track( "zoom", {
  449. offsetX: this.mouse.hovering.inPoint,
  450. width: this.mouse.hovering.outPoint - this.mouse.hovering.inPoint
  451. });
  452. }
  453. }
  454. }
  455. if ( this.mouse.mode === wResize ) {
  456. // // console.log( "wResize" );
  457. thumbLeft = true;
  458. document.body.style.cursor="w-resize";
  459. this.mouse.hovering.inPoint = this.options.duration / this.width * (this.mouse.x-4-bounds.left);
  460. if ( this.options.mode !== "smartZoom" ) {
  461. this.mouse.hovering.popcornEvent.start = this.mouse.hovering.inPoint;
  462. } else {
  463. linkedTracks = this.options.linkedTracks;
  464. for( j in linkedTracks ) {
  465. linkedTracks[j].track( "zoom", {
  466. offsetX: this.mouse.hovering.inPoint,
  467. width: this.mouse.hovering.outPoint - this.mouse.hovering.inPoint
  468. });
  469. }
  470. }
  471. }
  472. if ( this.mouse.mode === drag ) {
  473. // console.log( "drag" );
  474. document.body.style.cursor="move";
  475. diff = this.mouse.hovering.outPoint - this.mouse.hovering.inPoint;
  476. var mouseX = this.mouse.x - this.mouse.hovering.grabX;
  477. this.mouse.hovering.inPoint = mouseX / this.width * this.options.duration;
  478. this.mouse.hovering.outPoint = this.mouse.hovering.inPoint + diff;
  479. if ( this.options.mode !== "smartZoom" ) {
  480. this.mouse.hovering.popcornEvent.start = this.mouse.hovering.inPoint ;
  481. this.mouse.hovering.popcornEvent.end = this.mouse.hovering.outPoint ;
  482. } else {
  483. linkedTracks = this.options.linkedTracks;
  484. for( j in linkedTracks ) {
  485. linkedTracks[j].track( "zoom", {
  486. offsetX: this.mouse.hovering.inPoint,
  487. width: this.mouse.hovering.outPoint - this.mouse.hovering.inPoint
  488. });
  489. }
  490. }
  491. }
  492. }
  493. //// // // console.log( this.mouse.mode, this.mouse.hovering, this.mouse.down );
  494. this._draw(thumbLeft, thumbRight);
  495. },
  496. _mouseupdown: function( event ) {
  497. // // // // console.log( "_mouseupdown", event.type, event );
  498. if ( event.type === "mousedown" ) {
  499. this.mouse.down = true;
  500. $.extend( lastMouseDown, { x: event.pageX, y: event.pageY });
  501. return;
  502. }
  503. if ( event.type === "mouseup" ) {
  504. //// // // console.log("mouseup");
  505. //this.mouse.mode = auto;
  506. this.mouse.mode = auto;
  507. if ( this.mouse.hovering && this.mouse.down ) {
  508. if ( this.options.mode !== "smartZoom" ) {
  509. //// // // console.log("lastMouseDown", lastMouseDown, { x: event.pageX, y: event.pageY });
  510. // If mouse hasnt moved, fire edit event (will open edit dialog)
  511. if ( lastMouseDown.x === event.pageX ) {
  512. this.mouse.hovering.editEvent( event );
  513. } else {
  514. // Placeholder for future ondrag
  515. //this.mouse.hovering.editEvent( event );
  516. }
  517. }
  518. this.mouse.hovering = null;
  519. }
  520. this.mouse.down = false;
  521. this._draw();
  522. }
  523. },
  524. _hover: function( event ) {
  525. // // // // console.log( "_hover", event.type, event );
  526. if ( event.type === "mouseenter" ) {
  527. //// // // console.log(event, this);
  528. this._draw();
  529. return;
  530. }
  531. if ( event.type === "mouseleave" ) {
  532. if ( this.mouse.hovering ) {
  533. // // // // console.log( "_hover:mouseleave", "this.mouse.hovering", this.mouse.hovering );
  534. this.mouse.hovering.hovered = false;
  535. }
  536. this.mouse.hovering = null;
  537. this.mouse.mode = auto;
  538. document.body.style.cursor="auto";
  539. this._draw();
  540. }
  541. }
  542. });*/
  543. }( this, this.document, this.jQuery, this._, this.Popcorn ));