nag.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*!
  2. * # Fomantic-UI - Nag
  3. * http://github.com/fomantic/Fomantic-UI/
  4. *
  5. *
  6. * Released under the MIT license
  7. * http://opensource.org/licenses/MIT
  8. *
  9. */
  10. ;(function ($, window, document, undefined) {
  11. 'use strict';
  12. $.isFunction = $.isFunction || function(obj) {
  13. return typeof obj === "function" && typeof obj.nodeType !== "number";
  14. };
  15. window = (typeof window != 'undefined' && window.Math == Math)
  16. ? window
  17. : (typeof self != 'undefined' && self.Math == Math)
  18. ? self
  19. : Function('return this')()
  20. ;
  21. $.fn.nag = function(parameters) {
  22. var
  23. $allModules = $(this),
  24. moduleSelector = $allModules.selector || '',
  25. time = new Date().getTime(),
  26. performance = [],
  27. query = arguments[0],
  28. methodInvoked = (typeof query == 'string'),
  29. queryArguments = [].slice.call(arguments, 1),
  30. returnedValue
  31. ;
  32. $allModules
  33. .each(function() {
  34. var
  35. settings = ( $.isPlainObject(parameters) )
  36. ? $.extend(true, {}, $.fn.nag.settings, parameters)
  37. : $.extend({}, $.fn.nag.settings),
  38. selector = settings.selector,
  39. error = settings.error,
  40. namespace = settings.namespace,
  41. eventNamespace = '.' + namespace,
  42. moduleNamespace = namespace + '-module',
  43. $module = $(this),
  44. $context = (settings.context)
  45. ? $(settings.context)
  46. : $('body'),
  47. element = this,
  48. instance = $module.data(moduleNamespace),
  49. module
  50. ;
  51. module = {
  52. initialize: function() {
  53. module.verbose('Initializing element');
  54. $module
  55. .on('click' + eventNamespace, selector.close, module.dismiss)
  56. .data(moduleNamespace, module)
  57. ;
  58. if(settings.detachable && $module.parent()[0] !== $context[0]) {
  59. $module
  60. .detach()
  61. .prependTo($context)
  62. ;
  63. }
  64. if(settings.displayTime > 0) {
  65. setTimeout(module.hide, settings.displayTime);
  66. }
  67. module.show();
  68. },
  69. destroy: function() {
  70. module.verbose('Destroying instance');
  71. $module
  72. .removeData(moduleNamespace)
  73. .off(eventNamespace)
  74. ;
  75. },
  76. show: function() {
  77. if( module.should.show() && !$module.is(':visible') ) {
  78. module.debug('Showing nag', settings.animation.show);
  79. if(settings.animation.show == 'fade') {
  80. $module
  81. .fadeIn(settings.duration, settings.easing)
  82. ;
  83. }
  84. else {
  85. $module
  86. .slideDown(settings.duration, settings.easing)
  87. ;
  88. }
  89. }
  90. },
  91. hide: function() {
  92. module.debug('Showing nag', settings.animation.hide);
  93. if(settings.animation.show == 'fade') {
  94. $module
  95. .fadeIn(settings.duration, settings.easing)
  96. ;
  97. }
  98. else {
  99. $module
  100. .slideUp(settings.duration, settings.easing)
  101. ;
  102. }
  103. },
  104. onHide: function() {
  105. module.debug('Removing nag', settings.animation.hide);
  106. $module.remove();
  107. if (settings.onHide) {
  108. settings.onHide();
  109. }
  110. },
  111. dismiss: function(event) {
  112. if(settings.storageMethod) {
  113. module.storage.set(settings.key, settings.value);
  114. }
  115. module.hide();
  116. event.stopImmediatePropagation();
  117. event.preventDefault();
  118. },
  119. should: {
  120. show: function() {
  121. if(settings.persist) {
  122. module.debug('Persistent nag is set, can show nag');
  123. return true;
  124. }
  125. if( module.storage.get(settings.key) != settings.value.toString() ) {
  126. module.debug('Stored value is not set, can show nag', module.storage.get(settings.key));
  127. return true;
  128. }
  129. module.debug('Stored value is set, cannot show nag', module.storage.get(settings.key));
  130. return false;
  131. }
  132. },
  133. get: {
  134. storageOptions: function() {
  135. var
  136. options = {}
  137. ;
  138. if(settings.expires) {
  139. options.expires = settings.expires;
  140. }
  141. if(settings.domain) {
  142. options.domain = settings.domain;
  143. }
  144. if(settings.path) {
  145. options.path = settings.path;
  146. }
  147. return options;
  148. }
  149. },
  150. clear: function() {
  151. module.storage.remove(settings.key);
  152. },
  153. storage: {
  154. set: function(key, value) {
  155. var
  156. options = module.get.storageOptions()
  157. ;
  158. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  159. window.localStorage.setItem(key, value);
  160. module.debug('Value stored using local storage', key, value);
  161. }
  162. else if(settings.storageMethod == 'sessionstorage' && window.sessionStorage !== undefined) {
  163. window.sessionStorage.setItem(key, value);
  164. module.debug('Value stored using session storage', key, value);
  165. }
  166. else if($.cookie !== undefined) {
  167. $.cookie(key, value, options);
  168. module.debug('Value stored using cookie', key, value, options);
  169. }
  170. else {
  171. module.error(error.noCookieStorage);
  172. return;
  173. }
  174. },
  175. get: function(key, value) {
  176. var
  177. storedValue
  178. ;
  179. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  180. storedValue = window.localStorage.getItem(key);
  181. }
  182. else if(settings.storageMethod == 'sessionstorage' && window.sessionStorage !== undefined) {
  183. storedValue = window.sessionStorage.getItem(key);
  184. }
  185. // get by cookie
  186. else if($.cookie !== undefined) {
  187. storedValue = $.cookie(key);
  188. }
  189. else {
  190. module.error(error.noCookieStorage);
  191. }
  192. if(storedValue == 'undefined' || storedValue == 'null' || storedValue === undefined || storedValue === null) {
  193. storedValue = undefined;
  194. }
  195. return storedValue;
  196. },
  197. remove: function(key) {
  198. var
  199. options = module.get.storageOptions()
  200. ;
  201. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  202. window.localStorage.removeItem(key);
  203. }
  204. else if(settings.storageMethod == 'sessionstorage' && window.sessionStorage !== undefined) {
  205. window.sessionStorage.removeItem(key);
  206. }
  207. // store by cookie
  208. else if($.cookie !== undefined) {
  209. $.removeCookie(key, options);
  210. }
  211. else {
  212. module.error(error.noStorage);
  213. }
  214. }
  215. },
  216. setting: function(name, value) {
  217. module.debug('Changing setting', name, value);
  218. if( $.isPlainObject(name) ) {
  219. $.extend(true, settings, name);
  220. }
  221. else if(value !== undefined) {
  222. if($.isPlainObject(settings[name])) {
  223. $.extend(true, settings[name], value);
  224. }
  225. else {
  226. settings[name] = value;
  227. }
  228. }
  229. else {
  230. return settings[name];
  231. }
  232. },
  233. internal: function(name, value) {
  234. if( $.isPlainObject(name) ) {
  235. $.extend(true, module, name);
  236. }
  237. else if(value !== undefined) {
  238. module[name] = value;
  239. }
  240. else {
  241. return module[name];
  242. }
  243. },
  244. debug: function() {
  245. if(!settings.silent && settings.debug) {
  246. if(settings.performance) {
  247. module.performance.log(arguments);
  248. }
  249. else {
  250. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  251. module.debug.apply(console, arguments);
  252. }
  253. }
  254. },
  255. verbose: function() {
  256. if(!settings.silent && settings.verbose && settings.debug) {
  257. if(settings.performance) {
  258. module.performance.log(arguments);
  259. }
  260. else {
  261. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  262. module.verbose.apply(console, arguments);
  263. }
  264. }
  265. },
  266. error: function() {
  267. if(!settings.silent) {
  268. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  269. module.error.apply(console, arguments);
  270. }
  271. },
  272. performance: {
  273. log: function(message) {
  274. var
  275. currentTime,
  276. executionTime,
  277. previousTime
  278. ;
  279. if(settings.performance) {
  280. currentTime = new Date().getTime();
  281. previousTime = time || currentTime;
  282. executionTime = currentTime - previousTime;
  283. time = currentTime;
  284. performance.push({
  285. 'Name' : message[0],
  286. 'Arguments' : [].slice.call(message, 1) || '',
  287. 'Element' : element,
  288. 'Execution Time' : executionTime
  289. });
  290. }
  291. clearTimeout(module.performance.timer);
  292. module.performance.timer = setTimeout(module.performance.display, 500);
  293. },
  294. display: function() {
  295. var
  296. title = settings.name + ':',
  297. totalTime = 0
  298. ;
  299. time = false;
  300. clearTimeout(module.performance.timer);
  301. $.each(performance, function(index, data) {
  302. totalTime += data['Execution Time'];
  303. });
  304. title += ' ' + totalTime + 'ms';
  305. if(moduleSelector) {
  306. title += ' \'' + moduleSelector + '\'';
  307. }
  308. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  309. console.groupCollapsed(title);
  310. if(console.table) {
  311. console.table(performance);
  312. }
  313. else {
  314. $.each(performance, function(index, data) {
  315. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  316. });
  317. }
  318. console.groupEnd();
  319. }
  320. performance = [];
  321. }
  322. },
  323. invoke: function(query, passedArguments, context) {
  324. var
  325. object = instance,
  326. maxDepth,
  327. found,
  328. response
  329. ;
  330. passedArguments = passedArguments || queryArguments;
  331. context = element || context;
  332. if(typeof query == 'string' && object !== undefined) {
  333. query = query.split(/[\. ]/);
  334. maxDepth = query.length - 1;
  335. $.each(query, function(depth, value) {
  336. var camelCaseValue = (depth != maxDepth)
  337. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  338. : query
  339. ;
  340. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  341. object = object[camelCaseValue];
  342. }
  343. else if( object[camelCaseValue] !== undefined ) {
  344. found = object[camelCaseValue];
  345. return false;
  346. }
  347. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  348. object = object[value];
  349. }
  350. else if( object[value] !== undefined ) {
  351. found = object[value];
  352. return false;
  353. }
  354. else {
  355. module.error(error.method, query);
  356. return false;
  357. }
  358. });
  359. }
  360. if ( $.isFunction( found ) ) {
  361. response = found.apply(context, passedArguments);
  362. }
  363. else if(found !== undefined) {
  364. response = found;
  365. }
  366. if(Array.isArray(returnedValue)) {
  367. returnedValue.push(response);
  368. }
  369. else if(returnedValue !== undefined) {
  370. returnedValue = [returnedValue, response];
  371. }
  372. else if(response !== undefined) {
  373. returnedValue = response;
  374. }
  375. return found;
  376. }
  377. };
  378. if(methodInvoked) {
  379. if(instance === undefined) {
  380. module.initialize();
  381. }
  382. module.invoke(query);
  383. }
  384. else {
  385. if(instance !== undefined) {
  386. instance.invoke('destroy');
  387. }
  388. module.initialize();
  389. }
  390. })
  391. ;
  392. return (returnedValue !== undefined)
  393. ? returnedValue
  394. : this
  395. ;
  396. };
  397. $.fn.nag.settings = {
  398. name : 'Nag',
  399. silent : false,
  400. debug : false,
  401. verbose : false,
  402. performance : true,
  403. namespace : 'Nag',
  404. // allows cookie to be overridden
  405. persist : false,
  406. // set to zero to require manually dismissal, otherwise hides on its own
  407. displayTime : 0,
  408. animation : {
  409. show : 'slide',
  410. hide : 'slide'
  411. },
  412. context : false,
  413. detachable : false,
  414. expires : 30,
  415. domain : false,
  416. path : '/',
  417. // type of storage to use
  418. storageMethod : 'cookie',
  419. // value to store in dismissed localstorage/cookie
  420. key : 'nag',
  421. value : 'dismiss',
  422. error: {
  423. noCookieStorage : '$.cookie is not included. A storage solution is required.',
  424. noStorage : 'Neither $.cookie or store is defined. A storage solution is required for storing state',
  425. method : 'The method you called is not defined.'
  426. },
  427. className : {
  428. bottom : 'bottom',
  429. fixed : 'fixed'
  430. },
  431. selector : {
  432. close : '.close.icon'
  433. },
  434. speed : 500,
  435. easing : 'easeOutQuad',
  436. onHide: function() {}
  437. };
  438. // Adds easing
  439. $.extend( $.easing, {
  440. easeOutQuad: function (x, t, b, c, d) {
  441. return -c *(t/=d)*(t-2) + b;
  442. }
  443. });
  444. })( jQuery, window, document );