YUI.add('tag-carousel', function(Y) {
var Lang = Y.Lang,
Widget = Y.Widget,
Node = Y.Node;
var NS = Y.namespace('mazzle');
NS.TagCarousel = TagCarousel;
/* TagCarousel class constructor */
function TagCarousel(config) {
TagCarousel.superclass.constructor.apply(this, arguments);
}
/*
* Required NAME static field, to identify the Widget class and
* used as an event prefix, to generate class names etc. (set to the
* class name in camel case).
*/
TagCarousel.NAME = "tag-carousel";
/*
* The attribute configuration for the TagCarousel widget. Attributes can be
* defined with default values, get/set functions and validator functions
* as with any other class extending Base.
*/
TagCarousel.ATTRS = {
tags: {
value: []
},
active: {
value: true
},
topIndent: {
value: true
},
edit: {
value: false
},
remove: {
value: false
},
confirm: {
value: false
},
concept: {
value: false
},
suggest: {
value: null
},
search: {
value: false
},
queryDelay: {
value: 0.3
},
minQueryLength: {
value: 2
},
info: {
value: false
}
};
/* Static constants used to define the markup templates used to create TagCarousel DOM elements */
TagCarousel.LIST_CLASS = 'tag-list';
TagCarousel.LIST_TEMPLATE = '
';
/* TagCarousel extends the base Widget class */
Y.extend(TagCarousel, Widget, {
initializer: function() {
this._nDelayID = -1;
this.scores = {};
},
destructor : function() {
},
renderUI : function() {
var content = this.get("contentBox"),
height = this.get("height");
// create search field
if(this.search) {
var search = Node.create('');
this.get("boundingBox").prepend(search);
search.on("keyup", this._search, this);
}
// info panel
if(this.get("info")) {
this.infoPanel = new Y.Overlay(
{width:"400px"
});
this.infoPanel.render();
}
// tag list
content.setStyle("position", "relative");
if(this.get("topIndent")) {
content.setStyle("top", height/2+"px");
}
this.listNode = content.appendChild(Node.create(TagCarousel.LIST_TEMPLATE));
},
bindUI : function() {
this.after("tagsChange", this.syncUI);
Y.delegate("click", this._itemSelect, this.listNode, "li .label", this);
if(this.get("edit")) {
Y.delegate("click", this._itemEdit, this.listNode, "li .edit", this);
}
if(this.get("remove")) {
Y.delegate("click", this._itemRemove, this.listNode, "li .remove", this);
}
if(this.get("confirm")) {
Y.delegate("click", this._itemConfirm, this.listNode, "li .confirm", this);
}
if(this.get("reconcile")) {
Y.delegate("click", this._reconcileSelect, this.listNode, "li .recon-select", this);
Y.delegate("click", this._reconcileInfo, this.listNode, "li .recon-concept", this);
}
this._scrollAnim = new Y.Anim({
node: this.get("boundingBox"),
duration: 1,
easing: Y.Easing.easeOut
});
},
syncUI : function() {
this._renderItems();
},
_renderItems : function() {
var tags = this.get("tags"),
timeIndex = {};
this.listNode.setContent("");
// format the items and store in the time index
for(var i=0; i < tags.length; i++) {
this.listNode.append('
'+this.formatItem(tags[i])+'
');
var time = Math.round(tags[i].startTime/1000); //TBD make this hookable
timeIndex[time] = i;
}
this._timeIndex = timeIndex;
},
formatItem : function(item) {
var tag = item.tag,
label = tag.label ? tag.label : tag.value,
score = item.score,
match = item.match;
html = '
';
} else {
html += '';
}
if(this.get("confirm")&&(match&&Lang.isArray(match))) {
for (var i=0; i < match.length; i++) {
var matchItem = match[i];
html += '
'
+'
'+matchItem.value.value+'
'
+''
+'
'
+''
+''
+'
';
}
}
if(this.get("concept")&&(match&&Lang.isArray(match))) {
for (var i=0; i < match.length; i++) {
var concept = match[i],
altlabel = concept.altlabel?concept.altlabel:'',
desc = concept.desc?concept.desc:'';
html += '
'
+'
'
+concept.label+' '+altlabel+'
'
+''
+'
'
+''
+'
';
}
}
if(item.uri) {
return ''+html+'';
} else {
return html;
}
},
_itemSelect : function(e) {
// item click
var node = e.currentTarget.get("parentNode"),
index = e.container.all("li").indexOf(node),
item = this.get("tags")[index],
arg = {li:node, index:index, tag:item};
if(!node.one('input')) {
Y.log('clicked tag '+item.tag.value+' at index '+index);
this._highlight(index);
this.fire("itemSelect", arg);
}
},
_itemEdit : function(e) {
var node = e.currentTarget.get("parentNode"),
index = e.container.all("li").indexOf(node),
item = this.get("tags")[index],
labelNode = node.one('.label'),
label = labelNode.get("innerHTML"),
arg = {
type:'edit',
index:index,
annotation:item
};
if(this.get("suggest")) {
var suggest = this.get("suggest");
if(suggest.hasClass("hidden")) {
suggest.align.to(labelNode, "tl","tl");
suggest.one('input').set("value", label);
suggest.removeClass("hidden");
} else {
suggest.addClass("hidden");
var newvalue = suggest.one('input').get("value");
labelNode.setContent(newvalue);
}
} else {
var inputNode = node.one('input');
if(inputNode) {
var newvalue = inputNode.get("value");
arg.newvalue = newvalue;
this.fire("itemUpdate", arg);
labelNode.setContent(newvalue);
} else {
Y.log('edit tag '+item.tag.value+' at index '+index);
labelNode.setContent("");
this.fire("itemStartEdit", arg);
}
}
},
_itemRemove : function(e) {
var node = e.currentTarget.get("parentNode"),
index = e.container.all("li").indexOf(node),
item = this.get("tags")[index],
arg = {
type:'remove',
index:index,
annotation:item
};
Y.log('remove tag '+item.tag.value+' at index '+index);
node.addClass("hidden");
this.fire("itemUpdate", arg);
},
_itemConfirm : function(e) {
var action = e.target.hasClass("accept") ? "accept" : "reject",
node = e.currentTarget.get("parentNode").get("parentNode"),
matchIndex = e.currentTarget.get("title"),
index = e.container.all("li").indexOf(node),
item = this.get("tags")[index],
arg = {
action:action,
index:index,
matchIndex:matchIndex,
annotation:item
};
Y.log(action+' match '+item.tag.value+' at index '+index);
this.fire("itemConfirm", arg);
},
_reconcileSelect : function(e) {
var reconcile = e.currentTarget.get("parentNode").get("parentNode"),
node = reconcile.get("parentNode"),
index = e.container.all("li").indexOf(node),
item = this.get("tags")[index],
rindex = reconcile.all('.r').indexOf(e.currentTarget.get("parentNode")),
concept = this.concepts['q'+index].result[rindex];
arg = {
index:index,
annotation:item,
concept:concept
};
Y.log('reconcile select '+item.tag.value+' with concept '+concept.name);
this.fire("reconcileSelect", arg);
},
showInfoPanel : function(o, node) {
this.infoPanel.set("bodyContent", o.html);
this.infoPanel.set("align", {
node:node,
points:[Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.TR]
});
},
_reconcileInfo : function(e) {
var reconcile = e.currentTarget.get("parentNode").get("parentNode"),
node = reconcile.get("parentNode"),
index = e.container.all("li").indexOf(node),
rindex = reconcile.all('.r').indexOf(e.currentTarget.get("parentNode")),
concept = this.concepts['q'+index].result[rindex],
id = concept.id;
var url = 'http://www.freebase.com/private/flyout?id='
+encodeURIComponent(id)
+'&callback={callback}';
Y.jsonp(url, {
on: {success: this.showInfoPanel},
context: this,
args: [node]
});
},
focusTag : function(tag) {
this.focusIndex(this.tagIndex(tag));
},
focusNode : function(node) {
var index = this.listNode.all("li").indexOf(node);
this.focusIndex(index);
},
focusTime : function(time) {
var timeIndex = this._timeIndex,
index = timeIndex[time];
if(index>=0) {
Y.log('tagged '+this.get("tags")[index].tag);
this.focusIndex(index);
}
},
focusIndex : function(index) {
if(this.get("active")) {
this._scrollTo(index);
this._highlight(index);
}
},
scoreIndex : function(index, score, id, action) {
var item = this.listNode.all("li").item(index),
el;
if(id) {
this.scores[id] = item;
}
if(type=='edit'&&score>0) {
el = '.edit';
} else if(type=='confirm') {
el = '.confirm';
}
if(el) {
item.addClass('scored '+action);
item.one(el).addClass("hidden");
if(score>0) {
item.one('.score')
.setContent(score)
.removeClass("hidden");
}
}
},
tagIndex : function(tag) {
var tags = this.get("tags");
var i = 0;
for (i; i < tags.length; i++) {
if(tags[i].tag === tag) {
return i;
}
}
},
_scrollTo : function(index) {
var node = this.get("boundingBox"),
items = this.listNode.all("li"),
anim = this._scrollAnim,
scrollTo = Math.abs(this.listNode.getY() - items.item(index).getY());
Y.log('scroll from '+node.get('scrollTop')+' to '+scrollTo);
anim.set('to', { scroll: [node.get('scrollTop'), scrollTo] });
anim.run();
},
_highlight : function(index) {
var items = this.listNode.all("li");
// removeFocus from other items
items.removeClass('focus');
// add focus class to current item
items.item(index).addClass('focus');
},
undo : function(e) {
var history = e.history,
items = this.listNode.all("li");
for (var i=0; i < history.length; i++) {
var action = history[i],
node = items.item(action.index);
if(action.type == "remove") {
node.removeClass("hidden");
} else if(action.type == "edit") {
node.one('.label').setContent(action.tag);
}
}
},
setConfirm : function(index, matchIndex, id, action) {
var match = this.listNode.all("li").item(index)
.all('.match').item(matchIndex);
this.scores[id] = match;
match.addClass(action);
match.one('.confirm').addClass("hidden");
},
updateMatch : function(matches) {
for(var i=0; i < matches.length; i++) {
var match = matches[i];
if(match.score) {
var node = this.scores[match.id];
if(node) {
node.one('.score')
.setContent(match.score)
.removeClass("hidden");
}
}
}
},
_search : function(e) {
var delay = this.get("queryDelay"),
query = e.currentTarget.get("value").toLowerCase(),
listNode = this.listNode,
tags = this.get("tags");
// Clear previous timeout
if(this._nDelayID != -1) {
clearTimeout(this._nDelayID);
}
if(query.length < this.get("minQueryLength")) {
listNode.all("li").each(function(node) {
node.removeClass("hidden");
})
} else {
// Set new timeout
this._nDelayID = setTimeout(function(){
listNode.all("li").each(function(node, i) {
var tag = tags[i].tag;
var label = tag.label ? tag.label : tag.value;
if(Lang.isString(label) && label.toLowerCase().indexOf(query) == 0) {
node.removeClass("hidden");
} else {
node.addClass("hidden");
}
})
}, delay*1000);
}
},
reconcileHandler : function(results) {
this.concepts = results;
this.listNode.all("li").each(function(node, index) {
var qresult = results['q'+index].result,
html = '';
for (var i=0; i < qresult.length; i++) {
var r = qresult[i];
html += '