YUI 3.x Home -

YUI Library Examples: Drag & Drop: Portal Style Example

Drag & Drop: Portal Style Example

Portal style example using Drag & Drop Event Bubbling and Animation.

Full HTML Source

  1. <body>
  2. <div id="doc3" class="yui-t2">
  3. <div id="hd"><h1>YUI 3.0 Portal Example</h1></div>
  4. <div id="bd">
  5. <div id="yui-main">
  6. <div class="yui-b">
  7. <div class="yui-g">
  8. <div id="play">
  9. <ul class="list" id="list1"></ul>
  10. <ul class="list" id="list2"></ul>
  11. <ul class="list" id="list3"></ul>
  12. </div>
  13. </div>
  14. </div>
  15. </div>
  16. <div class="yui-b">
  17. <div id="feeds">
  18. <p>Drag a news feed into one of the columns.</p>
  19. <ul></ul>
  20. </div>
  21. </div>
  22. </div>
  23. <div id="ft">Built with <a href="http://developer.yahoo.com/yui/3/">YUI 3.0</a>: <a href="http://developer.yahoo.com/yui/3/yui">Core</a>, Loader, Get, <a href="http://developer.yahoo.com/yui/3/cookie">Cookie</a>, <a href="http://developer.yahoo.com/yui/3/animation">Animation</a>, <a href="http://developer.yahoo.com/yui/3/io">IO</a>, <a href="http://developer.yahoo.com/yui/3/json">JSON</a> &amp; <a href="http://developer.yahoo.com/yui/3/dd">DD</a>.</div>
  24. </div>
  25. <script src="assets/portal.js"></script>
  26. </body>
<body>
<div id="doc3" class="yui-t2">
    <div id="hd"><h1>YUI 3.0 Portal Example</h1></div>
    <div id="bd">
        <div id="yui-main">
	        <div class="yui-b">
                <div class="yui-g">
                    <div id="play">
                        <ul class="list" id="list1"></ul>
                        <ul class="list" id="list2"></ul>
                        <ul class="list" id="list3"></ul>
                    </div>
	            </div>
            </div>
	    </div>
	    <div class="yui-b">
            <div id="feeds">
                <p>Drag a news feed into one of the columns.</p>
                <ul></ul>
            </div>
        </div>
    </div>
    <div id="ft">Built with <a href="http://developer.yahoo.com/yui/3/">YUI 3.0</a>: <a href="http://developer.yahoo.com/yui/3/yui">Core</a>, Loader, Get, <a href="http://developer.yahoo.com/yui/3/cookie">Cookie</a>, <a href="http://developer.yahoo.com/yui/3/animation">Animation</a>, <a href="http://developer.yahoo.com/yui/3/io">IO</a>, <a href="http://developer.yahoo.com/yui/3/json">JSON</a> &amp; <a href="http://developer.yahoo.com/yui/3/dd">DD</a>.</div>
</div>
<script src="assets/portal.js"></script>
</body>

Full Javascript Source

  1. //Use loader to grab the modules needed
  2. YUI(yuiConfig).use('dd', 'anim', 'io', 'cookie', 'json', function(Y) {
  3. //Make this an Event Target so we can bubble to it
  4. var Portal = function() {
  5. Portal.superclass.constructor.apply(this, arguments);
  6. };
  7. Portal.NAME = 'portal';
  8. Y.extend(Portal, Y.Base);
  9. //This is our new bubble target..
  10. Y.Portal = new Portal();
  11.  
  12.  
  13. //Setup some private variables..
  14. var goingUp = false, lastY = 0, trans = {};
  15.  
  16. //The list of feeds that we are going to use
  17. var feeds = {
  18. 'ynews': {
  19. id: 'ynews',
  20. title: 'Yahoo! US News',
  21. url: 'rss.news.yahoo.com/rss/us'
  22. },
  23. 'yui': {
  24. id: 'yui',
  25. title: 'YUI Blog',
  26. url: 'feeds.yuiblog.com/YahooUserInterfaceBlog'
  27. },
  28. 'slashdot': {
  29. id: 'slashdot',
  30. title: 'Slashdot',
  31. url: 'rss.slashdot.org/Slashdot/slashdot'
  32. },
  33. 'ajaxian': {
  34. id: 'ajaxian',
  35. title: 'Ajaxian',
  36. url: 'feeds.feedburner.com/ajaxian'
  37. },
  38. 'daringfireball': {
  39. id: 'daringfireball',
  40. title: 'Daring Fireball',
  41. url: 'daringfireball.net/index.xml'
  42. },
  43. 'wiredtech': {
  44. id: 'wiredtech',
  45. title: 'Wire: Tech Biz',
  46. url: 'www.wired.com/rss/techbiz.xml'
  47. },
  48. 'techcrunch': {
  49. id: 'techcrunch',
  50. title: 'TechCrunch',
  51. url: 'feedproxy.google.com/Techcrunch'
  52. },
  53. 'smashing': {
  54. id: 'smashing',
  55. title: 'Smashing Magazine',
  56. url: 'www.smashingmagazine.com/wp-rss.php'
  57. }
  58. };
  59.  
  60. //Setup the config for IO to use flash
  61. Y.io.transport({
  62. id: 'flash',
  63. yid: Y.id,
  64. src: buildDir + 'io/io.swf?stamp=' + (new Date()).getTime()
  65. });
  66.  
  67. //Simple method for stopping event propagation
  68. //Using this so we can detach it later
  69. var stopper = function(e) {
  70. e.stopPropagation();
  71. };
  72.  
  73. //Get the order, placement and minned state of the modules and save them to a cookie
  74. var _setCookies = function() {
  75. var dds = Y.DD.DDM._drags;
  76. var list = {};
  77. //Walk all the drag elements
  78. Y.each(dds, function(v, k) {
  79. var par = v.get('node').get('parentNode');
  80. //Find all the lists with drag items in them
  81. if (par.test('ul.list')) {
  82. if (!list[par.get('id')]) {
  83. list[par.get('id')] = [];
  84. }
  85. }
  86. });
  87. //Walk the list
  88. Y.each(list, function(v, k) {
  89. //Get all the li's in the list
  90. var lis = Y.all('#' + k + ' li.item');
  91. lis.each(function(v2, k2) {
  92. //Get the drag instance for the list item
  93. var dd = Y.DD.DDM.getDrag('#' + v2.get('id'));
  94. //Get the mod node
  95. var mod = dd.get('node').query('div.mod');
  96. //Is it minimized
  97. var min = (mod.hasClass('minned')) ? true : false;
  98. //Setup the cookie data
  99. list[k][list[k].length] = { id: dd.get('data').id, min: min };
  100. });
  101. });
  102. //JSON encode the cookie data
  103. var cookie = Y.JSON.stringify(list);
  104. //Set the sub-cookie
  105. Y.Cookie.setSub('yui', 'portal', cookie);
  106. };
  107.  
  108. //Helper method for creating the feed DD on the left
  109. var _createFeedDD = function(node, data) {
  110. //Create the DD instance
  111. var dd = new Y.DD.Drag({
  112. node: node,
  113. data: data,
  114. bubbles: Y.Portal
  115. }).plug(Y.Plugin.DDProxy, {
  116. moveOnEnd: false,
  117. borderStyle: 'none'
  118. });
  119. //Setup some stopper events
  120. dd.on('drag:start', _handleStart);
  121. dd.on('drag:end', stopper);
  122. dd.on('drag:drophit', stopper);
  123. };
  124.  
  125. //Handle the node:click event
  126. /* {{{ */
  127. var _nodeClick = function(e) {
  128. //Is the target an href?
  129. if (e.target.test('a')) {
  130. var a = e.target, anim = null, div = a.get('parentNode').get('parentNode');
  131. //Did they click on the min button
  132. if (a.hasClass('min')) {
  133. //Get some node references
  134. var ul = div.query('ul'),
  135. h2 = div.query('h2'),
  136. h = h2.get('offsetHeight'),
  137. hUL = ul.get('offsetHeight'),
  138. inner = div.query('div.inner');
  139.  
  140. //Create an anim instance on this node.
  141. anim = new Y.Anim({
  142. node: inner
  143. });
  144. //Is it expanded?
  145. if (!div.hasClass('minned')) {
  146. //Set the vars for collapsing it
  147. anim.setAttrs({
  148. to: {
  149. height: 0
  150. },
  151. duration: '.25',
  152. easing: Y.Easing.easeOut,
  153. iteration: 1
  154. });
  155. //On the end, toggle the minned class
  156. //Then set the cookies for state
  157. anim.on('end', function() {
  158. div.toggleClass('minned');
  159. _setCookies();
  160. });
  161. } else {
  162. //Set the vars for expanding it
  163. anim.setAttrs({
  164. to: {
  165. height: (hUL)
  166. },
  167. duration: '.25',
  168. easing: Y.Easing.easeOut,
  169. iteration: 1
  170. });
  171. //Toggle the minned class
  172. //Then set the cookies for state
  173. div.toggleClass('minned');
  174. _setCookies();
  175. }
  176. //Run the animation
  177. anim.run();
  178.  
  179. }
  180. //Was close clicked?
  181. if (a.hasClass('close')) {
  182. //Get some Node references..
  183. var li = div.get('parentNode'),
  184. id = li.get('id'),
  185. dd = Y.DD.DDM.getDrag('#' + id),
  186. data = dd.get('data'),
  187. item = Y.Node.get('#' + data.id);
  188.  
  189. //Destroy the DD instance.
  190. dd.destroy();
  191. //Setup the animation for making it disappear
  192. anim = new Y.Anim({
  193. node: div,
  194. to: {
  195. opacity: 0
  196. },
  197. duration: '.25',
  198. easing: Y.Easing.easeOut
  199. });
  200. anim.on('end', function() {
  201. //On end of the first anim, setup another to make it collapse
  202. var anim = new Y.Anim({
  203. node: div,
  204. to: {
  205. height: 0
  206. },
  207. duration: '.25',
  208. easing: Y.Easing.easeOut
  209. });
  210. anim.on('end', function() {
  211. //Remove it from the document
  212. li.get('parentNode').removeChild(li);
  213. item.removeClass('disabled');
  214. //Setup a drag instance on the feed list
  215. _createFeedDD(item, data);
  216. _setCookies();
  217.  
  218. });
  219. //Run the animation
  220. anim.run();
  221. });
  222. //Run the animation
  223. anim.run();
  224. }
  225. //Stop the click
  226. e.halt();
  227. }
  228. };
  229. /* }}} */
  230.  
  231. //This creates the module, either from a drag event or from the cookie load
  232. var setupModDD = function(mod, data, dd) {
  233. var node = mod;
  234. //Listen for the click so we can react to the buttons
  235. node.query('h2').on('click', _nodeClick);
  236.  
  237. //Remove the event's on the original drag instance
  238. dd.detachAll('drag:start');
  239. dd.detachAll('drag:end');
  240. dd.detachAll('drag:drophit');
  241.  
  242. //It's a target
  243. dd.set('target', true);
  244. //Setup the handles
  245. dd.addHandle('h2').addInvalid('a');
  246. //Remove the mouse listeners on this node
  247. dd._unprep();
  248. //Update a new node
  249. dd.set('node', mod);
  250. //Reset the mouse handlers
  251. dd._prep();
  252.  
  253. //The Yahoo! Pipes URL
  254. var url = 'http:/'+'/pipes.yahooapis.com/pipes/pipe.run?_id=6b7b2c6a32f5a12e7259c36967052387&_render=json&url=http:/'+'/' + data.url;
  255. //Start the XDR request
  256. var id = Y.io(url, {
  257. method: 'GET',
  258. xdr: {
  259. use:'flash'
  260. },
  261. //XDR Listeners
  262. on: {
  263. success: function(id, data) {
  264. //On success get the feed data
  265. var d = feeds[trans[id]],
  266. //Node reference
  267. inner = d.mod.query('div.inner'),
  268. //Parse the JSON data
  269. oRSS = Y.JSON.parse(data.responseText),
  270. html = '';
  271.  
  272. //Did we get data?
  273. if (oRSS && oRSS.count) {
  274. //Walk the list and create the news list
  275. Y.each(oRSS.value.items, function(v, k) {
  276. if (k < 5) {
  277. html += '<li><a href="' + v.link + '" target="_blank">' + v.title + '</a>';
  278. }
  279. });
  280. }
  281. //Set the innerHTML of the module
  282. inner.set('innerHTML', '<ul>' + html + '</ul>');
  283. if (Y.DD.DDM.activeDrag) {
  284. //If we are still dragging, update the proxy element too..
  285. var proxy_inner = Y.DD.DDM.activeDrag.get('dragNode').query('div.inner');
  286. proxy_inner.set('innerHTML', '<ul>' + html + '</ul>');
  287.  
  288. }
  289. },
  290. failure: function(id, data) {
  291. //Something failed..
  292. alert('Feed failed to load..' + id + ' :: ' + data);
  293. }
  294. }
  295. });
  296. //Keep track of the transaction
  297. feeds[data.id].trans = id;
  298. feeds[data.id].mod = mod;
  299. trans[id.id] = data.id;
  300. };
  301.  
  302.  
  303. //Helper method to create the markup for the module..
  304. var createMod = function(feed) {
  305. var str = '<li class="item">' +
  306. '<div class="mod">' +
  307. '<h2><strong>' + feed.title + '</strong> <a title="minimize module" class="min" href="#"> </a>' +
  308. '<a title="close module" class="close" href="#"></a></h2>' +
  309. '<div class="inner">' +
  310. ' <div class="loading">Feed loading, please wait..</div>' +
  311. '</div>' +
  312. '</div>' +
  313. '</li>';
  314. return Y.Node.create(str);
  315. };
  316.  
  317. //Handle the start Drag event on the left side
  318. var _handleStart = function(e) {
  319. //Stop the event
  320. stopper(e);
  321. //Some private vars
  322. var drag = this,
  323. list3 = Y.Node.get('#list1'),
  324. mod = createMod(drag.get('data'));
  325.  
  326. //Add it to the first list
  327. list3.appendChild(mod);
  328. //Set the item on the left column disabled.
  329. drag.get('node').addClass('disabled');
  330. //Set the node on the instance
  331. drag.set('node', mod);
  332. //Add some styles to the proxy node.
  333. drag.get('dragNode').setStyles({
  334. opacity: '.5',
  335. borderStyle: 'none',
  336. width: '320px',
  337. height: '61px'
  338. });
  339. //Update the innerHTML of the proxy with the innerHTML of the module
  340. drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
  341. //set the inner module to hidden
  342. drag.get('node').query('div.mod').setStyle('visibility', 'hidden');
  343. //add a class for styling
  344. drag.get('node').addClass('moving');
  345. //Setup the DD instance
  346. setupModDD(mod, drag.get('data'), drag);
  347.  
  348. //Remove the listener
  349. this.detach('drag:start', _handleStart);
  350. };
  351.  
  352. //Walk through the feeds list and create the list on the left
  353. var feedList = Y.Node.get('#feeds ul');
  354. Y.each(feeds, function(v, k) {
  355. var li = Y.Node.create('<li id="' + k + '">' + v.title + '</li>');
  356. feedList.appendChild(li);
  357. //Create the DD instance for this item
  358. _createFeedDD(li, v);
  359. });
  360.  
  361. //This does the calculations for when and where to move a module
  362. var _moveMod = function(drag, drop) {
  363. if (drag.get('node').hasClass('item')) {
  364. var dragNode = drag.get('node'),
  365. dropNode = drop.get('node'),
  366. append = false,
  367. padding = 30,
  368. xy = drag.mouseXY,
  369. region = drop.region,
  370. middle1 = region.top + ((region.bottom - region.top) / 2),
  371. middle2 = region.left + ((region.right - region.left) / 2),
  372. dir = false,
  373. dir1 = false,
  374. dir2 = false;
  375.  
  376. //We could do something a little more fancy here, but we won't ;)
  377. if ((xy[1] < (region.top + padding))) {
  378. dir1 = 'top';
  379. }
  380. if ((region.bottom - padding) < xy[1]) {
  381. dir1 = 'bottom';
  382. }
  383. if ((region.right - padding) < xy[0]) {
  384. dir2 = 'right';
  385. }
  386. if ((xy[0] < (region.left + padding))) {
  387. dir2 = 'left';
  388. }
  389. dir = dir2;
  390. if (dir2 === false) {
  391. dir = dir1;
  392. }
  393. switch (dir) {
  394. case 'top':
  395. var next = dropNode.get('nextSibling');
  396. if (next) {
  397. dropNode = next;
  398. } else {
  399. append = true;
  400. }
  401. break;
  402. case 'bottom':
  403. break;
  404. case 'right':
  405. case 'left':
  406. break;
  407. }
  408.  
  409.  
  410. if ((dropNode !== null) && dir) {
  411. if (dropNode && dropNode.get('parentNode')) {
  412. if (!append) {
  413. dropNode.get('parentNode').insertBefore(dragNode, dropNode);
  414. } else {
  415. dropNode.get('parentNode').appendChild(dragNode);
  416. }
  417. }
  418. }
  419. //Resync all the targets because something moved..
  420. Y.Lang.later(50, Y, function() {
  421. Y.DD.DDM.syncActiveShims(true);
  422. });
  423. }
  424. };
  425.  
  426. /*
  427.   Handle the drop:enter event
  428.   Now when we get a drop enter event, we check to see if the target is an LI, then we know it's out module.
  429.   Here is where we move the module around in the DOM.
  430.   */
  431. Y.Portal.on('drop:enter', function(e) {
  432. if (!e.drag || !e.drop || (e.drop !== e.target)) {
  433. return false;
  434. }
  435. if (e.drop.get('node').get('tagName').toLowerCase() === 'li') {
  436. if (e.drop.get('node').hasClass('item')) {
  437. _moveMod(e.drag, e.drop);
  438. }
  439. }
  440. });
  441.  
  442. //Handle the drag:drag event
  443. //On drag we need to know if they are moved up or down so we can place the module in the proper DOM location.
  444. Y.Portal.on('drag:drag', function(e) {
  445. var y = e.target.mouseXY[1];
  446. if (y < lastY) {
  447. goingUp = true;
  448. } else {
  449. goingUp = false;
  450. }
  451. lastY = y;
  452. });
  453.  
  454. /*
  455.   Handle the drop:hit event
  456.   Now that we have a drop on the target, we check to see if the drop was not on a LI.
  457.   This means they dropped on the empty space of the UL.
  458.   */
  459. Y.Portal.on('drag:drophit', function(e) {
  460. var drop = e.drop.get('node'),
  461. drag = e.drag.get('node');
  462.  
  463. if (drop.get('tagName').toLowerCase() !== 'li') {
  464. if (!drop.contains(drag)) {
  465. drop.appendChild(drag);
  466. }
  467. }
  468. });
  469.  
  470. //Handle the drag:start event
  471. //Use some CSS here to make our dragging better looking.
  472. Y.Portal.on('drag:start', function(e) {
  473. var drag = e.target;
  474. if (drag.target) {
  475. drag.target.set('locked', true);
  476. }
  477. drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
  478. drag.get('dragNode').setStyle('opacity','.5');
  479. drag.get('node').query('div.mod').setStyle('visibility', 'hidden');
  480. drag.get('node').addClass('moving');
  481. });
  482.  
  483. //Handle the drag:end event
  484. //Replace some of the styles we changed on start drag.
  485. Y.Portal.on('drag:end', function(e) {
  486. var drag = e.target;
  487. if (drag.target) {
  488. drag.target.set('locked', false);
  489. }
  490. drag.get('node').setStyle('visibility', '');
  491. drag.get('node').query('div.mod').setStyle('visibility', '');
  492. drag.get('node').removeClass('moving');
  493. drag.get('dragNode').set('innerHTML', '');
  494. _setCookies();
  495. });
  496.  
  497.  
  498. //Handle going over a UL, for empty lists
  499. Y.Portal.on('drop:over', function(e) {
  500. var drop = e.drop.get('node'),
  501. drag = e.drag.get('node');
  502.  
  503. if (drop.get('tagName').toLowerCase() !== 'li') {
  504. if (!drop.contains(drag)) {
  505. drop.appendChild(drag);
  506. Y.Lang.later(50, Y, function() {
  507. Y.DD.DDM.syncActiveShims(true);
  508. });
  509. }
  510. }
  511. });
  512.  
  513. //Create simple targets for the main lists..
  514. var uls = Y.all('#play ul.list');
  515. uls.each(function(v, k) {
  516. var tar = new Y.DD.Drop({
  517. node: v,
  518. padding: '20 0',
  519. bubbles: Y.Portal
  520. });
  521. });
  522.  
  523.  
  524. Y.on('io:xdrReady', function() {
  525. //Get the cookie data
  526. var cookie = Y.Cookie.getSub('yui', 'portal');
  527. if (cookie) {
  528. //JSON parse the stored data
  529. var obj = Y.JSON.parse(cookie);
  530.  
  531. //Walk the data
  532. Y.each(obj, function(v, k) {
  533. //Get the node for the list
  534. var list = Y.Node.get('#' + k);
  535. //Walk the items in this list
  536. Y.each(v, function(v2, k2) {
  537. //Get the drag for it
  538. var drag = Y.DD.DDM.getDrag('#' + v2.id);
  539. //Create the module
  540. var mod = createMod(drag.get('data'));
  541. if (v2.min) {
  542. //If it's minned add some CSS
  543. mod.query('div.mod').addClass('minned');
  544. mod.query('div.inner').setStyle('height', '0px');
  545. }
  546. //Add it to the list
  547. list.appendChild(mod);
  548. //Set the drag listeners
  549. drag.get('node').addClass('disabled');
  550. drag.set('node', mod);
  551. drag.set('dragNode', Y.DD.DDM._proxy);
  552. drag.detachAll('drag:start');
  553. drag.detachAll('drag:end');
  554. drag.detachAll('drag:drophit');
  555. drag._unprep();
  556. //Setup the new Drag listeners
  557. setupModDD(mod, drag.get('data'), drag);
  558. });
  559. });
  560. }
  561. });
  562.  
  563. });
//Use loader to grab the modules needed
YUI(yuiConfig).use('dd', 'anim', 'io', 'cookie', 'json', function(Y) {
    //Make this an Event Target so we can bubble to it
    var Portal = function() {
        Portal.superclass.constructor.apply(this, arguments);
    };
    Portal.NAME = 'portal';
    Y.extend(Portal, Y.Base);
    //This is our new bubble target..
    Y.Portal = new Portal();
 
 
    //Setup some private variables..
    var goingUp = false, lastY = 0, trans = {};
 
    //The list of feeds that we are going to use
    var feeds = {
        'ynews': {
            id: 'ynews',
            title: 'Yahoo! US News',
            url: 'rss.news.yahoo.com/rss/us'
        },
        'yui': {
            id: 'yui',
            title: 'YUI Blog',
            url: 'feeds.yuiblog.com/YahooUserInterfaceBlog'
        },
        'slashdot': {
            id: 'slashdot',
            title: 'Slashdot',
            url: 'rss.slashdot.org/Slashdot/slashdot'
        },
        'ajaxian': {
            id: 'ajaxian',
            title: 'Ajaxian',
            url: 'feeds.feedburner.com/ajaxian'
        },
        'daringfireball': {
            id: 'daringfireball',
            title: 'Daring Fireball',
            url: 'daringfireball.net/index.xml'
        },
        'wiredtech': {
            id: 'wiredtech',
            title: 'Wire: Tech Biz',
            url: 'www.wired.com/rss/techbiz.xml'
        },
        'techcrunch': {
            id: 'techcrunch',
            title: 'TechCrunch',
            url: 'feedproxy.google.com/Techcrunch'
        },
        'smashing': {
            id: 'smashing',
            title: 'Smashing Magazine',
            url: 'www.smashingmagazine.com/wp-rss.php'
        }
    };
 
    //Setup the config for IO to use flash
	Y.io.transport({
		id: 'flash',
		yid: Y.id,
		src: buildDir + 'io/io.swf?stamp=' + (new Date()).getTime()
    });
 
    //Simple method for stopping event propagation
    //Using this so we can detach it later
    var stopper = function(e) {
        e.stopPropagation();
    };
 
    //Get the order, placement and minned state of the modules and save them to a cookie
    var _setCookies = function() {
        var dds = Y.DD.DDM._drags;
        var list = {};
        //Walk all the drag elements
        Y.each(dds, function(v, k) {
            var par = v.get('node').get('parentNode');
            //Find all the lists with drag items in them
            if (par.test('ul.list')) {
                if (!list[par.get('id')]) {
                    list[par.get('id')] = [];
                }
            }
        });
        //Walk the list
        Y.each(list, function(v, k) {
            //Get all the li's in the list
            var lis = Y.all('#' + k + ' li.item');
            lis.each(function(v2, k2) {
                //Get the drag instance for the list item
                var dd = Y.DD.DDM.getDrag('#' + v2.get('id'));
                //Get the mod node
                var mod = dd.get('node').query('div.mod');
                //Is it minimized
                var min = (mod.hasClass('minned')) ? true : false;
                //Setup the cookie data
                list[k][list[k].length] = { id: dd.get('data').id, min: min };
            });
        });
        //JSON encode the cookie data
        var cookie = Y.JSON.stringify(list);
        //Set the sub-cookie
        Y.Cookie.setSub('yui', 'portal', cookie);
    };
 
    //Helper method for creating the feed DD on the left
    var _createFeedDD = function(node, data) {
        //Create the DD instance
        var dd = new Y.DD.Drag({
            node: node,
            data: data,
            bubbles: Y.Portal
        }).plug(Y.Plugin.DDProxy, {
            moveOnEnd: false,
            borderStyle: 'none'
        });
        //Setup some stopper events
        dd.on('drag:start', _handleStart);
        dd.on('drag:end', stopper);
        dd.on('drag:drophit', stopper);
    };
 
    //Handle the node:click event
    /* {{{ */
    var _nodeClick = function(e) {
        //Is the target an href?
        if (e.target.test('a')) {
            var a = e.target, anim = null, div = a.get('parentNode').get('parentNode');
            //Did they click on the min button
            if (a.hasClass('min')) {
                //Get some node references
                var ul = div.query('ul'),
                    h2 = div.query('h2'),
                h = h2.get('offsetHeight'),
                hUL = ul.get('offsetHeight'),
                inner = div.query('div.inner');
 
                //Create an anim instance on this node.
                anim = new Y.Anim({
                    node: inner
                });
                //Is it expanded?
                if (!div.hasClass('minned')) {
                    //Set the vars for collapsing it
                    anim.setAttrs({
                        to: {
                            height: 0
                        },
                        duration: '.25',
                        easing: Y.Easing.easeOut,
                        iteration: 1
                    });
                    //On the end, toggle the minned class
                    //Then set the cookies for state
                    anim.on('end', function() {
                        div.toggleClass('minned');
                        _setCookies();
                    });
                } else {
                    //Set the vars for expanding it
                    anim.setAttrs({
                        to: {
                            height: (hUL)
                        },
                        duration: '.25',
                        easing: Y.Easing.easeOut,
                        iteration: 1
                    });
                    //Toggle the minned class
                    //Then set the cookies for state
                    div.toggleClass('minned');
                    _setCookies();
                }
                //Run the animation
                anim.run();
 
            }
            //Was close clicked?
            if (a.hasClass('close')) {
                //Get some Node references..
                var li = div.get('parentNode'),
                    id = li.get('id'),
                    dd = Y.DD.DDM.getDrag('#' + id),
                    data = dd.get('data'),
                    item = Y.Node.get('#' + data.id);
 
                //Destroy the DD instance.
                dd.destroy();
                //Setup the animation for making it disappear
                anim = new Y.Anim({
                    node: div,
                    to: {
                        opacity: 0
                    },
                    duration: '.25',
                    easing: Y.Easing.easeOut
                });
                anim.on('end', function() {
                    //On end of the first anim, setup another to make it collapse
                    var anim = new Y.Anim({
                        node: div,
                        to: {
                            height: 0
                        },
                        duration: '.25',
                        easing: Y.Easing.easeOut
                    });
                    anim.on('end', function() {
                        //Remove it from the document
                        li.get('parentNode').removeChild(li);
                        item.removeClass('disabled');
                        //Setup a drag instance on the feed list
                        _createFeedDD(item, data);
                        _setCookies();
 
                    });
                    //Run the animation
                    anim.run();
                });
                //Run the animation
                anim.run();
            }
            //Stop the click
            e.halt();
        }
    };
    /* }}} */
 
    //This creates the module, either from a drag event or from the cookie load
    var setupModDD = function(mod, data, dd) {
        var node = mod;
        //Listen for the click so we can react to the buttons
        node.query('h2').on('click', _nodeClick);
 
        //Remove the event's on the original drag instance
        dd.detachAll('drag:start');
        dd.detachAll('drag:end');
        dd.detachAll('drag:drophit');
 
        //It's a target
        dd.set('target', true);
        //Setup the handles
        dd.addHandle('h2').addInvalid('a');
        //Remove the mouse listeners on this node
        dd._unprep();
        //Update a new node
        dd.set('node', mod);
        //Reset the mouse handlers
        dd._prep();
 
        //The Yahoo! Pipes URL
        var url = 'http:/'+'/pipes.yahooapis.com/pipes/pipe.run?_id=6b7b2c6a32f5a12e7259c36967052387&_render=json&url=http:/'+'/' + data.url;
        //Start the XDR request
        var id = Y.io(url, {
            method: 'GET',
            xdr: { 
			    use:'flash'
		    },
            //XDR Listeners
		    on: { 
			    success: function(id, data) {
                    //On success get the feed data
                    var d = feeds[trans[id]],
                    //Node reference
                    inner = d.mod.query('div.inner'),
                    //Parse the JSON data
                    oRSS = Y.JSON.parse(data.responseText),
                    html = '';
 
                    //Did we get data?
		            if (oRSS && oRSS.count) {
                        //Walk the list and create the news list
			            Y.each(oRSS.value.items, function(v, k) {
                            if (k < 5) {
                                html += '<li><a href="' + v.link + '" target="_blank">' + v.title + '</a>';
                            }
                        });
		            }
                    //Set the innerHTML of the module
                    inner.set('innerHTML', '<ul>' + html + '</ul>');
                    if (Y.DD.DDM.activeDrag) {
                        //If we are still dragging, update the proxy element too..
                        var proxy_inner = Y.DD.DDM.activeDrag.get('dragNode').query('div.inner');
                        proxy_inner.set('innerHTML', '<ul>' + html + '</ul>');
 
                    }
                },
			    failure: function(id, data) {
                    //Something failed..
                    alert('Feed failed to load..' + id + ' :: ' + data);
                }
		    }
        });
        //Keep track of the transaction
        feeds[data.id].trans = id;
        feeds[data.id].mod = mod;
        trans[id.id] = data.id;
    };
 
 
    //Helper method to create the markup for the module..
    var createMod = function(feed) {
        var str = '<li class="item">' +
                    '<div class="mod">' + 
                        '<h2><strong>' + feed.title + '</strong> <a title="minimize module" class="min" href="#"> </a>' +
                        '<a title="close module" class="close" href="#"></a></h2>' +
                        '<div class="inner">' +
                        '    <div class="loading">Feed loading, please wait..</div>' + 
                        '</div>' +
                    '</div>' +
                '</li>';
        return Y.Node.create(str);
    };
 
    //Handle the start Drag event on the left side
    var _handleStart = function(e) {
        //Stop the event
        stopper(e);
        //Some private vars
        var drag = this,
            list3 = Y.Node.get('#list1'),
            mod = createMod(drag.get('data'));
 
        //Add it to the first list
        list3.appendChild(mod);
        //Set the item on the left column disabled.
        drag.get('node').addClass('disabled');
        //Set the node on the instance
        drag.set('node', mod);
        //Add some styles to the proxy node.
        drag.get('dragNode').setStyles({
            opacity: '.5',
            borderStyle: 'none',
            width: '320px',
            height: '61px'
        });
        //Update the innerHTML of the proxy with the innerHTML of the module
        drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
        //set the inner module to hidden
        drag.get('node').query('div.mod').setStyle('visibility', 'hidden');
        //add a class for styling
        drag.get('node').addClass('moving');
        //Setup the DD instance
        setupModDD(mod, drag.get('data'), drag);
 
        //Remove the listener
        this.detach('drag:start', _handleStart);
    };
 
    //Walk through the feeds list and create the list on the left
    var feedList = Y.Node.get('#feeds ul');
    Y.each(feeds, function(v, k) {
        var li = Y.Node.create('<li id="' + k + '">' + v.title + '</li>');
        feedList.appendChild(li);
        //Create the DD instance for this item
        _createFeedDD(li, v);
    });
 
    //This does the calculations for when and where to move a module
    var _moveMod = function(drag, drop) {
        if (drag.get('node').hasClass('item')) {
            var dragNode = drag.get('node'),
                dropNode = drop.get('node'),
                append = false,
                padding = 30,
                xy = drag.mouseXY,
                region = drop.region,
                middle1 = region.top + ((region.bottom - region.top) / 2),
                middle2 = region.left + ((region.right - region.left) / 2),
                dir = false,
                dir1 = false,
                dir2 = false;
 
                //We could do something a little more fancy here, but we won't ;)
                if ((xy[1] < (region.top + padding))) {
                    dir1 = 'top';
                }
                if ((region.bottom - padding) < xy[1]) {
                    dir1 = 'bottom';
                }
                if ((region.right - padding) < xy[0]) {
                    dir2 = 'right';
                }
                if ((xy[0] < (region.left + padding))) {
                    dir2 = 'left';
                }
                dir = dir2;
                if (dir2 === false) {
                    dir = dir1;
                }
                switch (dir) {
                    case 'top':
                        var next = dropNode.get('nextSibling');
                        if (next) {
                            dropNode = next;
                        } else {
                            append = true;
                        }
                        break;
                    case 'bottom':
                        break;
                    case 'right':
                    case 'left':
                        break;
                }
 
 
            if ((dropNode !== null) && dir) {
                if (dropNode && dropNode.get('parentNode')) {
                    if (!append) {
                        dropNode.get('parentNode').insertBefore(dragNode, dropNode);
                    } else {
                        dropNode.get('parentNode').appendChild(dragNode);
                    }
                }
            }
            //Resync all the targets because something moved..
            Y.Lang.later(50, Y, function() {
                Y.DD.DDM.syncActiveShims(true);
            });
        }
    };
 
    /*
    Handle the drop:enter event
    Now when we get a drop enter event, we check to see if the target is an LI, then we know it's out module.
    Here is where we move the module around in the DOM.    
    */
    Y.Portal.on('drop:enter', function(e) {
        if (!e.drag || !e.drop || (e.drop !== e.target)) {
            return false;
        }
        if (e.drop.get('node').get('tagName').toLowerCase() === 'li') {
            if (e.drop.get('node').hasClass('item')) {
                _moveMod(e.drag, e.drop);
            }
        }
    });
 
    //Handle the drag:drag event
    //On drag we need to know if they are moved up or down so we can place the module in the proper DOM location.
    Y.Portal.on('drag:drag', function(e) {
        var y = e.target.mouseXY[1];
        if (y < lastY) {
            goingUp = true;
        } else {
            goingUp = false;
        }
        lastY = y;
    });
 
    /*
    Handle the drop:hit event
    Now that we have a drop on the target, we check to see if the drop was not on a LI.
    This means they dropped on the empty space of the UL.
    */
    Y.Portal.on('drag:drophit', function(e) {
        var drop = e.drop.get('node'),
            drag = e.drag.get('node');
 
        if (drop.get('tagName').toLowerCase() !== 'li') {
            if (!drop.contains(drag)) {
                drop.appendChild(drag);
            }
        }
    });
 
    //Handle the drag:start event
    //Use some CSS here to make our dragging better looking.
    Y.Portal.on('drag:start', function(e) {
        var drag = e.target;
        if (drag.target) {
            drag.target.set('locked', true);
        }
        drag.get('dragNode').set('innerHTML', drag.get('node').get('innerHTML'));
        drag.get('dragNode').setStyle('opacity','.5');
        drag.get('node').query('div.mod').setStyle('visibility', 'hidden');
        drag.get('node').addClass('moving');
    });
 
    //Handle the drag:end event
    //Replace some of the styles we changed on start drag.
    Y.Portal.on('drag:end', function(e) {
        var drag = e.target;
        if (drag.target) {
            drag.target.set('locked', false);
        }
        drag.get('node').setStyle('visibility', '');
        drag.get('node').query('div.mod').setStyle('visibility', '');
        drag.get('node').removeClass('moving');
        drag.get('dragNode').set('innerHTML', '');
        _setCookies();
    });
 
 
    //Handle going over a UL, for empty lists
    Y.Portal.on('drop:over', function(e) {
        var drop = e.drop.get('node'),
            drag = e.drag.get('node');
 
        if (drop.get('tagName').toLowerCase() !== 'li') {
            if (!drop.contains(drag)) {
                drop.appendChild(drag);
                Y.Lang.later(50, Y, function() {
                    Y.DD.DDM.syncActiveShims(true);
                });
            }
        }
    });
 
    //Create simple targets for the main lists..
    var uls = Y.all('#play ul.list');
    uls.each(function(v, k) {
        var tar = new Y.DD.Drop({
            node: v,
            padding: '20 0',
            bubbles: Y.Portal
        });
    });
 
 
    Y.on('io:xdrReady', function() {
        //Get the cookie data
        var cookie = Y.Cookie.getSub('yui', 'portal');
        if (cookie) {
            //JSON parse the stored data
            var obj = Y.JSON.parse(cookie);
 
            //Walk the data
            Y.each(obj, function(v, k) {
                //Get the node for the list
                var list = Y.Node.get('#' + k);
                //Walk the items in this list
                Y.each(v, function(v2, k2) {
                    //Get the drag for it
                    var drag = Y.DD.DDM.getDrag('#' + v2.id);
                    //Create the module
                    var mod = createMod(drag.get('data'));
                    if (v2.min) {
                        //If it's minned add some CSS
                        mod.query('div.mod').addClass('minned');
                        mod.query('div.inner').setStyle('height', '0px');
                    }
                    //Add it to the list
                    list.appendChild(mod);
                    //Set the drag listeners
                    drag.get('node').addClass('disabled');
                    drag.set('node', mod);
                    drag.set('dragNode', Y.DD.DDM._proxy);
                    drag.detachAll('drag:start');
                    drag.detachAll('drag:end');
                    drag.detachAll('drag:drophit');
                    drag._unprep();
                    //Setup the new Drag listeners
                    setupModDD(mod, drag.get('data'), drag);
                });
            });
        }
    });
 
});

Copyright © 2009 Yahoo! Inc. All rights reserved.

Privacy Policy - Terms of Service - Copyright Policy - Job Openings