You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

513 lines
15 KiB

10 years ago
10 years ago
10 years ago
  1. /*
  2. * # Semantic - Visit
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ($, window, document, undefined) {
  12. $.visit = $.fn.visit = function(parameters) {
  13. var
  14. $allModules = $.isFunction(this)
  15. ? $(window)
  16. : $(this),
  17. moduleSelector = $allModules.selector || '',
  18. time = new Date().getTime(),
  19. performance = [],
  20. query = arguments[0],
  21. methodInvoked = (typeof query == 'string'),
  22. queryArguments = [].slice.call(arguments, 1),
  23. returnedValue
  24. ;
  25. $allModules
  26. .each(function() {
  27. var
  28. settings = $.extend(true, {}, $.fn.visit.settings, parameters),
  29. error = settings.error,
  30. namespace = settings.namespace,
  31. eventNamespace = '.' + namespace,
  32. moduleNamespace = namespace + '-module',
  33. $module = $(this),
  34. $displays = $(),
  35. element = this,
  36. instance = $module.data(moduleNamespace),
  37. module
  38. ;
  39. module = {
  40. initialize: function() {
  41. if(settings.count) {
  42. module.store(settings.key.count, settings.count);
  43. }
  44. else if(settings.id) {
  45. module.add.id(settings.id);
  46. }
  47. else if(settings.increment && methodInvoked !== 'increment') {
  48. module.increment();
  49. }
  50. module.add.display($module);
  51. module.instantiate();
  52. },
  53. instantiate: function() {
  54. module.verbose('Storing instance of visit module', module);
  55. instance = module;
  56. $module
  57. .data(moduleNamespace, module)
  58. ;
  59. },
  60. destroy: function() {
  61. module.verbose('Destroying instance');
  62. $module
  63. .removeData(moduleNamespace)
  64. ;
  65. },
  66. increment: function(id) {
  67. var
  68. currentValue = module.get.count(),
  69. newValue = +(currentValue) + 1
  70. ;
  71. if(id) {
  72. module.add.id(id);
  73. }
  74. else {
  75. if(newValue > settings.limit && !settings.surpass) {
  76. newValue = settings.limit;
  77. }
  78. module.debug('Incrementing visits', newValue);
  79. module.store(settings.key.count, newValue);
  80. }
  81. },
  82. decrement: function(id) {
  83. var
  84. currentValue = module.get.count(),
  85. newValue = +(currentValue) - 1
  86. ;
  87. if(id) {
  88. module.remove.id(id);
  89. }
  90. else {
  91. module.debug('Removing visit');
  92. module.store(settings.key.count, newValue);
  93. }
  94. },
  95. get: {
  96. count: function() {
  97. return +(module.retrieve(settings.key.count)) || 0;
  98. },
  99. idCount: function(ids) {
  100. ids = ids || module.get.ids();
  101. return ids.length;
  102. },
  103. ids: function(delimitedIDs) {
  104. var
  105. idArray = []
  106. ;
  107. delimitedIDs = delimitedIDs || module.retrieve(settings.key.ids);
  108. if(typeof delimitedIDs === 'string') {
  109. idArray = delimitedIDs.split(settings.delimiter);
  110. }
  111. module.verbose('Found visited ID list', idArray);
  112. return idArray;
  113. },
  114. storageOptions: function(data) {
  115. var
  116. options = {}
  117. ;
  118. if(settings.expires) {
  119. options.expires = settings.expires;
  120. }
  121. if(settings.domain) {
  122. options.domain = settings.domain;
  123. }
  124. if(settings.path) {
  125. options.path = settings.path;
  126. }
  127. return options;
  128. }
  129. },
  130. has: {
  131. visited: function(id, ids) {
  132. var
  133. visited = false
  134. ;
  135. ids = ids || module.get.ids();
  136. if(id !== undefined && ids) {
  137. $.each(ids, function(index, value){
  138. if(value == id) {
  139. visited = true;
  140. }
  141. });
  142. }
  143. return visited;
  144. }
  145. },
  146. set: {
  147. count: function(value) {
  148. module.store(settings.key.count, value);
  149. },
  150. ids: function(value) {
  151. module.store(settings.key.ids, value);
  152. }
  153. },
  154. reset: function() {
  155. module.store(settings.key.count, 0);
  156. module.store(settings.key.ids, null);
  157. },
  158. add: {
  159. id: function(id) {
  160. var
  161. currentIDs = module.retrieve(settings.key.ids),
  162. newIDs = (currentIDs === undefined || currentIDs === '')
  163. ? id
  164. : currentIDs + settings.delimiter + id
  165. ;
  166. if( module.has.visited(id) ) {
  167. module.debug('Unique content already visited, not adding visit', id, currentIDs);
  168. }
  169. else if(id === undefined) {
  170. module.debug('ID is not defined');
  171. }
  172. else {
  173. module.debug('Adding visit to unique content', id);
  174. module.store(settings.key.ids, newIDs);
  175. }
  176. module.set.count( module.get.idCount() );
  177. },
  178. display: function(selector) {
  179. var
  180. $element = $(selector)
  181. ;
  182. if($element.size() > 0 && !$.isWindow($element[0])) {
  183. module.debug('Updating visit count for element', $element);
  184. $displays = ($displays.size() > 0)
  185. ? $displays.add($element)
  186. : $element
  187. ;
  188. }
  189. }
  190. },
  191. remove: {
  192. id: function(id) {
  193. var
  194. currentIDs = module.get.ids(),
  195. newIDs = []
  196. ;
  197. if(id !== undefined && currentIDs !== undefined) {
  198. module.debug('Removing visit to unique content', id, currentIDs);
  199. $.each(currentIDs, function(index, value){
  200. if(value !== id) {
  201. newIDs.push(value);
  202. }
  203. });
  204. newIDs = newIDs.join(settings.delimiter);
  205. module.store(settings.key.ids, newIDs );
  206. }
  207. module.set.count( module.get.idCount() );
  208. }
  209. },
  210. check: {
  211. limit: function(value) {
  212. value = value || module.get.count();
  213. if(settings.limit) {
  214. if(value >= settings.limit) {
  215. module.debug('Pages viewed exceeded limit, firing callback', value, settings.limit);
  216. $.proxy(settings.onLimit, element)(value);
  217. }
  218. module.debug('Limit not reached', value, settings.limit);
  219. $.proxy(settings.onChange, element)(value);
  220. }
  221. module.update.display(value);
  222. }
  223. },
  224. update: {
  225. display: function(value) {
  226. value = value || module.get.count();
  227. if($displays.size() > 0) {
  228. module.debug('Updating displayed view count', $displays);
  229. $displays.html(value);
  230. }
  231. }
  232. },
  233. store: function(key, value) {
  234. var
  235. options = module.get.storageOptions(value)
  236. ;
  237. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  238. window.localStorage.setItem(key, value);
  239. module.debug('Value stored using local storage', key, value);
  240. }
  241. else if($.cookie !== undefined) {
  242. $.cookie(key, value, options);
  243. module.debug('Value stored using cookie', key, value, options);
  244. }
  245. else {
  246. module.error(error.noCookieStorage);
  247. return;
  248. }
  249. if(key == settings.key.count) {
  250. module.check.limit(value);
  251. }
  252. },
  253. retrieve: function(key, value) {
  254. var
  255. storedValue
  256. ;
  257. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  258. storedValue = window.localStorage.getItem(key);
  259. }
  260. // get by cookie
  261. else if($.cookie !== undefined) {
  262. storedValue = $.cookie(key);
  263. }
  264. else {
  265. module.error(error.noCookieStorage);
  266. }
  267. if(storedValue == 'undefined' || storedValue == 'null' || storedValue === undefined || storedValue === null) {
  268. storedValue = undefined;
  269. }
  270. return storedValue;
  271. },
  272. setting: function(name, value) {
  273. if( $.isPlainObject(name) ) {
  274. $.extend(true, settings, name);
  275. }
  276. else if(value !== undefined) {
  277. settings[name] = value;
  278. }
  279. else {
  280. return settings[name];
  281. }
  282. },
  283. internal: function(name, value) {
  284. module.debug('Changing internal', name, value);
  285. if(value !== undefined) {
  286. if( $.isPlainObject(name) ) {
  287. $.extend(true, module, name);
  288. }
  289. else {
  290. module[name] = value;
  291. }
  292. }
  293. else {
  294. return module[name];
  295. }
  296. },
  297. debug: function() {
  298. if(settings.debug) {
  299. if(settings.performance) {
  300. module.performance.log(arguments);
  301. }
  302. else {
  303. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  304. module.debug.apply(console, arguments);
  305. }
  306. }
  307. },
  308. verbose: function() {
  309. if(settings.verbose && settings.debug) {
  310. if(settings.performance) {
  311. module.performance.log(arguments);
  312. }
  313. else {
  314. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  315. module.verbose.apply(console, arguments);
  316. }
  317. }
  318. },
  319. error: function() {
  320. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  321. module.error.apply(console, arguments);
  322. },
  323. performance: {
  324. log: function(message) {
  325. var
  326. currentTime,
  327. executionTime,
  328. previousTime
  329. ;
  330. if(settings.performance) {
  331. currentTime = new Date().getTime();
  332. previousTime = time || currentTime;
  333. executionTime = currentTime - previousTime;
  334. time = currentTime;
  335. performance.push({
  336. 'Name' : message[0],
  337. 'Arguments' : [].slice.call(message, 1) || '',
  338. 'Element' : element,
  339. 'Execution Time' : executionTime
  340. });
  341. }
  342. clearTimeout(module.performance.timer);
  343. module.performance.timer = setTimeout(module.performance.display, 100);
  344. },
  345. display: function() {
  346. var
  347. title = settings.name + ':',
  348. totalTime = 0
  349. ;
  350. time = false;
  351. clearTimeout(module.performance.timer);
  352. $.each(performance, function(index, data) {
  353. totalTime += data['Execution Time'];
  354. });
  355. title += ' ' + totalTime + 'ms';
  356. if(moduleSelector) {
  357. title += ' \'' + moduleSelector + '\'';
  358. }
  359. if($allModules.size() > 1) {
  360. title += ' ' + '(' + $allModules.size() + ')';
  361. }
  362. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  363. console.groupCollapsed(title);
  364. if(console.table) {
  365. console.table(performance);
  366. }
  367. else {
  368. $.each(performance, function(index, data) {
  369. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  370. });
  371. }
  372. console.groupEnd();
  373. }
  374. performance = [];
  375. }
  376. },
  377. invoke: function(query, passedArguments, context) {
  378. var
  379. object = instance,
  380. maxDepth,
  381. found,
  382. response
  383. ;
  384. passedArguments = passedArguments || queryArguments;
  385. context = element || context;
  386. if(typeof query == 'string' && object !== undefined) {
  387. query = query.split(/[\. ]/);
  388. maxDepth = query.length - 1;
  389. $.each(query, function(depth, value) {
  390. var camelCaseValue = (depth != maxDepth)
  391. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  392. : query
  393. ;
  394. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  395. object = object[camelCaseValue];
  396. }
  397. else if( object[camelCaseValue] !== undefined ) {
  398. found = object[camelCaseValue];
  399. return false;
  400. }
  401. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  402. object = object[value];
  403. }
  404. else if( object[value] !== undefined ) {
  405. found = object[value];
  406. return false;
  407. }
  408. else {
  409. return false;
  410. }
  411. });
  412. }
  413. if ( $.isFunction( found ) ) {
  414. response = found.apply(context, passedArguments);
  415. }
  416. else if(found !== undefined) {
  417. response = found;
  418. }
  419. if($.isArray(returnedValue)) {
  420. returnedValue.push(response);
  421. }
  422. else if(returnedValue !== undefined) {
  423. returnedValue = [returnedValue, response];
  424. }
  425. else if(response !== undefined) {
  426. returnedValue = response;
  427. }
  428. return found;
  429. }
  430. };
  431. if(methodInvoked) {
  432. if(instance === undefined) {
  433. module.initialize();
  434. }
  435. module.invoke(query);
  436. }
  437. else {
  438. if(instance !== undefined) {
  439. module.destroy();
  440. }
  441. module.initialize();
  442. }
  443. })
  444. ;
  445. return (returnedValue !== undefined)
  446. ? returnedValue
  447. : this
  448. ;
  449. };
  450. $.fn.visit.settings = {
  451. name : 'Visit',
  452. debug : false,
  453. verbose : true,
  454. performance : true,
  455. namespace : 'visit',
  456. increment : false,
  457. surpass : false,
  458. count : false,
  459. limit : false,
  460. delimiter : '&',
  461. storageMethod : 'localstorage',
  462. key : {
  463. count : 'visit-count',
  464. ids : 'visit-ids'
  465. },
  466. expires : 30,
  467. domain : false,
  468. path : '/',
  469. onLimit : function() {},
  470. onChange : function() {},
  471. error : {
  472. method : 'The method you called is not defined',
  473. missingPersist : 'Using the persist setting requires the inclusion of PersistJS',
  474. noCookieStorage : 'The default storage cookie requires $.cookie to be included.'
  475. }
  476. };
  477. })( jQuery, window , document );