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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
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 2013 Contributors
  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, this)(value);
  217. }
  218. module.debug('Limit not reached', value, settings.limit);
  219. $.proxy(settings.onChange, this)(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. 'Element' : element,
  337. 'Name' : message[0],
  338. 'Arguments' : [].slice.call(message, 1) || '',
  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 : false,
  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 );