FeedViewer.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. require({cache:{
  2. 'url:dojox/atom/widget/templates/FeedViewer.html':"<div class=\"feedViewerContainer\" dojoAttachPoint=\"feedViewerContainerNode\">\n\t<table cellspacing=\"0\" cellpadding=\"0\" class=\"feedViewerTable\">\n\t\t<tbody dojoAttachPoint=\"feedViewerTableBody\" class=\"feedViewerTableBody\">\n\t\t</tbody>\n\t</table>\n</div>\n",
  3. 'url:dojox/atom/widget/templates/FeedViewerEntry.html':"<tr class=\"feedViewerEntry\" dojoAttachPoint=\"entryNode\" dojoAttachEvent=\"onclick:onClick\">\n <td class=\"feedViewerEntryUpdated\" dojoAttachPoint=\"timeNode\">\n </td>\n <td>\n <table border=\"0\" width=\"100%\" dojoAttachPoint=\"titleRow\">\n <tr padding=\"0\" border=\"0\">\n <td class=\"feedViewerEntryTitle\" dojoAttachPoint=\"titleNode\">\n </td>\n <td class=\"feedViewerEntryDelete\" align=\"right\">\n <span dojoAttachPoint=\"deleteButton\" dojoAttachEvent=\"onclick:deleteEntry\" class=\"feedViewerDeleteButton\" style=\"display:none;\">[delete]</span>\n </td>\n <tr>\n </table>\n </td>\n</tr>",
  4. 'url:dojox/atom/widget/templates/FeedViewerGrouping.html':"<tr dojoAttachPoint=\"groupingNode\" class=\"feedViewerGrouping\">\n\t<td colspan=\"2\" dojoAttachPoint=\"titleNode\" class=\"feedViewerGroupingTitle\">\n\t</td>\n</tr>"}});
  5. define("dojox/atom/widget/FeedViewer", [
  6. "dojo/_base/kernel",
  7. "dojo/_base/lang",
  8. "dojo/_base/array",
  9. "dojo/_base/connect",
  10. "dojo/dom-class",
  11. "dijit/_Widget",
  12. "dijit/_Templated",
  13. "dijit/_Container",
  14. "../io/Connection",
  15. "dojo/text!./templates/FeedViewer.html",
  16. "dojo/text!./templates/FeedViewerEntry.html",
  17. "dojo/text!./templates/FeedViewerGrouping.html",
  18. "dojo/i18n!./nls/FeedViewerEntry",
  19. "dojo/_base/declare"
  20. ], function (dojo, lang, arrayUtil, connect, domClass, _Widget, _Templated, _Container, Connection, template, entryTemplate, groupingTemplate, i18nViewer) {
  21. dojo.experimental("dojox.atom.widget.FeedViewer");
  22. var widget = dojo.getObject("dojox.atom.widget", true);
  23. widget.FeedViewer = dojo.declare(/*===== "dojox.atom.widget.FeedViewer", =====*/ [_Widget, _Templated, _Container],{
  24. // summary:
  25. // An ATOM feed viewer that allows for viewing a feed, deleting entries, and editing entries.
  26. // description:
  27. // An ATOM feed viewer that allows for viewing a feed, deleting entries, and editing entries.
  28. feedViewerTableBody: null, //The body of the feed viewer table so we can access it and populate it. Will be assigned via template.
  29. feedViewerTable: null, //The overal table container which contains the feed viewer table. Will be assigned via template.
  30. entrySelectionTopic: "", //The topic to broadcast when any entry is clicked so that a listener can pick up it and display it.
  31. url: "", //The URL to which to connect to initially on creation.
  32. xmethod: false,
  33. localSaveOnly: false,
  34. //Templates for the HTML rendering. Need to figure these out better, admittedly.
  35. templateString: template,
  36. _feed: null,
  37. _currentSelection: null, // Currently selected entry
  38. _includeFilters: null,
  39. alertsEnabled: false,
  40. postCreate: function(){
  41. // summary:
  42. // The postCreate function.
  43. // description:
  44. // The postCreate function. Creates our AtomIO object for future interactions and subscribes to the
  45. // event given in markup/creation.
  46. this._includeFilters = [];
  47. if(this.entrySelectionTopic !== ""){
  48. this._subscriptions = [dojo.subscribe(this.entrySelectionTopic, this, "_handleEvent")];
  49. }
  50. this.atomIO = new Connection();
  51. this.childWidgets = [];
  52. },
  53. startup: function(){
  54. // summary:
  55. // The startup function.
  56. // description:
  57. // The startup function. Parses the filters and sets the feed based on the given url.
  58. this.containerNode = this.feedViewerTableBody;
  59. var children = this.getDescendants();
  60. for(var i in children){
  61. var child = children[i];
  62. if(child && child.isFilter){
  63. this._includeFilters.push(new widget.FeedViewer.CategoryIncludeFilter(child.scheme, child.term, child.label));
  64. child.destroy();
  65. }
  66. }
  67. if(this.url !== ""){
  68. this.setFeedFromUrl(this.url);
  69. }
  70. },
  71. clear: function(){
  72. // summary:
  73. // Function clearing all current entries in the feed view.
  74. // description:
  75. // Function clearing all current entries in the feed view.
  76. //
  77. // returns:
  78. // Nothing.
  79. this.destroyDescendants();
  80. },
  81. setFeedFromUrl: function(/*string*/url){
  82. // summary:
  83. // Function setting the feed from a URL which to get the feed.
  84. // description:
  85. // Function setting the dojox.atom.io.model.Feed data into the view.
  86. //
  87. // url:
  88. // The URL to the feed to load.
  89. //
  90. // returns:
  91. // Nothing.
  92. if(url !== ""){
  93. if(this._isRelativeURL(url)){
  94. var baseUrl = "";
  95. if(url.charAt(0) !== '/'){
  96. baseUrl = this._calculateBaseURL(window.location.href, true);
  97. }else{
  98. baseUrl = this._calculateBaseURL(window.location.href, false);
  99. }
  100. this.url = baseUrl + url;
  101. }
  102. this.atomIO.getFeed(url,lang.hitch(this,this.setFeed));
  103. }
  104. },
  105. setFeed: function(/*object*/feed){
  106. // summary:
  107. // Function setting the dojox.atom.io.model.Feed data into the view.
  108. // description:
  109. // Function setting the dojox.atom.io.model.Feed data into the view.
  110. //
  111. // entry:
  112. // The dojox.atom.io.model.Feed object to process
  113. //
  114. // returns:
  115. // Nothing.
  116. this._feed = feed;
  117. this.clear();
  118. var entrySorter=function(a,b){
  119. var dispA = this._displayDateForEntry(a);
  120. var dispB = this._displayDateForEntry(b);
  121. if(dispA > dispB){return -1;}
  122. if(dispA < dispB){return 1;}
  123. return 0;
  124. };
  125. // This function may not be safe in different locales.
  126. var groupingStr = function(dateStr){
  127. var dpts = dateStr.split(',');
  128. dpts.pop(); // remove year and time
  129. return dpts.join(",");
  130. };
  131. var sortedEntries = feed.entries.sort(lang.hitch(this,entrySorter));
  132. if(feed){
  133. var lastSectionTitle = null;
  134. for(var i=0;i<sortedEntries.length;i++){
  135. var entry = sortedEntries[i];
  136. if(this._isFilterAccepted(entry)){
  137. var time = this._displayDateForEntry(entry);
  138. var sectionTitle = "";
  139. if(time !== null){
  140. sectionTitle = groupingStr(time.toLocaleString());
  141. if(sectionTitle === "" ){
  142. //Generally an issue on Opera with how its toLocaleString() works, so do a quick and dirty date construction M/D/Y
  143. sectionTitle = "" + (time.getMonth() + 1) + "/" + time.getDate() + "/" + time.getFullYear();
  144. }
  145. }
  146. if((lastSectionTitle === null) || (lastSectionTitle != sectionTitle)){
  147. this.appendGrouping(sectionTitle);
  148. lastSectionTitle = sectionTitle;
  149. }
  150. this.appendEntry(entry);
  151. }
  152. }
  153. }
  154. },
  155. _displayDateForEntry: function(/*object*/entry){
  156. // summary:
  157. // Internal function for determining the appropriate date to display.
  158. // description: Internal function for determining of a particular entry is editable.
  159. //
  160. // entry:
  161. // The dojox.atom.io.model.Entry object to examine.
  162. //
  163. // returns:
  164. // An appropriate date for the feed viewer display.
  165. if(entry.updated){return entry.updated;}
  166. if(entry.modified){return entry.modified;}
  167. if(entry.issued){return entry.issued;}
  168. return new Date();
  169. },
  170. appendGrouping: function(/*string*/titleText){
  171. // summary:
  172. // Function for appending a new grouping of entries to the feed view.
  173. // description:
  174. // Function for appending a grouping of entries to the feed view.
  175. //
  176. // entry:
  177. // The title of the new grouping to create on the view.
  178. //
  179. // returns:
  180. // Nothing.
  181. var entryWidget = new widget.FeedViewerGrouping({});
  182. entryWidget.setText(titleText);
  183. this.addChild(entryWidget);
  184. this.childWidgets.push(entryWidget);
  185. },
  186. appendEntry: function(/*object*/entry){
  187. // summary:
  188. // Function for appending an entry to the feed view.
  189. // description:
  190. // Function for appending an entry to the feed view.
  191. //
  192. // entry:
  193. // The dojox.atom.io.model.Entry object to append
  194. //
  195. // returns:
  196. // Nothing.
  197. var entryWidget = new widget.FeedViewerEntry({"xmethod": this.xmethod});
  198. entryWidget.setTitle(entry.title.value);
  199. entryWidget.setTime(this._displayDateForEntry(entry).toLocaleTimeString());
  200. entryWidget.entrySelectionTopic = this.entrySelectionTopic;
  201. entryWidget.feed = this;
  202. this.addChild(entryWidget);
  203. this.childWidgets.push(entryWidget);
  204. this.connect(entryWidget, "onClick", "_rowSelected");
  205. entry.domNode = entryWidget.entryNode;
  206. //Need to set up a bi-directional reference here to control events between the two.
  207. entry._entryWidget = entryWidget;
  208. entryWidget.entry = entry;
  209. },
  210. deleteEntry: function(/*object*/entryRow){
  211. // summary:
  212. // Function for deleting a row from the view
  213. // description:
  214. // Function for deleting a row from the view
  215. if(!this.localSaveOnly){
  216. this.atomIO.deleteEntry(entryRow.entry, lang.hitch(this, this._removeEntry, entryRow), null, this.xmethod);
  217. }else{
  218. this._removeEntry(entryRow, true);
  219. }
  220. dojo.publish(this.entrySelectionTopic, [{ action: "delete", source: this, entry: entryRow.entry }]);
  221. },
  222. _removeEntry: function(/*FeedViewerEntry*/ entry, /* boolean */success){
  223. // summary:
  224. // callback for when an entry is deleted from a feed.
  225. // description:
  226. // callback for when an entry is deleted from a feed.
  227. if(success){
  228. /* Check if this is the last Entry beneath the given date */
  229. var idx = arrayUtil.indexOf(this.childWidgets, entry);
  230. var before = this.childWidgets[idx-1];
  231. var after = this.childWidgets[idx+1];
  232. if( before.isInstanceOf(widget.FeedViewerGrouping) &&
  233. (after === undefined || after.isInstanceOf(widget.FeedViewerGrouping))){
  234. before.destroy();
  235. }
  236. /* Destroy the FeedViewerEntry to remove it from the view */
  237. entry.destroy();
  238. }else{}
  239. },
  240. _rowSelected: function(/*object*/evt){
  241. // summary:
  242. // Internal function for handling the selection of feed entries.
  243. // description:
  244. // Internal function for handling the selection of feed entries.
  245. //
  246. // evt:
  247. // The click event that triggered a selection.
  248. //
  249. // returns:
  250. // Nothing.
  251. var selectedNode = evt.target;
  252. while(selectedNode){
  253. if(domClass.contains(selectedNode, 'feedViewerEntry')) {
  254. break;
  255. }
  256. selectedNode = selectedNode.parentNode;
  257. }
  258. for(var i=0;i<this._feed.entries.length;i++){
  259. var entry = this._feed.entries[i];
  260. if( (selectedNode === entry.domNode) && (this._currentSelection !== entry) ){
  261. //Found it and it isn't already selected.
  262. domClass.add(entry.domNode, "feedViewerEntrySelected");
  263. domClass.remove(entry._entryWidget.timeNode, "feedViewerEntryUpdated");
  264. domClass.add(entry._entryWidget.timeNode, "feedViewerEntryUpdatedSelected");
  265. this.onEntrySelected(entry);
  266. if(this.entrySelectionTopic !== ""){
  267. dojo.publish(this.entrySelectionTopic, [{ action: "set", source: this, feed: this._feed, entry: entry }]);
  268. }
  269. if(this._isEditable(entry)){
  270. entry._entryWidget.enableDelete();
  271. }
  272. this._deselectCurrentSelection();
  273. this._currentSelection = entry;
  274. break;
  275. }else if( (selectedNode === entry.domNode) && (this._currentSelection === entry) ){
  276. //Found it and it is the current selection, we just want to de-select it so users can 'unselect rows' if they want.
  277. dojo.publish(this.entrySelectionTopic, [{ action: "delete", source: this, entry: entry }]);
  278. this._deselectCurrentSelection();
  279. break;
  280. }
  281. }
  282. },
  283. _deselectCurrentSelection: function(){
  284. // summary:
  285. // Internal function for unselecting the current selection.
  286. // description:
  287. // Internal function for unselecting the current selection.
  288. //
  289. // returns:
  290. // Nothing.
  291. if(this._currentSelection){
  292. domClass.add(this._currentSelection._entryWidget.timeNode, "feedViewerEntryUpdated");
  293. domClass.remove(this._currentSelection.domNode, "feedViewerEntrySelected");
  294. domClass.remove(this._currentSelection._entryWidget.timeNode, "feedViewerEntryUpdatedSelected");
  295. this._currentSelection._entryWidget.disableDelete();
  296. this._currentSelection = null;
  297. }
  298. },
  299. _isEditable: function(/*object*/entry){
  300. // summary:
  301. // Internal function for determining of a particular entry is editable.
  302. // description:
  303. // Internal function for determining of a particular entry is editable.
  304. // This is used for determining if the delete action should be displayed or not.
  305. //
  306. // entry:
  307. // The dojox.atom.io.model.Entry object to examine
  308. //
  309. // returns:
  310. // Boolean denoting if the entry seems editable or not..
  311. var retVal = false;
  312. if(entry && entry !== null && entry.links && entry.links !== null){
  313. for(var x in entry.links){
  314. if(entry.links[x].rel && entry.links[x].rel == "edit"){
  315. retVal = true;
  316. break;
  317. }
  318. }
  319. }
  320. return retVal;
  321. },
  322. onEntrySelected: function(/*object*/entry){
  323. // summary:
  324. // Function intended for over-riding/replacement as an attachpoint to for other items to recieve
  325. // selection notification.
  326. // description: Function intended for over0-riding/replacement as an attachpoint to for other items to recieve
  327. // selection notification.
  328. //
  329. // entry:
  330. // The dojox.atom.io.model.Entry object selected.
  331. //
  332. // returns:
  333. // Nothing.
  334. },
  335. _isRelativeURL: function(/*string*/url){
  336. // summary:
  337. // Method to determine if the URL is relative or absolute.
  338. // description:
  339. // Method to determine if the URL is relative or absolute. Basic assumption is if it doesn't start
  340. // with http:// or file://, it's relative to the current document.
  341. //
  342. // url:
  343. // The URL to inspect.
  344. //
  345. // returns:
  346. // boolean indicating whether it's a relative url or not.
  347. var isFileURL = function(url){
  348. var retVal = false;
  349. if(url.indexOf("file://") === 0){
  350. retVal = true;
  351. }
  352. return retVal;
  353. }
  354. var isHttpURL = function(url){
  355. var retVal = false;
  356. if(url.indexOf("http://") === 0){
  357. retVal = true;
  358. }
  359. return retVal;
  360. }
  361. var retVal = false;
  362. if(url !== null){
  363. if(!isFileURL(url) && !isHttpURL(url)){
  364. retVal = true;
  365. }
  366. }
  367. return retVal;
  368. },
  369. _calculateBaseURL: function(/*string*/fullURL, /*boolean*/currentPageRelative){
  370. // summary:
  371. // Internal function to calculate a baseline URL from the provided full URL.
  372. // description:
  373. // Internal function to calculate a baseline URL from the provided full URL.
  374. //
  375. // fullURL:
  376. // The full URL as a string.
  377. // currentPageRelative:
  378. // Flag to denote of the base URL should be calculated as just the server base, or relative to the current page/location in the URL.
  379. //
  380. // returns:
  381. // String of the baseline URL
  382. var baseURL = null;
  383. if(fullURL !== null){
  384. //Check to see if we need to strip off any query parameters from the URL.
  385. var index = fullURL.indexOf("?");
  386. if(index != -1){
  387. fullURL = fullURL.substring(0,index);
  388. //console.debug("Removed query parameters. URL now: " + fullURL);
  389. }
  390. if(currentPageRelative){
  391. //Relative to the 'current page' in the URL, so we need to trim that off.
  392. //Now we need to trim if necessary. If it ends in /, then we don't have a filename to trim off
  393. //so we can return.
  394. index = fullURL.lastIndexOf("/");
  395. if((index > 0) && (index < fullURL.length) && (index !== (fullURL.length -1))){
  396. //We want to include the terminating /
  397. baseURL = fullURL.substring(0,(index + 1));
  398. }else{
  399. baseURL = fullURL;
  400. }
  401. }else{
  402. //We want to find the first occurance of / after the <protocol>://
  403. index = fullURL.indexOf("://");
  404. if(index > 0){
  405. index = index + 3;
  406. var protocol = fullURL.substring(0,index);
  407. var fragmentURL = fullURL.substring(index, fullURL.length);
  408. index = fragmentURL.indexOf("/");
  409. if((index < fragmentURL.length) && (index > 0) ){
  410. baseURL = protocol + fragmentURL.substring(0,index);
  411. }else{
  412. baseURL = protocol + fragmentURL;
  413. }
  414. }
  415. }
  416. }
  417. return baseURL;
  418. },
  419. _isFilterAccepted: function(/*object*/entry) {
  420. // summary:
  421. // Internal function to do matching of category filters to widgets.
  422. // description:
  423. // Internal function to do matching of category filters to widgets.
  424. //
  425. // returns:
  426. // boolean denoting if this entry matched one of the accept filters.
  427. var accepted = false;
  428. if (this._includeFilters && (this._includeFilters.length > 0)) {
  429. for (var i = 0; i < this._includeFilters.length; i++) {
  430. var filter = this._includeFilters[i];
  431. if (filter.match(entry)) {
  432. accepted = true;
  433. break;
  434. }
  435. }
  436. }
  437. else {
  438. accepted = true;
  439. }
  440. return accepted;
  441. },
  442. addCategoryIncludeFilter: function(/*object*/filter) {
  443. // summary:
  444. // Function to add a filter for entry inclusion in the feed view.
  445. // description:
  446. // Function to add a filter for entry inclusion in the feed view.
  447. //
  448. // filter:
  449. // The basic items to filter on and the values.
  450. // Should be of format: {scheme: <some text or null>, term: <some text or null>, label: <some text or null>}
  451. //
  452. // returns:
  453. // Nothing.
  454. if (filter) {
  455. var scheme = filter.scheme;
  456. var term = filter.term;
  457. var label = filter.label;
  458. var addIt = true;
  459. if (!scheme) {
  460. scheme = null;
  461. }
  462. if (!term) {
  463. scheme = null;
  464. }
  465. if (!label) {
  466. scheme = null;
  467. }
  468. if (this._includeFilters && this._includeFilters.length > 0) {
  469. for (var i = 0; i < this._includeFilters.length; i++) {
  470. var eFilter = this._includeFilters[i];
  471. if ((eFilter.term === term) && (eFilter.scheme === scheme) && (eFilter.label === label)) {
  472. //Verify we don't have this filter already.
  473. addIt = false;
  474. break;
  475. }
  476. }
  477. }
  478. if (addIt) {
  479. this._includeFilters.push(widget.FeedViewer.CategoryIncludeFilter(scheme, term, label));
  480. }
  481. }
  482. },
  483. removeCategoryIncludeFilter: function(/*object*/filter) {
  484. // summary:
  485. // Function to remove a filter for entry inclusion in the feed view.
  486. // description:
  487. // Function to remove a filter for entry inclusion in the feed view.
  488. //
  489. // filter:
  490. // The basic items to identify the filter that is present.
  491. // Should be of format: {scheme: <some text or null>, term: <some text or null>, label: <some text or null>}
  492. //
  493. // returns:
  494. // Nothing.
  495. if (filter) {
  496. var scheme = filter.scheme;
  497. var term = filter.term;
  498. var label = filter.label;
  499. if (!scheme) {
  500. scheme = null;
  501. }
  502. if (!term) {
  503. scheme = null;
  504. }
  505. if (!label) {
  506. scheme = null;
  507. }
  508. var newFilters = [];
  509. if (this._includeFilters && this._includeFilters.length > 0) {
  510. for (var i = 0; i < this._includeFilters.length; i++) {
  511. var eFilter = this._includeFilters[i];
  512. if (!((eFilter.term === term) && (eFilter.scheme === scheme) && (eFilter.label === label))) {
  513. //Keep only filters that do not match
  514. newFilters.push(eFilter);
  515. }
  516. }
  517. this._includeFilters = newFilters;
  518. }
  519. }
  520. },
  521. _handleEvent: function(/*object*/entrySelectionEvent) {
  522. // summary:
  523. // Internal function for listening to a topic that will handle entry notification.
  524. // description:
  525. // Internal function for listening to a topic that will handle entry notification.
  526. //
  527. // entrySelectionEvent:
  528. // The topic message containing the entry that was selected for view.
  529. //
  530. // returns:
  531. // Nothing.
  532. if(entrySelectionEvent.source != this) {
  533. if(entrySelectionEvent.action == "update" && entrySelectionEvent.entry) {
  534. var evt = entrySelectionEvent;
  535. if(!this.localSaveOnly){
  536. this.atomIO.updateEntry(evt.entry, lang.hitch(evt.source,evt.callback), null, true);
  537. }
  538. this._currentSelection._entryWidget.setTime(this._displayDateForEntry(evt.entry).toLocaleTimeString());
  539. this._currentSelection._entryWidget.setTitle(evt.entry.title.value);
  540. } else if(entrySelectionEvent.action == "post" && entrySelectionEvent.entry) {
  541. if(!this.localSaveOnly){
  542. this.atomIO.addEntry(entrySelectionEvent.entry, this.url, lang.hitch(this,this._addEntry));
  543. }else{
  544. this._addEntry(entrySelectionEvent.entry);
  545. }
  546. }
  547. }
  548. },
  549. _addEntry: function(/*object*/entry) {
  550. // summary:
  551. // callback function used when adding an entry to the feed.
  552. // description:
  553. // callback function used when adding an entry to the feed. After the entry has been posted to the feed,
  554. // we add it to our feed representation (to show it on the page) and publish an event to update any entry viewers.
  555. this._feed.addEntry(entry);
  556. this.setFeed(this._feed);
  557. dojo.publish(this.entrySelectionTopic, [{ action: "set", source: this, feed: this._feed, entry: entry }]);
  558. },
  559. destroy: function(){
  560. // summary:
  561. // Destroys this widget, including all descendants and subscriptions.
  562. // description:
  563. // Destroys this widget, including all descendants and subscriptions.
  564. this.clear();
  565. arrayUtil.forEach(this._subscriptions, dojo.unsubscribe);
  566. }
  567. });
  568. widget.FeedViewerEntry = dojo.declare(/*===== "dojox.atom.widget.FeedViewerEntry", =====*/ [_Widget, _Templated],{
  569. // summary:
  570. // Widget for handling the display of an entry and specific events associated with it.
  571. // description: Widget for handling the display of an entry and specific events associated with it.
  572. templateString: entryTemplate,
  573. entryNode: null,
  574. timeNode: null,
  575. deleteButton: null,
  576. entry: null,
  577. feed: null,
  578. postCreate: function(){
  579. var _nlsResources = i18nViewer;
  580. this.deleteButton.innerHTML = _nlsResources.deleteButton;
  581. },
  582. setTitle: function(/*string*/text){
  583. // summary:
  584. // Function to set the title of the entry.
  585. // description:
  586. // Function to set the title of the entry.
  587. //
  588. // text:
  589. // The title.
  590. //
  591. // returns:
  592. // Nothing.
  593. if (this.titleNode.lastChild){this.titleNode.removeChild(this.titleNode.lastChild);}
  594. var titleTextNode = document.createElement("div");
  595. titleTextNode.innerHTML = text;
  596. this.titleNode.appendChild(titleTextNode);
  597. },
  598. setTime: function(/*string*/timeText){
  599. // summary:
  600. // Function to set the time of the entry.
  601. // description:
  602. // Function to set the time of the entry.
  603. //
  604. // timeText:
  605. // The string form of the date.
  606. //
  607. // returns:
  608. // Nothing.
  609. if (this.timeNode.lastChild){this.timeNode.removeChild(this.timeNode.lastChild);}
  610. var timeTextNode = document.createTextNode(timeText);
  611. this.timeNode.appendChild(timeTextNode);
  612. },
  613. enableDelete: function(){
  614. // summary:
  615. // Function to enable the delete action on this entry.
  616. // description:
  617. // Function to enable the delete action on this entry.
  618. //
  619. // returns:
  620. // Nothing.
  621. if (this.deleteButton !== null) {
  622. //TODO Fix this
  623. this.deleteButton.style.display = 'inline';
  624. }
  625. },
  626. disableDelete: function(){
  627. // summary:
  628. // Function to disable the delete action on this entry.
  629. // description:
  630. // Function to disable the delete action on this entry.
  631. //
  632. // returns:
  633. // Nothing.
  634. if (this.deleteButton !== null) {
  635. this.deleteButton.style.display = 'none';
  636. }
  637. },
  638. deleteEntry: function(/*object*/event) {
  639. // summary:
  640. // Function to handle the delete event and delete the entry.
  641. // description:
  642. // Function to handle the delete event and delete the entry.
  643. //
  644. // returns:
  645. // Nothing.
  646. event.preventDefault();
  647. event.stopPropagation();
  648. this.feed.deleteEntry(this);
  649. },
  650. onClick: function(/*object*/e){
  651. // summary:
  652. // Attach point for when a row is clicked on.
  653. // description:
  654. // Attach point for when a row is clicked on.
  655. //
  656. // e:
  657. // The event generated by the click.
  658. }
  659. });
  660. widget.FeedViewerGrouping = dojo.declare(/*===== "dojox.atom.widget.FeedViewerGrouping", =====*/ [_Widget, _Templated],{
  661. // summary:
  662. // Grouping of feed entries.
  663. // description:
  664. // Grouping of feed entries.
  665. templateString: groupingTemplate,
  666. groupingNode: null,
  667. titleNode: null,
  668. setText: function(text){
  669. // summary:
  670. // Sets the text to be shown above this grouping.
  671. // description:
  672. // Sets the text to be shown above this grouping.
  673. //
  674. // text:
  675. // The text to show.
  676. if (this.titleNode.lastChild){this.titleNode.removeChild(this.titleNode.lastChild);}
  677. var textNode = document.createTextNode(text);
  678. this.titleNode.appendChild(textNode);
  679. }
  680. });
  681. widget.AtomEntryCategoryFilter = dojo.declare(/*===== "dojox.atom.widget.AtomEntryCategoryFilter", =====*/ [_Widget, _Templated],{
  682. // summary:
  683. // A filter to be applied to the list of entries.
  684. // description:
  685. // A filter to be applied to the list of entries.
  686. scheme: "",
  687. term: "",
  688. label: "",
  689. isFilter: true
  690. });
  691. widget.FeedViewer.CategoryIncludeFilter = dojo.declare(/*===== "dojox.atom.widget.FeedViewer.CategoryIncludeFilter", =====*/ null,{
  692. constructor: function(scheme, term, label){
  693. // summary:
  694. // The initializer function.
  695. // description:
  696. // The initializer function.
  697. this.scheme = scheme;
  698. this.term = term;
  699. this.label = label;
  700. },
  701. match: function(entry) {
  702. // summary:
  703. // Function to determine if this category filter matches against a category on an atom entry
  704. // description:
  705. // Function to determine if this category filter matches against a category on an atom entry
  706. //
  707. // returns:
  708. // boolean denoting if this category filter matched to this entry.
  709. var matched = false;
  710. if (entry !== null) {
  711. var categories = entry.categories;
  712. if (categories !== null) {
  713. for (var i = 0; i < categories.length; i++) {
  714. var category = categories[i];
  715. if (this.scheme !== "") {
  716. if (this.scheme !== category.scheme) {
  717. break;
  718. }
  719. }
  720. if (this.term !== "") {
  721. if (this.term !== category.term) {
  722. break;
  723. }
  724. }
  725. if (this.label !== "") {
  726. if (this.label !== category.label) {
  727. break;
  728. }
  729. }
  730. //Made it this far, everything matched.
  731. matched = true;
  732. }
  733. }
  734. }
  735. return matched;
  736. }
  737. });
  738. return widget.FeedViewer;
  739. });