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.

499 lines
15 KiB

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.id) {
  42. module.add.id(settings.id);
  43. }
  44. else if(settings.count) {
  45. module.store(settings.key.count, settings.count);
  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);
  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 && module.has.visited(id) ) {
  72. module.debug('Unique content already visited, skipping increment', 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 && !module.has.visited(id) ) {
  88. module.debug('Decrement skipped, content has not been visited', 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. ids: function(delimitedIDs) {
  100. delimitedIDs = delimitedIDs || module.retrieve(settings.key.ids);
  101. if(typeof delimitedIDs === 'string') {
  102. return delimitedIDs.split(settings.delimiter);
  103. }
  104. else {
  105. return [];
  106. }
  107. },
  108. storageOptions: function(data) {
  109. var
  110. options = {}
  111. ;
  112. if(settings.expires) {
  113. options.expires = settings.expires;
  114. }
  115. if(settings.domain) {
  116. options.domain = settings.domain;
  117. }
  118. if(settings.path) {
  119. options.path = settings.path;
  120. }
  121. return options;
  122. }
  123. },
  124. has: {
  125. visited: function(id, ids) {
  126. var
  127. visited = false
  128. ;
  129. ids = ids || module.get.ids();
  130. if(id !== undefined && ids) {
  131. $.each(ids, function(index, value){
  132. if(value == id) {
  133. visited = true;
  134. }
  135. });
  136. }
  137. return visited;
  138. }
  139. },
  140. set: {
  141. count: function(value) {
  142. module.store(settings.key.count, value);
  143. },
  144. ids: function(value) {
  145. module.store(settings.key.ids, value);
  146. }
  147. },
  148. reset: function() {
  149. module.store(settings.key.count, 0);
  150. module.store(settings.key.ids, '');
  151. },
  152. add: {
  153. id: function(id) {
  154. var
  155. currentIDs = module.retrieve(settings.key.ids),
  156. newIDs = (currentIDs === undefined || currentIDs === '')
  157. ? id
  158. : currentIDs + settings.delimiter + id
  159. ;
  160. if( module.has.visited(id) ) {
  161. module.debug('Unique content already visited, not adding visit', id);
  162. }
  163. else if(id === undefined) {
  164. module.debug('ID is not defined');
  165. }
  166. else {
  167. module.debug('Adding visit to unique content', id);
  168. module.increment(id);
  169. module.store(settings.key.ids, newIDs);
  170. }
  171. },
  172. display: function(selector) {
  173. var
  174. $element = $(selector)
  175. ;
  176. if($element.size() > 0 && !$.isWindow($element[0])) {
  177. module.debug('Updating visit count for element', $element);
  178. $displays = ($displays.size() > 0)
  179. ? $displays.add($element)
  180. : $element
  181. ;
  182. }
  183. module.update.display();
  184. }
  185. },
  186. remove: {
  187. id: function(id) {
  188. var
  189. currentIDs = module.get.ids(),
  190. newIDs = []
  191. ;
  192. if(id !== undefined && currentIDs !== undefined) {
  193. module.debug('Removing visit to unique content', id, currentIDs);
  194. $.each(currentIDs, function(index, value){
  195. if(value !== id) {
  196. newIDs.push(value);
  197. }
  198. });
  199. newIDs = newIDs.join(settings.delimiter);
  200. module.store(settings.key.ids, newIDs );
  201. }
  202. }
  203. },
  204. update: {
  205. display: function(value) {
  206. value = value || module.get.count();
  207. if($displays.size() > 0) {
  208. module.debug('Updating displayed view count', $displays);
  209. $displays.html(value);
  210. }
  211. if(value >= settings.limit) {
  212. module.debug('Pages viewed exceeded limit, firing callback', value, settings.limit);
  213. $.proxy(settings.onLimit, this)(value);
  214. }
  215. $.proxy(settings.onChange, this)(value);
  216. }
  217. },
  218. store: function(key, value) {
  219. var
  220. options = module.get.storageOptions(value)
  221. ;
  222. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  223. window.localStorage.setItem(key, value);
  224. module.debug('Value stored using local storage', key, value);
  225. }
  226. else if($.cookie !== undefined) {
  227. $.cookie(key, value, options);
  228. module.debug('Value stored using cookie', key, value, options);
  229. }
  230. else {
  231. module.error(error.noCookieStorage);
  232. return;
  233. }
  234. if(key == settings.key.count) {
  235. module.update.display(value);
  236. }
  237. },
  238. retrieve: function(key, value) {
  239. var
  240. storedValue
  241. ;
  242. if(settings.storageMethod == 'localstorage' && window.localStorage !== undefined) {
  243. storedValue = window.localStorage.getItem(key);
  244. }
  245. // get by cookie
  246. else if($.cookie !== undefined) {
  247. storedValue = $.cookie(key);
  248. }
  249. else {
  250. module.error(error.noCookieStorage);
  251. }
  252. if(storedValue == 'undefined' || storedValue == 'null' || storedValue === undefined || storedValue === null) {
  253. storedValue = undefined;
  254. }
  255. return storedValue;
  256. },
  257. setting: function(name, value) {
  258. if( $.isPlainObject(name) ) {
  259. $.extend(true, settings, name);
  260. }
  261. else if(value !== undefined) {
  262. settings[name] = value;
  263. }
  264. else {
  265. return settings[name];
  266. }
  267. },
  268. internal: function(name, value) {
  269. module.debug('Changing internal', name, value);
  270. if(value !== undefined) {
  271. if( $.isPlainObject(name) ) {
  272. $.extend(true, module, name);
  273. }
  274. else {
  275. module[name] = value;
  276. }
  277. }
  278. else {
  279. return module[name];
  280. }
  281. },
  282. debug: function() {
  283. if(settings.debug) {
  284. if(settings.performance) {
  285. module.performance.log(arguments);
  286. }
  287. else {
  288. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  289. module.debug.apply(console, arguments);
  290. }
  291. }
  292. },
  293. verbose: function() {
  294. if(settings.verbose && settings.debug) {
  295. if(settings.performance) {
  296. module.performance.log(arguments);
  297. }
  298. else {
  299. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  300. module.verbose.apply(console, arguments);
  301. }
  302. }
  303. },
  304. error: function() {
  305. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  306. module.error.apply(console, arguments);
  307. },
  308. performance: {
  309. log: function(message) {
  310. var
  311. currentTime,
  312. executionTime,
  313. previousTime
  314. ;
  315. if(settings.performance) {
  316. currentTime = new Date().getTime();
  317. previousTime = time || currentTime;
  318. executionTime = currentTime - previousTime;
  319. time = currentTime;
  320. performance.push({
  321. 'Element' : element,
  322. 'Name' : message[0],
  323. 'Arguments' : [].slice.call(message, 1) || '',
  324. 'Execution Time' : executionTime
  325. });
  326. }
  327. clearTimeout(module.performance.timer);
  328. module.performance.timer = setTimeout(module.performance.display, 100);
  329. },
  330. display: function() {
  331. var
  332. title = settings.name + ':',
  333. totalTime = 0
  334. ;
  335. time = false;
  336. clearTimeout(module.performance.timer);
  337. $.each(performance, function(index, data) {
  338. totalTime += data['Execution Time'];
  339. });
  340. title += ' ' + totalTime + 'ms';
  341. if(moduleSelector) {
  342. title += ' \'' + moduleSelector + '\'';
  343. }
  344. if($allModules.size() > 1) {
  345. title += ' ' + '(' + $allModules.size() + ')';
  346. }
  347. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  348. console.groupCollapsed(title);
  349. if(console.table) {
  350. console.table(performance);
  351. }
  352. else {
  353. $.each(performance, function(index, data) {
  354. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  355. });
  356. }
  357. console.groupEnd();
  358. }
  359. performance = [];
  360. }
  361. },
  362. invoke: function(query, passedArguments, context) {
  363. var
  364. object = instance,
  365. maxDepth,
  366. found,
  367. response
  368. ;
  369. passedArguments = passedArguments || queryArguments;
  370. context = element || context;
  371. if(typeof query == 'string' && object !== undefined) {
  372. query = query.split(/[\. ]/);
  373. maxDepth = query.length - 1;
  374. $.each(query, function(depth, value) {
  375. var camelCaseValue = (depth != maxDepth)
  376. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  377. : query
  378. ;
  379. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  380. object = object[camelCaseValue];
  381. }
  382. else if( object[camelCaseValue] !== undefined ) {
  383. found = object[camelCaseValue];
  384. return false;
  385. }
  386. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  387. object = object[value];
  388. }
  389. else if( object[value] !== undefined ) {
  390. found = object[value];
  391. return false;
  392. }
  393. else {
  394. return false;
  395. }
  396. });
  397. }
  398. if ( $.isFunction( found ) ) {
  399. response = found.apply(context, passedArguments);
  400. }
  401. else if(found !== undefined) {
  402. response = found;
  403. }
  404. if($.isArray(returnedValue)) {
  405. returnedValue.push(response);
  406. }
  407. else if(returnedValue !== undefined) {
  408. returnedValue = [returnedValue, response];
  409. }
  410. else if(response !== undefined) {
  411. returnedValue = response;
  412. }
  413. return found;
  414. }
  415. };
  416. if(methodInvoked) {
  417. if(instance === undefined) {
  418. module.initialize();
  419. }
  420. module.invoke(query);
  421. }
  422. else {
  423. if(instance !== undefined) {
  424. module.destroy();
  425. }
  426. module.initialize();
  427. }
  428. })
  429. ;
  430. return (returnedValue !== undefined)
  431. ? returnedValue
  432. : this
  433. ;
  434. };
  435. $.fn.visit.settings = {
  436. name : 'Visit',
  437. debug : false,
  438. verbose : true,
  439. performance : true,
  440. namespace : 'visit',
  441. increment : true,
  442. surpass : false,
  443. count : false,
  444. limit : false,
  445. delimiter : '&',
  446. storageMethod : 'localstorage',
  447. key : {
  448. count : 'visit-count',
  449. ids : 'visit-ids'
  450. },
  451. expires : 30,
  452. domain : false,
  453. path : false,
  454. onLimit : function() {},
  455. onChange : function() {},
  456. error : {
  457. method : 'The method you called is not defined',
  458. missingPersist : 'Using the persist setting requires the inclusion of PersistJS',
  459. noCookieStorage : 'The default storage cookie requires $.cookie to be included.'
  460. }
  461. };
  462. })( jQuery, window , document );