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.

489 lines
14 KiB

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