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 = '
    '+label+'
    '; if(item.count) { html += '
    '+item.count+'
    '; } if(this.get("edit")&&!score) { html += '
    e
    '; } if(this.get("remove")&&!score) { html += '
    x
    '; } if(score) { html += '
    '+score+'
    '; } 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 += '
    ' +'v' +''+r.name+'
    '; }; node.append('
    '+html+'
    '); }) }, reconcile : function(server) { var tags = this.get("tags"), query = {}; for (var i=0; i < tags.length; i++) { query['q'+i] = {query:tags[i].tag.value} } var url = server+"?queries="+encodeURIComponent(Y.JSON.stringify(query))+"&callback={callback}"; Y.jsonp(url, { on: {success: this.reconcileHandler}, context: this }); } }); }, 'gallery-2010.03.02-18' ,{requires:['node','anim','widget']});