PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/notification-portlet-webapp/src/main/webapp/scripts/jquery.notifications.js

https://github.com/manhattancollege/up-NotificationPortlet
JavaScript | 329 lines | 228 code | 41 blank | 60 comment | 14 complexity | a0216a8afcfe01815de690097fb2904c MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Licensed to Jasig under one or more contributor license
  3. * agreements. See the NOTICE file distributed with this work
  4. * for additional information regarding copyright ownership.
  5. * Jasig licenses this file to you under the Apache License,
  6. * Version 2.0 (the "License"); you may not use this file
  7. * except in compliance with the License. You may obtain a
  8. * copy of the License at:
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on
  14. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. //
  20. // Notifications portlet jQuery plugin
  21. // developed on behalf of the University
  22. // of Manchester
  23. //
  24. // Author: Jacob Lichner
  25. // Email: jlichner@unicon.net
  26. //
  27. (function ($) {
  28. // Set underscore's templating syntax
  29. _.templateSettings = {
  30. interpolate : /\{\{(.+?)\}\}/g, // {{ variable }}
  31. evaluate : /\{%(.+?)%\}/g // {% expression %}
  32. };
  33. $.fn.notifications = function (opts) {
  34. // Cache existing DOM elements
  35. var portlet = this.find(".notification-portlet-wrapper"),
  36. outerContainer = this.selector,
  37. links = this.find(".notification-portlet a"),
  38. errorContainer = this.find(".notification-error-container"),
  39. loading = this.find(".notification-loading"),
  40. notifications = this.find(".notification-container"),
  41. detailView = this.find(".notification-detail-wrapper"),
  42. detailContainer = this.find(".notification-detail-container"),
  43. backButton = this.find(".notification-back-button"),
  44. refreshButton = this.find(".notification-refresh a"),
  45. filterOptions = this.find(".notification-options"),
  46. todayFilter = filterOptions.find(".today"),
  47. allFilter = filterOptions.find(".all");
  48. // Notification gets cached in the AJAX callback
  49. // but is created here for scope
  50. var notification;
  51. // Store the filter state (notifications that are
  52. // currently being displayed), defaults to today
  53. var filterState = {"days": 1};
  54. function getNotifications(params, doRefresh) {
  55. // Looading div is displayed by default
  56. // and is then hidden after the AJAX call
  57. loading.ajaxStop(function () {
  58. $(this).hide();
  59. portlet.fadeIn("fast");
  60. filterOptions.fadeIn("fast");
  61. });
  62. // First 'prime-the-pump' with an ActionURL
  63. $.ajax({
  64. type: 'POST',
  65. dataType: 'json',
  66. data: { refresh: doRefresh },
  67. url: opts.invokeNotificationServiceUrl,
  68. async: false
  69. });
  70. // Now fetch the notifications with a ResourceURL
  71. $.ajax({
  72. url : opts.getNotificationsUrl,
  73. type : 'POST',
  74. dataType : 'json',
  75. data : params,
  76. beforeSend: function () {
  77. // Hide detail view
  78. if ( detailView.is(":visible") ) {
  79. detailView.hide();
  80. notifications.show();
  81. }
  82. // Show loading
  83. portlet.hide();
  84. loading.show();
  85. // Unbind click events
  86. links.unbind("click");
  87. backButton.unbind("click");
  88. filterOptions.find("a").unbind("click");
  89. // Clear out notifications and errors
  90. notifications.html(" ");
  91. errorContainer.html(" ");
  92. },
  93. success: function (data) {
  94. var data = data.notificationResponse;
  95. // Build notifications
  96. buildNotifications(data);
  97. // Once notifications have been injected into the DOM
  98. // we cache the notication element...
  99. notification = $(outerContainer + " .notifications a");
  100. // ...and bind our events
  101. bindEvent.accordion(data);
  102. bindEvent.viewDetail();
  103. bindEvent.goBack();
  104. bindEvent.refresh();
  105. bindEvent.filterOptions(data);
  106. // Errors
  107. errorHandling(data);
  108. },
  109. error: function () {
  110. $(this).html(" ").text("AJAX failed. ~ THE END ~");
  111. }
  112. });
  113. }
  114. // Build notifications using underscore.js
  115. // template method
  116. function buildNotifications(data) {
  117. // HTML string compiled with underscore.js
  118. var html = '\
  119. {% if (categories.length < 1 ) { %} \
  120. <div class="no-notifications-container"> \
  121. <h3>You have 0 notifications.</h3> \
  122. </div> \
  123. {% } else { %} \
  124. {% var accordion = categories.length > 1; %} \
  125. {% _.each(categories, function(category) { %} \
  126. <div class="notification-trigger"> \
  127. <h3 class="portlet-section-header trigger-symbol" role="header"> \
  128. {{ category.title }} \
  129. {% if (accordion) { %} \
  130. ({{ category.entries.length }}) \
  131. {% } %} \
  132. </h3> \
  133. </div> \
  134. {% if (category.entries.length < 1) { %} \
  135. <!-- no notifications --> \
  136. {% } else { %} \
  137. <div class="notification-content" style="display: none;"> \
  138. <ul class="notifications"> \
  139. {% _.each(category.entries, function(entry) { %} \
  140. <li> \
  141. {% if (!accordion) { %} \
  142. &raquo; \
  143. {% } %} \
  144. <a href="{{ entry.url }}" \
  145. data-body="{{ escape(entry.body) }}" \
  146. data-title="{{ entry.title }}" \
  147. data-source="{{ entry.source }}"> {{ entry.title }}</a> \
  148. {% if ( entry.dueDate ) { \
  149. var date = new Date(entry.dueDate.time), \
  150. month = date.getMonth() + 1, \
  151. day = date.getDay(), \
  152. year = date.getFullYear(), \
  153. overDue = (date < new Date() ? " overdue" : ""); %} \
  154. <span class="notification-due-date{{ overDue }}"> \
  155. Due {{ month }}/{{ day }}/{{ year }} \
  156. </span> \
  157. {% } %} \
  158. </li> \
  159. {% }); %} \
  160. </ul> \
  161. </div> \
  162. {% } %} \
  163. {% }); %} \
  164. {% } %} \
  165. ';
  166. var compiled = _.template(html, data);
  167. // Inject compiled markup into notifications container div
  168. notifications.html(" ").prepend(compiled);
  169. }
  170. // Bind events object helps keep events together
  171. var bindEvent = {
  172. // Accordion via plugin
  173. accordion: function (data) {
  174. if ( data.categories.length === 1 ) {
  175. portlet.removeClass("accordion");
  176. notifications.children().show();
  177. } else {
  178. notifications.accordion();
  179. portlet.addClass("accordion");
  180. }
  181. },
  182. // View detail page
  183. viewDetail: function () {
  184. notification.click(function () {
  185. // Notification detail is retrieved from 'data-'
  186. // attributes and stored in a notification object
  187. var notification = {
  188. body : $(this).data("body"),
  189. title : $(this).data("title"),
  190. source : $(this).data("source"),
  191. link : $(this).attr("href")
  192. }
  193. var html = '\
  194. <h3><a href="{{ link }}">{{ title }}</a></h3> \
  195. <p>{{ unescape(body) }}</p> \
  196. <p class="notification-source"> \
  197. Source: <a href="{{ link }}">{{ source }}</a> \
  198. </p> \
  199. ';
  200. var compiled = _.template(html, notification);
  201. $.each([notifications, errorContainer], function () {
  202. $(this).hide(
  203. "slide", 200, function () {
  204. detailContainer.html(" ").append(compiled);
  205. detailView.show();
  206. });
  207. });
  208. return false;
  209. });
  210. },
  211. // Go back to all notifications
  212. goBack: function () {
  213. backButton.click(function () {
  214. detailView.hide(
  215. "slide", {direction: "right"}, 200, function () {
  216. notifications.show();
  217. errorContainer.show();
  218. }
  219. )
  220. return false;
  221. })
  222. .hover(
  223. function () { $(this).addClass('hover'); },
  224. function () { $(this).removeClass('hover') }
  225. );
  226. },
  227. refresh: function () {
  228. refreshButton.click(function () {
  229. getNotifications(filterState, 'true');
  230. return false;
  231. });
  232. },
  233. filterOptions: function (data) {
  234. todayFilter.click(function () {
  235. filter($(this), {"days":1});
  236. return false;
  237. });
  238. allFilter.click(function () {
  239. filter($(this));
  240. return false;
  241. });
  242. }
  243. }
  244. // Filter notifications by passing params
  245. // via ajax ie {"days":1} is today, also
  246. // stores and returns filterState
  247. function filter(link, params) {
  248. filterState = params || {};
  249. if ( link.hasClass("active") ) {
  250. return false;
  251. } else {
  252. getNotifications(filterState);
  253. filterOptions.find("a").removeClass("active");
  254. link.addClass("active");
  255. }
  256. return filterState;
  257. }
  258. // Errors (broken feeds)
  259. function errorHandling(data) {
  260. if ( data.errors ) {
  261. var html = '\
  262. {% _.each(errors, function(error) { %} \
  263. <div class="portlet-msg-error" errorkey="{{ error.key }}"> \
  264. {{ error.source }}: {{ error.error }} \
  265. <a href="#" class="remove" title="Hide"></a> \
  266. </div> \
  267. {% }); %} \
  268. ';
  269. var compile = _.template(html, data);
  270. errorContainer.show().append(compile);
  271. errorContainer.find(".remove").click(function () {
  272. var thisErrorContainer = $(this).parent();
  273. thisErrorContainer.fadeOut("fast", function () {
  274. var settings = [];
  275. $.ajax({
  276. url: (opts.hideErrorUrl).replace("ERRORKEY", thisErrorContainer.attr("errorkey")),
  277. type: 'POST',
  278. success: function() { return false; }
  279. });
  280. });
  281. return false;
  282. });
  283. }
  284. }
  285. // Load notifications
  286. getNotifications(filterState);
  287. }
  288. })(jQuery);