867 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			867 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| compile insert RTI_IDE_HTML_GOOGLE_DYNAMICFEED
 | |
| /**
 | |
|  * Copyright (c) 2008 Google Inc.
 | |
|  *
 | |
|  * You are free to copy and use this sample.
 | |
|  * License can be found here: http://code.google.com/apis/ajaxsearch/faq/#license
 | |
| */
 | |
| 
 | |
| /**
 | |
|  * @fileoverview A feed gadget based on the AJAX Feed API.
 | |
|  * @author dcollison@google.com (Derek Collison)
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * GFdynamicFeedControl
 | |
|  * @param {string} feed The feed URL.
 | |
|  * @param {String|Object} container Either the id string or the element itself.
 | |
|  * @param {Object} options Options map.
 | |
|  * @constructor
 | |
|  */
 | |
| 
 | |
| function GFdynamicFeedControl(feedUrls, container, options) {
 | |
|   // node elements.
 | |
|   this.nodes = {};
 | |
|   this.collapseElements = [];
 | |
|   
 | |
|   // the feeds.
 | |
|   this.feeds = [];
 | |
|   this.results = [];
 | |
| 
 | |
|   if (typeof feedUrls == 'string') {
 | |
|     this.feeds.push({url:feedUrls});
 | |
|   } else if (typeof feedUrls == 'object') {
 | |
|     for (var i=0; i<feedUrls.length; i++) {
 | |
|       var entry = feedUrls[i];
 | |
|       var o = {};
 | |
|       var feedUrl;
 | |
|       if (typeof entry == 'string') {
 | |
|         o.url = feedUrls[i];
 | |
|       } else if (typeof entry == 'object') {
 | |
|         o = feedUrls[i];
 | |
| 	if (o && o.title) {
 | |
| 	  var s = o.title;
 | |
| 	  o.title = s.replace(/</g,'<').replace(/>/g, '>');
 | |
| 	}
 | |
|       }
 | |
|       this.feeds.push(o);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (typeof container == "string") {
 | |
|     container = document.getElementById(container);
 | |
|   }
 | |
|   this.parseOptions_(options);
 | |
|   this.setup_(container);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Default time in milliseconds for the feed to be reloaded.
 | |
|  * @type Number
 | |
|  */
 | |
| GFdynamicFeedControl.DEFAULT_NUM_RESULTS = 4;
 | |
| /*
 | |
|  * Default time in milliseconds for the feed to be reloaded.
 | |
|  * @type Number
 | |
|  */
 | |
| GFdynamicFeedControl.DEFAULT_FEED_CYCLE_TIME = 1800000;
 | |
| /*
 | |
|  * Default display time in milliseconds for each entry.
 | |
|  * @type Number
 | |
|  */
 | |
| GFdynamicFeedControl.DEFAULT_DISPLAY_TIME = 5000;
 | |
| /*
 | |
|  * Default fadeout transition time in milliseconds for each entry.
 | |
|  * @type Number
 | |
|  */
 | |
| GFdynamicFeedControl.DEFAULT_FADEOUT_TIME = 1000;
 | |
| /*
 | |
|  * Default time between transition steps in milliseconds
 | |
|  * @type Number
 | |
|  */
 | |
| GFdynamicFeedControl.DEFAULT_TRANSISTION_STEP = 40;
 | |
| /*
 | |
|  * Default hover time in milliseconds for each entry.
 | |
|  * @type Number
 | |
|  */
 | |
| GFdynamicFeedControl.DEFAULT_HOVER_TIME = 100;
 | |
| 
 | |
| /**
 | |
|  * Setup default option map and apply overrides from constructor.
 | |
|  * @param {Object} options Options map.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.parseOptions_ = function(options) {
 | |
|   // Default Options
 | |
|   // TODO(dcollison) - implement Feed Cycle.
 | |
|   this.options = {
 | |
|     numResults : GFdynamicFeedControl.DEFAULT_NUM_RESULTS,
 | |
|     feedCycleTime : GFdynamicFeedControl.DEFAULT_FEED_CYCLE_TIME,
 | |
|     linkTarget : google.feeds.LINK_TARGET_BLANK,
 | |
|     displayTime : GFdynamicFeedControl.DEFAULT_DISPLAY_TIME,
 | |
|     transitionTime : GFdynamicFeedControl.DEFAULT_TRANSISTION_TIME,
 | |
|     transitionStep : GFdynamicFeedControl.DEFAULT_TRANSISTION_STEP,
 | |
|     fadeOutTime: GFdynamicFeedControl.DEFAULT_FADEOUT_TIME,
 | |
|     scrollOnFadeOut : true,
 | |
|     pauseOnHover : true,
 | |
|     hoverTime : GFdynamicFeedControl.DEFAULT_HOVER_TIME,
 | |
|     autoCleanup : true,
 | |
|     transitionCallback : null,
 | |
|     feedTransitionCallback : null,
 | |
|     feedLoadCallback : null,
 | |
|     collapseable : false,
 | |
|     sortByDate : false,
 | |
|     horizontal : false,
 | |
|     stacked : false,
 | |
|     title : null
 | |
|   };
 | |
| 
 | |
|   if (options) {
 | |
|     for (var o in this.options) {
 | |
|       if (typeof options[o] != 'undefined') {
 | |
|         this.options[o] = options[o];
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   // Cant be collapseable unless stacked
 | |
|   if(!this.options.stacked) {
 | |
|     this.options.collapseable = false;
 | |
|   }
 | |
|   
 | |
|   // Override strange/bad options
 | |
|   this.options.displayTime = Math.max(200, this.options.displayTime);
 | |
|   this.options.fadeOutTime = Math.max(0, this.options.fadeOutTime);
 | |
| 
 | |
|   // Calculated
 | |
|   var ts = this.options.fadeOutTime / this.options.transitionStep;
 | |
|   this.fadeOutDelta = Math.min(1, (1.0/ts));
 | |
| 
 | |
|   // Flag to start
 | |
|   this.started = false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Basic setup.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.setup_ = function(container) {
 | |
|   if (container == null) return;
 | |
|   this.nodes.container = container;
 | |
| 
 | |
|   // Browser fun.
 | |
|   if (window.ActiveXObject) {
 | |
|     this.ie = this[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true;
 | |
|   } else if (document.childNodes && !document.all && !navigator.taintEnabled) {
 | |
|     this.safari = true;
 | |
|   } else if (document.getBoxObjectFor != null) {
 | |
|     this.gecko = true;
 | |
|   }
 | |
|   // The feedControl instance for generating entry HTML.
 | |
|   this.feedControl = new google.feeds.FeedControl();
 | |
|   this.feedControl.setLinkTarget(this.options.linkTarget);
 | |
| 
 | |
|   // The feeds
 | |
|   this.expected = this.feeds.length;
 | |
|   this.errors = 0;
 | |
| 
 | |
|   for (var i = 0; i < this.feeds.length; i++) {
 | |
|     var feed = new google.feeds.Feed(this.feeds[i].url);
 | |
|     feed.setResultFormat(google.feeds.Feed.JSON_FORMAT);
 | |
|     feed.setNumEntries(this.options.numResults);
 | |
|     feed.load(this.bind_(this.feedLoaded_, i));
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Helper method to bind this instance correctly.
 | |
|  * @param {Object} method function/method to bind.
 | |
|  * @return {Function}
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.bind_ = function(method) {
 | |
|   var self = this;
 | |
|   var opt_args = [].slice.call(arguments, 1);
 | |
|   return function() {
 | |
|     var args = opt_args.concat([].slice.call(arguments));
 | |
|     return method.apply(self, args);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Callback associated with the AJAX Feed api after load.
 | |
|  * @param {Object} result Loaded result.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.feedLoaded_ = function(index, result) {
 | |
|   if (this.options.feedLoadCallback) {
 | |
|     this.options.feedLoadCallback(result);
 | |
|   }
 | |
|   if (result.error) {
 | |
|     // Ignore failed feeds for the most part.
 | |
|     // The user has control through the feedLoadCallback above
 | |
|     // if they choose to do something more createive.
 | |
|     // Only complain if we can't load anything.
 | |
|     if (++this.errors >= this.expected) {
 | |
|       this.nodes.container.innerHTML = 'Feed' + ((this.expected > 1)?'s ':' ') +
 | |
|                                        'could not be loaded.';
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
|   // Override of title option.
 | |
|   if (this.feeds[index].title) {
 | |
|     result.feed.title = this.feeds[index].title;
 | |
|   }
 | |
|   this.results.push(result);
 | |
| 
 | |
|   if (!this.started) {
 | |
|     this.createSubContainers_();
 | |
|     this.displayResult_(0);
 | |
|   } else if (!this.options.horizontal && this.options.stacked) {
 | |
|     this.addResult_(this.results.length-1);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Insert blog in correct place
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.sortByDate_ = function(resultIndex, newTitle,
 | |
|                                                       newList) {
 | |
|   // if sorting by date, insert it into the correct spot
 | |
|   var newEntryDate = this.results[resultIndex].feed.entries[0].publishedDate;
 | |
|   var newEntryDateMS = Date.parse(newEntryDate);
 | |
|   var insertIndex = null;
 | |
| 
 | |
|   for (var i = 0; i < this.results.length; i++) {
 | |
|     var mostRecentPost = this.results[i].feed.entries[0].publishedDate;
 | |
|     var mostRecentPostMS = Date.parse(mostRecentPost);
 | |
|     if(newEntryDateMS > mostRecentPostMS) {
 | |
|       insertIndex = i;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If it's most stale blog, just append as usual
 | |
|   if(insertIndex == null) {
 | |
|     this.nodes.root.appendChild(newTitle);
 | |
|     this.nodes.root.appendChild(newList);
 | |
|     this.createListEntries_(resultIndex, newList);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If it is fresher than a blog, insert to correct position
 | |
|   var insertBeforeIndex = 2 + (insertIndex * 2);
 | |
|   var swapToIndex = insertBeforeIndex + 2;
 | |
|   var tempSwap = null;
 | |
|   var myResultIndex = resultIndex + 1;
 | |
| 
 | |
|   var sectionsToChange = this.nodes.root.childNodes;
 | |
|   var nodeToInsertBefore = sectionsToChange[insertBeforeIndex];
 | |
| 
 | |
|   this.nodes.root.insertBefore(newTitle, nodeToInsertBefore);
 | |
|   this.nodes.root.insertBefore(newList, nodeToInsertBefore);
 | |
| 
 | |
|   this.results.splice(insertIndex, 0, this.results[resultIndex]);
 | |
|   this.results.splice(myResultIndex, 1);
 | |
|   
 | |
|   var nodesToChangeClick = sectionsToChange[swapToIndex].nextSibling.childNodes;
 | |
|   
 | |
|   this.createListEntries_(insertIndex, newList);
 | |
| 
 | |
|   // Keep freshest blog open first
 | |
|   if(insertIndex == 0) {
 | |
|     this.displayResult_(0);
 | |
|   }
 | |
| 
 | |
|   insertIndex += 1;
 | |
|   // Reset all of the onmousehover listeners to highlight corect index
 | |
|   for (var i = swapToIndex; i < sectionsToChange.length; i += 2) {
 | |
|     var nodesToChangeClick = sectionsToChange[i].nextSibling.childNodes;
 | |
|     for (var j=0; j < nodesToChangeClick.length; j++) {
 | |
|       nodesToChangeClick[j].onmouseover = this.bind_(this.listMouseOver_, 
 | |
|                                                      insertIndex, j);
 | |
|       nodesToChangeClick[j].onmouseout = this.bind_(this.listMouseOut_, 
 | |
|                                                     insertIndex, j);
 | |
|     }
 | |
|     insertIndex++;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Setup to display the Result for stacked mode
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.addResult_ = function(resultIndex) {
 | |
|   var result = this.results[resultIndex];
 | |
|   var newTitle = this.createDiv_('gfg-subtitle');
 | |
|   this.setTitle_(result.feed, newTitle);
 | |
|   var newList = this.createDiv_('gfg-list');
 | |
| 
 | |
|   // add a collapseable button
 | |
|   if(this.options.collapseable) {
 | |
|     var collapseLink = document.createElement('div');
 | |
|     newList.style.display = 'none';
 | |
|     collapseLink.className = 'gfg-collapse-closed';
 | |
|     newTitle.appendChild(collapseLink);
 | |
|     collapseLink.onclick = this.toggleCollapse(this, newList, collapseLink);
 | |
|     this.collapseElements.push({
 | |
|       list : newList,
 | |
|       collapse : collapseLink
 | |
|     });
 | |
|   }
 | |
| 
 | |
| 
 | |
|   var clearFloat = document.createElement('div');
 | |
|   clearFloat.className = 'clearFloat';
 | |
|   newTitle.appendChild(clearFloat);
 | |
| 
 | |
|   // If not sorting by date, add them as usual
 | |
|   if(!this.options.sortByDate) {
 | |
|     this.nodes.root.appendChild(newTitle);
 | |
|     this.nodes.root.appendChild(newList);
 | |
|     this.createListEntries_(resultIndex, newList);
 | |
|   } else {
 | |
|     this.sortByDate_(resultIndex, newTitle, newList);
 | |
|   }
 | |
|   
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Setup to display the Result
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.displayResult_ = function(resultIndex) {
 | |
|   this.resultIndex = resultIndex;
 | |
|   var result = this.results[resultIndex];
 | |
|   if (this.options.feedTransitionCallback) {
 | |
|     this.options.feedTransitionCallback(result);
 | |
|   }
 | |
|   if (this.options.title) {
 | |
|     this.setPlainTitle_(this.options.title);
 | |
|   } else {
 | |
|     this.setTitle_(result.feed);
 | |
|   }
 | |
|   this.clearNode_(this.nodes.entry);
 | |
| 
 | |
|   if (this.started && !this.options.horizontal && this.options.stacked) {
 | |
|     this.entries = result.feed.entries;
 | |
|   } else {
 | |
|     this.createListEntries_(resultIndex, this.nodes.list);
 | |
|   }
 | |
|   this.displayEntries_();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the Title to just plaintext
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.setPlainTitle_ = function(title, opt_element) {
 | |
|   var el = opt_element || this.nodes.title;
 | |
|   el.innerHTML = title;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the Title
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.setTitle_ = function(resultFeed, opt_element) {
 | |
|   var el = opt_element || this.nodes.title;
 | |
|   this.clearNode_(el);
 | |
|   var link = document.createElement('a');
 | |
|   link.target = google.feeds.LINK_TARGET_BLANK;
 | |
|   link.href = resultFeed.link;
 | |
|   link.className = 'gfg-collapse-href';
 | |
|   link.innerHTML = resultFeed.title;
 | |
|   el.appendChild(link);
 | |
| }
 | |
| 
 | |
| GFdynamicFeedControl.prototype.toggleCollapse = function(feedControl, 
 | |
|                                                          listReference, 
 | |
|                                                          collapseLink) {
 | |
|   return function() {
 | |
|     var els = feedControl.collapseElements;
 | |
|     for (var i=0; i < els.length; i++) {
 | |
|       var el = els[i];
 | |
|       el.list.style.display = 'none';
 | |
|       el.collapse.className = 'gfg-collapse-closed';
 | |
|     }
 | |
|     listReference.style.display = 'block';
 | |
|     collapseLink.className = 'gfg-collapse-open';
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create the list Entries
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.createListEntries_ = function(resultIndex, node) {
 | |
|   var entries = this.results[resultIndex].feed.entries;
 | |
|   this.clearNode_(node);
 | |
|   for (var i = 0; i < entries.length; i++) {
 | |
|     this.feedControl.createHtml(entries[i]);
 | |
|     var className = 'gfg-listentry ';
 | |
|     className += (i%2)?'gfg-listentry-even':'gfg-listentry-odd';
 | |
|     var listEntry = this.createDiv_(className);
 | |
|     var link = this.createLink_(entries[i].link,
 | |
|                                 entries[i].title,
 | |
|                                 this.options.linkTarget);
 | |
|     listEntry.appendChild(link);
 | |
|     if (this.options.pauseOnHover) {
 | |
|       listEntry.onmouseover = this.bind_(this.listMouseOver_, resultIndex, i);
 | |
|       listEntry.onmouseout = this.bind_(this.listMouseOut_, resultIndex, i);
 | |
|     }
 | |
|     entries[i].listEntry = listEntry;
 | |
|     node.appendChild(listEntry);
 | |
|   }
 | |
|   if (node == this.nodes.list) {
 | |
|     this.entries = entries;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Begin to display the entries.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.displayEntries_ = function() {
 | |
|   this.entryIndex = 0;
 | |
|   this.displayCurrentEntry_();
 | |
|   this.setDisplayTimer_();
 | |
|   this.started = true;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Display next entry.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.displayNextEntry_ = function() {
 | |
|   // Check to see if we have been orphaned and need to cleanup..
 | |
|   if (this.options.autoCleanup && this.isOrphaned_()) {
 | |
|       this.cleanup_();
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   if (++this.entryIndex >= this.entries.length) {
 | |
|     // End of list, see if we should rotate feeds..
 | |
|     if (this.results.length > 1) {
 | |
|       if (++this.resultIndex >= this.results.length) {
 | |
|         this.resultIndex = 0;
 | |
|       }
 | |
|       this.displayResult_(this.resultIndex);
 | |
|       return;
 | |
|     } else {
 | |
|       this.entryIndex = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (this.options.transitionCallback) {
 | |
|     this.options.transitionCallback(this.entries[this.entryIndex]);
 | |
|   }
 | |
|   this.displayCurrentEntry_();
 | |
|   this.setDisplayTimer_();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Display current entry.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.displayCurrentEntry_ = function() {
 | |
|   this.clearNode_(this.nodes.entry);
 | |
|   this.current = this.entries[this.entryIndex].html;
 | |
|   this.current.style.top = '0px';
 | |
|   this.nodes.entry.appendChild(this.current);
 | |
|   this.createOverlay_();
 | |
|   
 | |
|   // Expand the blog who's post is being displayed
 | |
|   if(this.options.collapseable) {
 | |
|     var feedTitle = null;
 | |
|     
 | |
|     for (var i=0; i < this.results.length; i++) {
 | |
|       if(this.results[i].feed.entries == this.entries) {
 | |
|         feedTitle = this.results[i].feed.title;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     var els = this.collapseElements;
 | |
| 
 | |
|     for (var i=0; i < els.length; i++) {
 | |
|       var el = els[i];
 | |
|       var divfeedTitle = el.collapse.previousSibling.innerHTML;
 | |
|       var expandClicker = el.collapse;
 | |
|       if(feedTitle == divfeedTitle) {
 | |
|         if(this.ie) {
 | |
|           expandClicker.click();
 | |
|         } else {
 | |
|           expandClicker.onclick();
 | |
|         }
 | |
| 
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   if (this.currentList) {
 | |
|     var className = 'gfg-listentry ';
 | |
|     className += (this.currentListIndex%2)?
 | |
|         'gfg-listentry-even':'gfg-listentry-odd';
 | |
|     this.currentList.className = className;
 | |
|   }
 | |
|   this.currentList = this.entries[this.entryIndex].listEntry;
 | |
|   this.currentListIndex = this.entryIndex;
 | |
|   var className = 'gfg-listentry gfg-listentry-highlight ';
 | |
|   className += (this.currentListIndex%2)?
 | |
|        'gfg-listentry-even':'gfg-listentry-odd';
 | |
|   this.currentList.className = className;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Simulated mouse hover events for list entries.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.listMouseHover_ = function(resultIndex,
 | |
|                                                           listIndex) {
 | |
|   var result = this.results[resultIndex];
 | |
|   var listEntry = result.feed.entries[listIndex].listEntry;
 | |
|   listEntry.selectTimer = null;
 | |
|   this.clearTransitionTimer_();
 | |
|   this.clearDisplayTimer_();
 | |
|   this.resultIndex = resultIndex;
 | |
|   this.entries = result.feed.entries;
 | |
|   this.entryIndex = listIndex;
 | |
|   this.displayCurrentEntry_();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Mouse over events for list entries.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.listMouseOver_ = function(resultIndex,
 | |
|                                                          listIndex) {
 | |
|   var result = this.results[resultIndex];
 | |
|   var listEntry = result.feed.entries[listIndex].listEntry;
 | |
|   var cb = this.bind_(this.listMouseHover_, resultIndex, listIndex);
 | |
|   listEntry.selectTimer = setTimeout(cb, this.options.hoverTime);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Mouse out events for list entries.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.listMouseOut_ = function(resultIndex, listIndex) {
 | |
|   var result = this.results[resultIndex];
 | |
|   var listEntry = result.feed.entries[listIndex].listEntry;
 | |
|   if (listEntry.selectTimer) {
 | |
|     clearTimeout(listEntry.selectTimer);
 | |
|     listEntry.selectTimer = null;
 | |
|   } else {
 | |
|     this.setDisplayTimer_();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Mouse over events for main entry.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.entryMouseOver_ = function(e) {
 | |
|   this.clearDisplayTimer_();
 | |
|   if (this.transitionTimer) {
 | |
|     this.clearTransitionTimer_();
 | |
|     this.displayCurrentEntry_();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Mouse out events for main entry.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.entryMouseOut_ = function(e) {
 | |
|   this.setDisplayTimer_();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Create the overlay div. This hack is for IE and transparency effects.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.createOverlay_ = function() {
 | |
|   if (this.current == null) return;
 | |
|   // Create div lazily and hold on to it..
 | |
|   if (this.overlay == null) {
 | |
|     var overlay = this.createDiv_('gfg-entry');
 | |
|     overlay.style.position = 'absolute';
 | |
|     overlay.style.top = '0px';
 | |
|     overlay.style.left = '0px';
 | |
|     this.overlay = overlay;
 | |
|   }
 | |
|   this.setOpacity_(this.overlay, 0);
 | |
|   this.nodes.entry.appendChild(this.overlay);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Sets the display timer.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.setDisplayTimer_ = function() {
 | |
|   if (this.displayTimer) {
 | |
|     this.clearDisplayTimer_();
 | |
|   }
 | |
|   var cb = this.bind_(this.setFadeOutTimer_);
 | |
|   this.displayTimer = setTimeout(cb, this.options.displayTime);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Class helper method for the time now in milliseconds
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.timeNow = function() {
 | |
|   var d = new Date();
 | |
|   return d.getTime();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Transition animation for fadeout. Cleanup when finished.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.fadeOutEntry_ = function() {
 | |
|   if (this.overlay) {
 | |
|     var delta = this.fadeOutDelta;
 | |
|     var ts = this.options.transitionStep;
 | |
|     var now = GFdynamicFeedControl.timeNow();
 | |
|     var tick = now - this.lastTick;
 | |
|     this.lastTick = now;
 | |
|     delta *= (tick/ts);
 | |
| 
 | |
|     var op = this.overlay.opacity + delta;
 | |
|     // Overlay opacity
 | |
|     this.setOpacity_(this.overlay, op);
 | |
|     // Scroll down
 | |
|     if (this.options.scrollOnFadeOut && (op > .5)) {
 | |
|       var r = (op-.5)*2;
 | |
|       var newTop = Math.round(this.current.offsetHeight * r);
 | |
|       this.current.style.top = newTop + 'px';
 | |
|     }
 | |
|     if (op < 1) return;
 | |
|   }
 | |
|   // Finished.
 | |
|   this.clearTransitionTimer_();
 | |
|   this.displayNextEntry_();
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Sets the transition timer for fadeout.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.setFadeOutTimer_ = function() {
 | |
|   this.clearTransitionTimer_();
 | |
|   this.lastTick = GFdynamicFeedControl.timeNow();
 | |
|   var cb = this.bind_(this.fadeOutEntry_);
 | |
|   this.transitionTimer = setInterval(cb, this.options.transitionStep);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Clear the transition timer. Used to prevent leaks.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.clearTransitionTimer_ = function() {
 | |
|   if (this.transitionTimer) {
 | |
|     clearInterval(this.transitionTimer);
 | |
|     this.transitionTimer = null;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Clear the display timer.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.clearDisplayTimer_ = function() {
 | |
|   if (this.displayTimer) {
 | |
|     clearTimeout(this.displayTimer);
 | |
|     this.displayTimer = null;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Setup our own subcontainer to the user supplied container.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.createSubContainers_ = function() {
 | |
|   var nodes = this.nodes;
 | |
|   var container = this.nodes.container;
 | |
| 
 | |
|   this.clearNode_(container);
 | |
|   if (this.options.horizontal) {
 | |
|     container = this.createDiv_('gfg-horizontal-container');
 | |
|     nodes.root = this.createDiv_('gfg-horizontal-root');
 | |
|     this.nodes.container.appendChild(container);
 | |
|   } else {
 | |
|     nodes.root = this.createDiv_('gfg-root');
 | |
|   }
 | |
|   nodes.title = this.createDiv_('gfg-title');
 | |
|   nodes.entry = this.createDiv_('gfg-entry');
 | |
|   nodes.list = this.createDiv_('gfg-list');
 | |
|   nodes.root.appendChild(nodes.title);
 | |
|   nodes.root.appendChild(nodes.entry);
 | |
| 
 | |
|   if (!this.options.horizontal && this.options.stacked) {
 | |
|     var newTitle = this.createDiv_('gfg-subtitle');
 | |
|     nodes.root.appendChild(newTitle);
 | |
|     this.setTitle_(this.results[0].feed, newTitle);
 | |
|     
 | |
|     if(this.options.collapseable) {
 | |
|       var collapseLink = document.createElement('div');
 | |
|       collapseLink.className = 'gfg-collapse-open';
 | |
|       newTitle.appendChild(collapseLink);
 | |
|       collapseLink.onclick = this.toggleCollapse(this, nodes.list, collapseLink);
 | |
|       this.collapseElements.push({
 | |
|         list : nodes.list,
 | |
|         collapse : collapseLink
 | |
|       });
 | |
|       nodes.list.style.display = 'block';
 | |
|     }
 | |
|     
 | |
|     var clearFloat = document.createElement('div');
 | |
|     clearFloat.className = 'clearFloat';
 | |
|     newTitle.appendChild(clearFloat);
 | |
|   }
 | |
|   
 | |
|   nodes.root.appendChild(nodes.list);
 | |
|   container.appendChild(nodes.root);
 | |
| 
 | |
|   if (this.options.pauseOnHover) {
 | |
|     nodes.entry.onmouseover = this.bind_(this.entryMouseOver_);
 | |
|     nodes.entry.onmouseout = this.bind_(this.entryMouseOut_);
 | |
|   }
 | |
| 
 | |
|   // Add Branding.
 | |
|   if (this.options.horizontal) {
 | |
|     nodes.branding = this.createDiv_('gfg-branding');
 | |
|     google.feeds.getBranding(nodes.branding, google.feeds.VERTICAL_BRANDING);
 | |
|     container.appendChild(nodes.branding);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Helper method to properly clear a node and its children.
 | |
|  * @param {Object} node Node to clear.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.clearNode_ = function(node) {
 | |
|   if (node == null) return;
 | |
|   var child;
 | |
|   while ((child = node.firstChild)) {
 | |
|     node.removeChild(child);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Helper method to create a div with optional class and text.
 | |
|  * @param {string} opt_className Optional className for the div.
 | |
|  * @param {string} opt_text Optional text for the innerHTML.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.createDiv_ = function(opt_className, opt_text) {
 | |
|   var el = document.createElement("div");
 | |
|   if (opt_text) {
 | |
|     el.innerHTML = opt_text;
 | |
|   }
 | |
|   if (opt_className) { el.className = opt_className; }
 | |
|   return el;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Helper method to create a link with href and text.
 | |
|  * @param {string} href Href URL
 | |
|  * @param {string} text text for the link.
 | |
|  * @param {string} opt_target Optional link target.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.createLink_ = function(href, text, opt_target) {
 | |
|   var link = document.createElement('a');
 | |
|   link.href = href;
 | |
|   link.innerHTML = text;
 | |
|   if (opt_target) {
 | |
|     link.target = opt_target;
 | |
|   }
 | |
|   return link;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Cleanup results on being orphaned.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.clearResults_ = function() {
 | |
|   for (var i=0; i < this.results.length; i++) {
 | |
|     var result = this.results[i];
 | |
|     var entries = result.feed.entries;
 | |
|     for (var i = 0; i < entries.length; i++) {
 | |
|       var entry = entries[i];
 | |
|       entry.html = null;
 | |
|       entry.listEntry.onmouseover = null;
 | |
|       entry.listEntry.onmouseout = null;
 | |
|       if (entry.listEntry.selectTimer) {
 | |
|         clearTimeout(entry.listEntry.selectTimer);
 | |
|         entry.listEntry.selectTimer = null;
 | |
|       }
 | |
|       entry.listEntry = null;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check for being orphaned.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.isOrphaned_ = function() {
 | |
|   var root = this.nodes.root;
 | |
|   var orphaned = false;
 | |
|   if (!root || !root.parentNode) {
 | |
|     orphaned = true;
 | |
|   } else if (this.options.horizontal && !root.parentNode.parentNode) {
 | |
|     orphaned = true;
 | |
|   }
 | |
|   return orphaned;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Cleanup on being orphaned.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.cleanup_ = function() {
 | |
|   this.started = false;
 | |
|   // Timer Events.
 | |
|   this.clearDisplayTimer_();
 | |
|   this.clearTransitionTimer_();
 | |
|   // Structures
 | |
|   this.clearResults_();
 | |
|   // Nodes
 | |
|   this.clearNode_(this.nodes.root);
 | |
|   this.nodes.container = null;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Helper method to set opacity for nodes.. Also takes into account
 | |
|  * visibility in general.
 | |
|  * @param {Element} node element.
 | |
|  * @param {Number} opacity alpha level.
 | |
|  * @private
 | |
|  */
 | |
| GFdynamicFeedControl.prototype.setOpacity_ = function(node, opacity) {
 | |
|   if (node == null) return;
 | |
|   opacity = Math.max(0, Math.min(1, opacity));
 | |
|   if (opacity == 0) {
 | |
|     if (node.style.visibility != 'hidden') {
 | |
|       node.style.visibility = 'hidden';
 | |
|     }
 | |
|   } else {
 | |
|     if (node.style.visibility != 'visible') {
 | |
|       node.style.visibility = 'visible';
 | |
|     }
 | |
|   }
 | |
|   if (this.ie) {
 | |
|     var normalized = Math.round(opacity*100);
 | |
|     node.style.filter = 'alpha(opacity=' + normalized + ')';
 | |
|   }
 | |
|   node.style.opacity = node.opacity = opacity;
 | |
| };
 | |
| 
 | |
| GFgadget = GFdynamicFeedControl;
 | |
| 
 |