FeedPortlet.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. // wrapped by build app
  2. define("dojox/widget/FeedPortlet", ["dijit","dojo","dojox","dojo/require!dojox/widget/Portlet,dijit/Tooltip,dijit/form/TextBox,dijit/form/Button,dojox/data/GoogleFeedStore"], function(dijit,dojo,dojox){
  3. dojo.provide("dojox.widget.FeedPortlet");
  4. dojo.require("dojox.widget.Portlet");
  5. dojo.require("dijit.Tooltip");
  6. dojo.require("dijit.form.TextBox");
  7. dojo.require("dijit.form.Button");
  8. dojo.require("dojox.data.GoogleFeedStore");
  9. dojo.declare("dojox.widget.FeedPortlet", dojox.widget.Portlet, {
  10. // summary:
  11. // A Portlet that loads a XML feed.
  12. // description: The feed is displayed as
  13. // an unordered list of links. When a link is hovered over
  14. // by the mouse, it displays a summary in a tooltip.
  15. // local: Boolean
  16. // Specifies whether the feed is to be loaded from the same domain as the
  17. // page, or a remote domain. If local is true, then the feed must be an
  18. // Atom feed. If it is false, it can be an Atom or RSS feed.
  19. local: false,
  20. // maxResults: Number
  21. // The number of results to display from the feed.
  22. maxResults: 5,
  23. // url: String
  24. // The URL of the feed to load. If this is different to the domain
  25. // of the HTML page, local should be set to false.
  26. url: "",
  27. // openNew: Boolean
  28. // If true, when a link is clicked it will open in a new window.
  29. // If false, it will not.
  30. openNew: true,
  31. // useFeedTitle: Boolean
  32. // If true, the title of the loaded feed is displayed in the title bar of the portlet.
  33. // If false, the title remains unchanged.
  34. showFeedTitle: true,
  35. postCreate: function(){
  36. this.inherited(arguments);
  37. if(this.local && !dojox.data.AtomReadStore){
  38. throw Error(this.declaredClass + ": To use local feeds, you must include dojox.data.AtomReadStore on the page.");
  39. }
  40. },
  41. onFeedError: function(){
  42. // summary:
  43. // Called when a feed fails to load successfully.
  44. this.containerNode.innerHTML = "Error accessing the feed."
  45. },
  46. addChild: function(child){
  47. this.inherited(arguments);
  48. var url = child.attr("feedPortletUrl");
  49. if(url){
  50. this.set("url", url);
  51. }
  52. },
  53. _getTitle: function(item){
  54. // summary:
  55. // Gets the title of a feed item.
  56. var t = this.store.getValue(item, "title");
  57. return this.local ? t.text : t;
  58. },
  59. _getLink: function(item){
  60. // summary:
  61. // Gets the href link of a feed item.
  62. var l = this.store.getValue(item, "link");
  63. return this.local ? l.href : l;
  64. },
  65. _getContent: function(item){
  66. // summary:
  67. // Gets the summary of a feed item.
  68. var c = this.store.getValue(item, "summary");
  69. if(!c){
  70. return null;
  71. }
  72. if(this.local){
  73. c = c.text;
  74. }
  75. // Filter out any sneaky scripts in the code
  76. c = c.split("<script").join("<!--").split("</script>").join("-->");
  77. c = c.split("<iframe").join("<!--").split("</iframe>").join("-->");
  78. return c;
  79. },
  80. _setUrlAttr: function(url){
  81. // summary:
  82. // Sets the URL to load.
  83. this.url = url;
  84. if(this._started){
  85. this.load();
  86. }
  87. },
  88. startup: function(){
  89. // summary:
  90. // Loads the widget.
  91. if(this.started || this._started){return;}
  92. this.inherited(arguments);
  93. if(!this.url || this.url == ""){
  94. throw new Error(this.id + ": A URL must be specified for the feed portlet");
  95. }
  96. if(this.url && this.url != ""){
  97. this.load();
  98. }
  99. },
  100. load: function(){
  101. // summary:
  102. // Loads the feed.
  103. if(this._resultList){
  104. dojo.destroy(this._resultList);
  105. }
  106. var store, query;
  107. // If the feed is on the same domain, use the AtomReadStore,
  108. // as we cannot be guaranteed that it will be available to
  109. // Google services.
  110. if(this.local){
  111. store = new dojox.data.AtomReadStore({
  112. url: this.url
  113. });
  114. query = {};
  115. }else{
  116. store = new dojox.data.GoogleFeedStore();
  117. query = {url: this.url};
  118. }
  119. var request = {
  120. query: query,
  121. count: this.maxResults,
  122. onComplete: dojo.hitch(this, function(items){
  123. if (this.showFeedTitle && store.getFeedValue) {
  124. var title = this.store.getFeedValue("title");
  125. if(title){
  126. this.set("title", title.text ? title.text : title);
  127. }
  128. }
  129. this.generateResults(items);
  130. }),
  131. onError: dojo.hitch(this, "onFeedError")
  132. };
  133. this.store = store;
  134. store.fetch(request);
  135. },
  136. generateResults: function (items){
  137. // summary:
  138. // Generates a list of hyperlinks and displays a tooltip
  139. // containing a summary when the mouse hovers over them.
  140. var store = this.store;
  141. var timer;
  142. var ul = (this._resultList =
  143. dojo.create("ul", {"class" : "dojoxFeedPortletList"}, this.containerNode));
  144. dojo.forEach(items, dojo.hitch(this, function(item){
  145. var li = dojo.create("li", {
  146. innerHTML: '<a href="'
  147. + this._getLink(item)
  148. + '"'
  149. + (this.openNew ? ' target="_blank"' : '')
  150. +'>'
  151. + this._getTitle(item) + '</a>'
  152. },ul);
  153. dojo.connect(li, "onmouseover", dojo.hitch(this, function(evt){
  154. if(timer){
  155. clearTimeout(timer);
  156. }
  157. // Show the tooltip after the mouse has been hovering
  158. // for a short time.
  159. timer = setTimeout(dojo.hitch(this, function(){
  160. timer = null;
  161. var summary = this._getContent(item);
  162. if(!summary){return;}
  163. var content = '<div class="dojoxFeedPortletPreview">'
  164. + summary + '</div>'
  165. dojo.query("li", ul).forEach(function(item){
  166. if(item != evt.target){
  167. dijit.hideTooltip(item);
  168. }
  169. });
  170. // Hover the tooltip over the anchor tag
  171. dijit.showTooltip(content, li.firstChild, !this.isLeftToRight());
  172. }), 500);
  173. }));
  174. // Hide the tooltip when the mouse leaves a list item.
  175. dojo.connect(li, "onmouseout", function(){
  176. if(timer){
  177. clearTimeout(timer);
  178. timer = null;
  179. }
  180. dijit.hideTooltip(li.firstChild);
  181. });
  182. }));
  183. this.resize();
  184. }
  185. });
  186. dojo.declare("dojox.widget.ExpandableFeedPortlet", dojox.widget.FeedPortlet, {
  187. // summary:
  188. // A FeedPortlet that uses an list of expandable links to display
  189. // a feed. An icon is placed to the left of each item
  190. // which, when clicked, toggles the visible state
  191. // of the item summary.
  192. // onlyOpenOne: Boolean
  193. // If true, only a single item can be expanded at any given time.
  194. onlyOpenOne: false,
  195. generateResults: function(items){
  196. // summary:
  197. // Generates a list of items, and places an icon beside them that
  198. // can be used to show or hide a summary of that item.
  199. var store = this.store;
  200. var iconCls = "dojoxPortletToggleIcon";
  201. var collapsedCls = "dojoxPortletItemCollapsed";
  202. var expandedCls = "dojoxPortletItemOpen";
  203. var timer;
  204. var ul = (this._resultList = dojo.create("ul", {
  205. "class": "dojoxFeedPortletExpandableList"
  206. }, this.containerNode));
  207. // Create the LI elements. Each LI has two DIV elements, the
  208. // top DIV contains the toggle icon and title, and the bottom
  209. // div contains the extended summary.
  210. dojo.forEach(items, dojo.hitch(this, dojo.hitch(this, function(item){
  211. var li = dojo.create("li", {"class": collapsedCls}, ul);
  212. var upper = dojo.create("div", {style: "width: 100%;"}, li);
  213. var lower = dojo.create("div", {"class": "dojoxPortletItemSummary", innerHTML: this._getContent(item)}, li);
  214. dojo.create("span", {
  215. "class": iconCls,
  216. innerHTML: "<img src='" + dojo.config.baseUrl + "/resources/blank.gif'>"}, upper);
  217. var a = dojo.create("a", {href: this._getLink(item), innerHTML: this._getTitle(item) }, upper);
  218. if(this.openNew){
  219. dojo.attr(a, "target", "_blank");
  220. }
  221. })));
  222. // Catch all clicks on the list. If a toggle icon is clicked,
  223. // toggle the visible state of the summary DIV.
  224. dojo.connect(ul, "onclick", dojo.hitch(this, function(evt){
  225. if(dojo.hasClass(evt.target, iconCls) || dojo.hasClass(evt.target.parentNode, iconCls)){
  226. dojo.stopEvent(evt);
  227. var li = evt.target.parentNode;
  228. while(li.tagName != "LI"){
  229. li = li.parentNode;
  230. }
  231. if(this.onlyOpenOne){
  232. dojo.query("li", ul).filter(function(item){
  233. return item != li;
  234. }).removeClass(expandedCls).addClass(collapsedCls);
  235. }
  236. var isExpanded = dojo.hasClass(li, expandedCls);
  237. dojo.toggleClass(li, expandedCls, !isExpanded);
  238. dojo.toggleClass(li, collapsedCls, isExpanded);
  239. }
  240. }));
  241. }
  242. });
  243. dojo.declare("dojox.widget.PortletFeedSettings",
  244. dojox.widget.PortletSettings, {
  245. // summary:
  246. // A Settings widget designed to be used with a dojox.widget.FeedPortlet
  247. // description:
  248. // It provides form items that the user can use to change the URL
  249. // for a feed to load into the FeedPortlet.
  250. // There are two forms that it can take. <br>
  251. // The first is to display a text field, with Load and Cancel buttons,
  252. // which is prepopulated with the enclosing FeedPortlet's URL.
  253. // If a <select> DOM node is used as the source node for this widget,
  254. // it displays a list of predefined URLs that the user can select from
  255. // to load into the enclosing FeedPortlet.
  256. //
  257. // example:
  258. // <div dojoType="dojox.widget.PortletFeedSettings"></div>
  259. //
  260. // example:
  261. // <select dojoType="dojox.widget.PortletFeedSettings">
  262. // <option>http://www.dojotoolkit.org/aggregator/rss</option>
  263. // <option>http://dojocampus.org/content/category/podcast/feed/</option>
  264. // </select>
  265. "class" : "dojoxPortletFeedSettings",
  266. // urls: Array
  267. // An array of JSON object specifying URLs to display in the
  268. // PortletFeedSettings object. Each object contains a 'url' and 'label'
  269. // attribute, e.g.
  270. // [{url:'http:google.com', label:'Google'}, {url:'http://dojotoolkit.org', label: 'Dojo'}]
  271. urls: null,
  272. // selectedIndex: Number
  273. // The selected URL. Defaults to zero.
  274. selectedIndex: 0,
  275. buildRendering: function(){
  276. // If JSON URLs have been specified, create a SELECT DOM node,
  277. // and insert the required OPTION elements.
  278. var s;
  279. if(this.urls && this.urls.length > 0){
  280. console.log(this.id + " -> creating select with urls ", this.urls)
  281. s = dojo.create("select");
  282. if(this.srcNodeRef){
  283. dojo.place(s, this.srcNodeRef, "before");
  284. dojo.destroy(this.srcNodeRef);
  285. }
  286. this.srcNodeRef = s;
  287. dojo.forEach(this.urls, function(url){
  288. dojo.create("option", {value: url.url || url, innerHTML: url.label || url}, s);
  289. });
  290. }
  291. // If the srcNodeRef is a SELECT node, then replace it with a DIV, and insert
  292. // the SELECT node into that div.
  293. if(this.srcNodeRef.tagName == "SELECT"){
  294. this.text = this.srcNodeRef;
  295. var div = dojo.create("div", {}, this.srcNodeRef, "before");
  296. div.appendChild(this.text);
  297. this.srcNodeRef = div;
  298. dojo.query("option", this.text).filter("return !item.value;").forEach("item.value = item.innerHTML");
  299. if(!this.text.value){
  300. if(this.content && this.text.options.length == 0){
  301. this.text.appendChild(this.content);
  302. }
  303. dojo.attr(s || this.text, "value", this.text.options[this.selectedIndex].value);
  304. }
  305. }
  306. this.inherited(arguments);
  307. },
  308. _setContentAttr: function(){
  309. },
  310. postCreate: function(){
  311. console.log(this.id + " -> postCreate");
  312. if(!this.text){
  313. // If a select node is not being used, create a new TextBox to
  314. // edit the URL.
  315. var text = this.text = new dijit.form.TextBox({});
  316. dojo.create("span", {
  317. innerHTML: "Choose Url: "
  318. }, this.domNode);
  319. this.addChild(text);
  320. }
  321. // Add a LOAD button
  322. this.addChild(new dijit.form.Button({
  323. label: "Load",
  324. onClick: dojo.hitch(this, function(){
  325. // Set the URL of the containing Portlet with the selected URL.
  326. this.portlet.attr("url",
  327. (this.text.tagName == "SELECT") ? this.text.value : this.text.attr('value'));
  328. if(this.text.tagName == "SELECT"){
  329. // Set the selected index on the Select node.
  330. dojo.some(this.text.options, dojo.hitch(this, function(opt, idx){
  331. if(opt.selected){
  332. this.set("selectedIndex", idx);
  333. return true;
  334. }
  335. return false;
  336. }));
  337. }
  338. // Hide the widget.
  339. this.toggle();
  340. })
  341. }));
  342. // Add a CANCEL button, which hides this widget
  343. this.addChild(new dijit.form.Button({
  344. label: "Cancel",
  345. onClick: dojo.hitch(this, "toggle")
  346. }));
  347. this.inherited(arguments);
  348. },
  349. startup: function(){
  350. // summary:
  351. // Sets the portlet associated with this PortletSettings object.
  352. if(this._started){return;}
  353. console.log(this.id + " -> startup");
  354. this.inherited(arguments);
  355. if(!this.portlet){
  356. throw Error(this.declaredClass + ": A PortletFeedSettings widget cannot exist without a Portlet.");
  357. }
  358. if(this.text.tagName == "SELECT"){
  359. // Set the initial selected option.
  360. dojo.forEach(this.text.options, dojo.hitch(this, function(opt, index){
  361. dojo.attr(opt, "selected", index == this.selectedIndex);
  362. }));
  363. }
  364. var url = this.portlet.attr("url");
  365. if(url){
  366. // If a SELECT node is used to choose a URL, ensure that the Portlet's URL
  367. // is one of the options.
  368. if(this.text.tagName == "SELECT"){
  369. if(!this.urls && dojo.query("option[value='" + url + "']", this.text).length < 1){
  370. dojo.place(dojo.create("option", {
  371. value: url,
  372. innerHTML: url,
  373. selected: "true"
  374. }), this.text, "first");
  375. }
  376. }else{
  377. this.text.attr("value", url);
  378. }
  379. }else{
  380. this.portlet.attr("url", this.get("feedPortletUrl"));
  381. }
  382. },
  383. _getFeedPortletUrlAttr: function(){
  384. return this.text.value;
  385. }
  386. });
  387. });