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.

485 lines
14 KiB

  1. /* ******************************
  2. Semantic Module: Dropdown
  3. Author: Jack Lukic
  4. Notes: First Commit May 25, 2013
  5. ****************************** */
  6. ;(function ( $, window, document, undefined ) {
  7. $.fn.sidebar = function(parameters) {
  8. var
  9. $allModules = $(this),
  10. settings = ( $.isPlainObject(parameters) )
  11. ? $.extend(true, {}, $.fn.sidebar.settings, parameters)
  12. : $.fn.sidebar.settings,
  13. selector = settings.selector,
  14. className = settings.className,
  15. namespace = settings.namespace,
  16. error = settings.error,
  17. eventNamespace = '.' + namespace,
  18. moduleNamespace = 'module-' + namespace,
  19. moduleSelector = $allModules.selector || '',
  20. time = new Date().getTime(),
  21. performance = [],
  22. query = arguments[0],
  23. methodInvoked = (typeof query == 'string'),
  24. queryArguments = [].slice.call(arguments, 1),
  25. invokedResponse
  26. ;
  27. $allModules
  28. .each(function() {
  29. var
  30. $module = $(this),
  31. $body = $('body'),
  32. $head = $('head'),
  33. $style = $('style[title=' + namespace + ']'),
  34. element = this,
  35. instance = $module.data(moduleNamespace),
  36. module
  37. ;
  38. module = {
  39. initialize: function() {
  40. module.debug('Initializing sidebar', $module);
  41. module.instantiate();
  42. },
  43. instantiate: function() {
  44. module.verbose('Storing instance of module', module);
  45. instance = module;
  46. $module
  47. .data(moduleNamespace, module)
  48. ;
  49. },
  50. destroy: function() {
  51. module.verbose('Destroying previous module for', $module);
  52. $module
  53. .off(eventNamespace)
  54. .removeData(moduleNamespace)
  55. ;
  56. },
  57. refresh: function() {
  58. module.verbose('Refreshing selector cache');
  59. $style = $('style[title=' + namespace + ']');
  60. },
  61. attach: {
  62. events: function(selector, event) {
  63. var
  64. $toggle = $(selector)
  65. ;
  66. event = $.isFunction(module[event])
  67. ? module[event]
  68. : module.toggle
  69. ;
  70. if($toggle.size() > 0) {
  71. module.debug('Attaching sidebar events to element', selector, event);
  72. $toggle
  73. .off(eventNamespace)
  74. .on('click' + eventNamespace, event)
  75. ;
  76. }
  77. else {
  78. module.error(error.notFound);
  79. }
  80. }
  81. },
  82. show: function() {
  83. module.debug('Showing sidebar');
  84. if(module.is.closed()) {
  85. if(!settings.overlay) {
  86. module.pushPage();
  87. }
  88. module.set.active();
  89. }
  90. else {
  91. module.debug('Sidebar is already visible');
  92. }
  93. },
  94. hide: function() {
  95. if(module.is.open()) {
  96. if(!settings.overlay) {
  97. module.pullPage();
  98. module.remove.pushed();
  99. }
  100. module.remove.active();
  101. }
  102. },
  103. toggle: function() {
  104. if(module.is.closed()) {
  105. module.show();
  106. }
  107. else {
  108. module.hide();
  109. }
  110. },
  111. pushPage: function() {
  112. var
  113. direction = module.get.direction(),
  114. distance = (module.is.vertical())
  115. ? $module.outerHeight()
  116. : $module.outerWidth()
  117. ;
  118. if(settings.useCSS) {
  119. module.debug('Using CSS to animate body');
  120. module.add.bodyCSS(direction, distance);
  121. module.set.pushed();
  122. }
  123. else {
  124. module.animatePage(direction, distance, module.set.pushed);
  125. }
  126. },
  127. pullPage: function() {
  128. var
  129. direction = module.get.direction()
  130. ;
  131. if(settings.useCSS) {
  132. module.debug('Resetting body position css');
  133. module.remove.bodyCSS();
  134. }
  135. else {
  136. module.debug('Resetting body position using javascript');
  137. module.animatePage(direction, 0);
  138. }
  139. module.remove.pushed();
  140. },
  141. animatePage: function(direction, distance) {
  142. var
  143. animateSettings = {}
  144. ;
  145. animateSettings['padding-' + direction] = distance;
  146. module.debug('Using javascript to animate body', animateSettings);
  147. $body
  148. .animate(animateSettings, settings.duration, module.set.pushed)
  149. ;
  150. },
  151. add: {
  152. bodyCSS: function(direction, distance) {
  153. var
  154. style
  155. ;
  156. if(direction !== className.bottom) {
  157. style = ''
  158. + '<style title="' + namespace + '">'
  159. + 'body.pushed {'
  160. + ' margin-' + direction + ': ' + distance + 'px !important;'
  161. + '}'
  162. + '</style>'
  163. ;
  164. }
  165. $head.append(style);
  166. module.debug('Adding body css to head', $style);
  167. }
  168. },
  169. remove: {
  170. bodyCSS: function() {
  171. module.debug('Removing body css styles', $style);
  172. module.refresh();
  173. $style.remove();
  174. },
  175. active: function() {
  176. $module.removeClass(className.active);
  177. },
  178. pushed: function() {
  179. module.verbose('Removing body push state', module.get.direction());
  180. $body
  181. .removeClass(className[ module.get.direction() ])
  182. .removeClass(className.pushed)
  183. ;
  184. }
  185. },
  186. set: {
  187. active: function() {
  188. $module.addClass(className.active);
  189. },
  190. pushed: function() {
  191. module.verbose('Adding body push state', module.get.direction());
  192. $body
  193. .addClass(className[ module.get.direction() ])
  194. .addClass(className.pushed)
  195. ;
  196. }
  197. },
  198. get: {
  199. direction: function() {
  200. if($module.hasClass(className.top)) {
  201. return className.top;
  202. }
  203. else if($module.hasClass(className.right)) {
  204. return className.right;
  205. }
  206. else if($module.hasClass(className.bottom)) {
  207. return className.bottom;
  208. }
  209. else {
  210. return className.left;
  211. }
  212. },
  213. transitionEvent: function() {
  214. var
  215. element = document.createElement('element'),
  216. transitions = {
  217. 'transition' :'transitionend',
  218. 'OTransition' :'oTransitionEnd',
  219. 'MozTransition' :'transitionend',
  220. 'WebkitTransition' :'webkitTransitionEnd'
  221. },
  222. transition
  223. ;
  224. for(transition in transitions){
  225. if( element.style[transition] !== undefined ){
  226. return transitions[transition];
  227. }
  228. }
  229. }
  230. },
  231. is: {
  232. open: function() {
  233. return $module.is(':animated') || $module.hasClass(className.active);
  234. },
  235. closed: function() {
  236. return !module.is.open();
  237. },
  238. vertical: function() {
  239. return $module.hasClass(className.top);
  240. }
  241. },
  242. setting: function(name, value) {
  243. if(value !== undefined) {
  244. if( $.isPlainObject(name) ) {
  245. $.extend(true, settings, name);
  246. }
  247. else {
  248. settings[name] = value;
  249. }
  250. }
  251. else {
  252. return settings[name];
  253. }
  254. },
  255. internal: function(name, value) {
  256. if(value !== undefined) {
  257. if( $.isPlainObject(name) ) {
  258. $.extend(true, module, name);
  259. }
  260. else {
  261. module[name] = value;
  262. }
  263. }
  264. else {
  265. return module[name];
  266. }
  267. },
  268. debug: function() {
  269. if(settings.debug) {
  270. if(settings.performance) {
  271. module.performance.log(arguments);
  272. }
  273. else {
  274. module.debug = Function.prototype.bind.call(console.info, console, settings.moduleName + ':');
  275. module.debug.apply(console, arguments);
  276. }
  277. }
  278. },
  279. verbose: function() {
  280. if(settings.verbose && settings.debug) {
  281. if(settings.performance) {
  282. module.performance.log(arguments);
  283. }
  284. else {
  285. module.verbose = Function.prototype.bind.call(console.info, console, settings.moduleName + ':');
  286. module.verbose.apply(console, arguments);
  287. }
  288. }
  289. },
  290. error: function() {
  291. module.error = Function.prototype.bind.call(console.error, console, settings.moduleName + ':');
  292. module.error.apply(console, arguments);
  293. },
  294. performance: {
  295. log: function(message) {
  296. var
  297. currentTime,
  298. executionTime,
  299. previousTime
  300. ;
  301. if(settings.performance) {
  302. currentTime = new Date().getTime();
  303. previousTime = time || currentTime;
  304. executionTime = currentTime - previousTime;
  305. time = currentTime;
  306. performance.push({
  307. 'Element' : element,
  308. 'Name' : message[0],
  309. 'Arguments' : [].slice.call(message, 1) || '',
  310. 'Execution Time' : executionTime
  311. });
  312. }
  313. clearTimeout(module.performance.timer);
  314. module.performance.timer = setTimeout(module.performance.display, 100);
  315. },
  316. display: function() {
  317. var
  318. title = settings.moduleName + ':',
  319. totalTime = 0
  320. ;
  321. time = false;
  322. clearTimeout(module.performance.timer);
  323. $.each(performance, function(index, data) {
  324. totalTime += data['Execution Time'];
  325. });
  326. title += ' ' + totalTime + 'ms';
  327. if(moduleSelector) {
  328. title += ' \'' + moduleSelector + '\'';
  329. }
  330. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  331. console.groupCollapsed(title);
  332. if(console.table) {
  333. console.table(performance);
  334. }
  335. else {
  336. $.each(performance, function(index, data) {
  337. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  338. });
  339. }
  340. console.groupEnd();
  341. }
  342. performance = [];
  343. }
  344. },
  345. invoke: function(query, passedArguments, context) {
  346. var
  347. maxDepth,
  348. found,
  349. response
  350. ;
  351. passedArguments = passedArguments || queryArguments;
  352. context = element || context;
  353. if(typeof query == 'string' && instance !== undefined) {
  354. query = query.split(/[\. ]/);
  355. maxDepth = query.length - 1;
  356. $.each(query, function(depth, value) {
  357. var camelCaseValue = (depth != maxDepth)
  358. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  359. : query
  360. ;
  361. if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
  362. instance = instance[value];
  363. }
  364. else if( $.isPlainObject( instance[camelCaseValue] ) && (depth != maxDepth) ) {
  365. instance = instance[camelCaseValue];
  366. }
  367. else if( instance[value] !== undefined ) {
  368. found = instance[value];
  369. return false;
  370. }
  371. else if( instance[camelCaseValue] !== undefined ) {
  372. found = instance[camelCaseValue];
  373. return false;
  374. }
  375. else {
  376. module.error(error.method);
  377. return false;
  378. }
  379. });
  380. }
  381. if ( $.isFunction( found ) ) {
  382. response = found.apply(context, passedArguments);
  383. }
  384. else if(found !== undefined) {
  385. response = found;
  386. }
  387. if($.isArray(invokedResponse)) {
  388. invokedResponse.push(response);
  389. }
  390. else if(typeof invokedResponse == 'string') {
  391. invokedResponse = [invokedResponse, response];
  392. }
  393. else if(response !== undefined) {
  394. invokedResponse = response;
  395. }
  396. return found;
  397. }
  398. };
  399. if(methodInvoked) {
  400. if(instance === undefined) {
  401. module.initialize();
  402. }
  403. module.invoke(query);
  404. }
  405. else {
  406. if(instance !== undefined) {
  407. module.destroy();
  408. }
  409. module.initialize();
  410. }
  411. })
  412. ;
  413. return (invokedResponse !== undefined)
  414. ? invokedResponse
  415. : this
  416. ;
  417. };
  418. $.fn.sidebar.settings = {
  419. moduleName : 'Sidebar',
  420. namespace : 'sidebar',
  421. verbose : true,
  422. debug : true,
  423. performance : true,
  424. useCSS : true,
  425. overlay : false,
  426. duration : 300,
  427. side : 'left',
  428. onChange : function(){},
  429. onShow : function(){},
  430. onHide : function(){},
  431. className: {
  432. active : 'active',
  433. pushed : 'pushed',
  434. top : 'top',
  435. left : 'left',
  436. right : 'right',
  437. bottom : 'bottom'
  438. },
  439. error : {
  440. method : 'The method you called is not defined.',
  441. notFound : 'There were no elements that matched the specified selector'
  442. }
  443. };
  444. })( jQuery, window , document );