//Use loader to grab the modules needed
YUI().use('dd', 'anim', 'yql', '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'
}
};
//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').one('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.one('ul'),
h2 = div.one('h2'),
h = h2.get('offsetHeight'),
hUL = ul.get('offsetHeight'),
inner = div.one('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.one('#' + 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.one('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();
Y.YQL('select * from feed where url="http:/'+'/' + data.url + '"', (function(mod) {
return function(r) {
if (r && r.query && r.query.results) {
var inner = mod.one('div.inner'),
html = '';
//Walk the list and create the news list
Y.each(r.query.results, function(items) {
Y.each(items, function(v, k) {
if (k < 5) {
if (v.title && v.title.content) {
v.title = v.title.content;
}
if (v.link && (Y.Lang.isArray(v.link))) {
v.link = v.link[0];
}
if (v.link && (Y.Lang.isObject(v.link))) {
v.link = v.link.href;
}
html += Y.Lang.sub('<li><a href="{link}" target="_blank">{title}</a>', v);
}
});
});
//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').one('div.inner');
proxy_inner.set('innerHTML', '<ul>' + html + '</ul>');
}
}
}
})(mod));
};
//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="#">X</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.one('#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').one('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.one('#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').one('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').one('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
});
});
//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.one('#' + 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.one('div.mod').addClass('minned');
mod.one('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);
});
});
}
});