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.

700 lines
21 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
10 years ago
10 years ago
10 years ago
  1. /*
  2. * # Semantic - Sidebar
  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. "use strict";
  13. $.fn.sidebar = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. $head = $('head'),
  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. requestAnimationFrame = window.requestAnimationFrame
  24. || window.mozRequestAnimationFrame
  25. || window.webkitRequestAnimationFrame
  26. || window.msRequestAnimationFrame
  27. || function(callback) { setTimeout(callback, 0); },
  28. returnedValue
  29. ;
  30. $allModules
  31. .each(function() {
  32. var
  33. settings = ( $.isPlainObject(parameters) )
  34. ? $.extend(true, {}, $.fn.sidebar.settings, parameters)
  35. : $.extend({}, $.fn.sidebar.settings),
  36. selector = settings.selector,
  37. className = settings.className,
  38. namespace = settings.namespace,
  39. error = settings.error,
  40. eventNamespace = '.' + namespace,
  41. moduleNamespace = 'module-' + namespace,
  42. $module = $(this),
  43. $context = $(settings.context),
  44. $style = $('style[title=' + namespace + ']'),
  45. $sidebars = $context.children(selector.sidebar),
  46. $pusher = $context.children(selector.pusher),
  47. $page = $pusher.children(selector.page),
  48. $fixed = $pusher.find(selector.fixed),
  49. element = this,
  50. instance = $module.data(moduleNamespace),
  51. transitionEnd,
  52. module
  53. ;
  54. module = {
  55. initialize: function() {
  56. module.debug('Initializing sidebar', $module);
  57. transitionEnd = module.get.transitionEvent();
  58. module.setup.context();
  59. // avoid locking rendering to change layout if included in onReady
  60. requestAnimationFrame(module.setup.layout);
  61. module.instantiate();
  62. },
  63. instantiate: function() {
  64. module.verbose('Storing instance of module', module);
  65. instance = module;
  66. $module
  67. .data(moduleNamespace, module)
  68. ;
  69. },
  70. destroy: function() {
  71. module.verbose('Destroying previous module for', $module);
  72. $module
  73. .off(eventNamespace)
  74. .removeData(moduleNamespace)
  75. ;
  76. },
  77. event: {
  78. clickaway: function(event) {
  79. if( $module.find(event.target).size() === 0 && $(event.target).filter($module).size() === 0 ) {
  80. module.verbose('User clicked on dimmed page');
  81. module.hide();
  82. }
  83. },
  84. scroll: function(event) {
  85. if( $module.find(event.target).size() === 0 && $(event.target).filter($module).size() === 0 ) {
  86. event.preventDefault();
  87. }
  88. }
  89. },
  90. bind: {
  91. clickaway: function() {
  92. $(window)
  93. .on('DOMMouseScroll' + eventNamespace, module.event.scroll)
  94. ;
  95. $context
  96. .on('click' + eventNamespace, module.event.clickaway)
  97. .on('touchend' + eventNamespace, module.event.clickaway)
  98. ;
  99. }
  100. },
  101. unbind: {
  102. clickaway: function() {
  103. $context
  104. .off(eventNamespace)
  105. ;
  106. $(window).off('DOMMouseScroll' + eventNamespace);
  107. }
  108. },
  109. refresh: function() {
  110. module.verbose('Refreshing selector cache');
  111. $context = $(settings.context);
  112. $style = $('style[title=' + namespace + ']');
  113. $sidebars = $context.children(selector.sidebar);
  114. $pusher = $context.children(selector.pusher);
  115. $page = $pusher.children(selector.page);
  116. $fixed = $pusher.find(selector.fixed);
  117. },
  118. repaint: function() {
  119. module.verbose('Forcing repaint event');
  120. var fakeAssignment = $context[0].offsetWidth;
  121. },
  122. setup: {
  123. layout: function() {
  124. if( $context.find(selector.pusher).size() === 0 ) {
  125. module.debug('Adding wrapper element for sidebar');
  126. $pusher = $('<div class="pusher" />');
  127. $page = $('<div class="page" />');
  128. $pusher.append($page);
  129. $context
  130. .children()
  131. .not(selector.omitted)
  132. .not($sidebars)
  133. .wrapAll($pusher)
  134. ;
  135. }
  136. if($module.prevAll($page)[0] !== $page[0]) {
  137. module.debug('Moved sidebar to correct parent element');
  138. $module.detach().prependTo($context);
  139. }
  140. module.refresh();
  141. },
  142. context: function() {
  143. module.verbose('Adding pusshable class to wrapper');
  144. $context.addClass(className.pushable);
  145. }
  146. },
  147. attachEvents: function(selector, event) {
  148. var
  149. $toggle = $(selector)
  150. ;
  151. event = $.isFunction(module[event])
  152. ? module[event]
  153. : module.toggle
  154. ;
  155. if($toggle.size() > 0) {
  156. module.debug('Attaching sidebar events to element', selector, event);
  157. $toggle
  158. .off(eventNamespace)
  159. .on('click' + eventNamespace, event)
  160. ;
  161. }
  162. else {
  163. module.error(error.notFound);
  164. }
  165. },
  166. show: function(callback) {
  167. callback = $.isFunction(callback)
  168. ? callback
  169. : function(){}
  170. ;
  171. module.debug('Showing sidebar', callback);
  172. if(module.is.closed()) {
  173. if(settings.overlay) {
  174. settings.transition = 'overlay';
  175. }
  176. if(settings.transition !== 'overlay') {
  177. module.hideAll();
  178. }
  179. module.pushPage(function() {
  180. module.set.active();
  181. $.proxy(callback, element)();
  182. $.proxy(settings.onShow, element)();
  183. });
  184. $.proxy(settings.onChange, element)();
  185. $.proxy(settings.onVisible, element)();
  186. }
  187. else {
  188. module.debug('Sidebar is already visible');
  189. }
  190. },
  191. hide: function(callback) {
  192. callback = $.isFunction(callback)
  193. ? callback
  194. : function(){}
  195. ;
  196. module.debug('Hiding sidebar', callback);
  197. if(module.is.visible()) {
  198. module.pullPage(function() {
  199. $.proxy(callback, element)();
  200. $.proxy(settings.onHidden, element)();
  201. });
  202. $.proxy(settings.onChange, element)();
  203. $.proxy(settings.onHide, element)();
  204. }
  205. },
  206. hideAll: function() {
  207. var
  208. $visibleSidebars = $sidebars.find('.' + className.visible)
  209. ;
  210. $visibleSidebars
  211. .sidebar('hide')
  212. ;
  213. },
  214. toggle: function() {
  215. module.verbose('Determining toggled direction');
  216. if(module.is.closed()) {
  217. module.show();
  218. }
  219. else {
  220. module.hide();
  221. }
  222. },
  223. pushPage: function(callback) {
  224. var
  225. $transition = (settings.transition == 'safe')
  226. ? $context
  227. : (settings.transition == 'overlay')
  228. ? $module
  229. : $pusher,
  230. transition
  231. ;
  232. callback = $.isFunction(callback)
  233. ? callback
  234. : function(){}
  235. ;
  236. transition = function() {
  237. module.set.visible();
  238. module.set.transition();
  239. module.set.direction();
  240. requestAnimationFrame(function() {
  241. module.set.inward();
  242. module.set.pushed();
  243. });
  244. };
  245. $transition
  246. .on(transitionEnd, function(event) {
  247. if( event.target == $transition[0] ) {
  248. $transition.off(transitionEnd);
  249. module.remove.inward();
  250. module.bind.clickaway();
  251. $.proxy(callback, element)();
  252. }
  253. })
  254. ;
  255. module.verbose('Adding context push state', $context);
  256. if(settings.transition === 'overlay') {
  257. requestAnimationFrame(transition);
  258. }
  259. else {
  260. if(settings.transition !== 'safe') {
  261. $module.scrollTop(0);
  262. if(settings.workaround === 'scroll') {
  263. window.scrollTo(0, 0);
  264. }
  265. }
  266. module.remove.allVisible();
  267. requestAnimationFrame(transition);
  268. }
  269. },
  270. pullPage: function(callback) {
  271. var
  272. $transition = (settings.transition == 'safe')
  273. ? $context
  274. : (settings.transition == 'overlay')
  275. ? $module
  276. : $pusher
  277. ;
  278. callback = $.isFunction(callback)
  279. ? callback
  280. : function(){}
  281. ;
  282. module.verbose('Removing context push state', module.get.direction());
  283. module.unbind.clickaway();
  284. $transition
  285. .on(transitionEnd, function(event) {
  286. if( event.target == $transition[0] ) {
  287. $transition.off(transitionEnd);
  288. module.remove.transition();
  289. module.remove.direction();
  290. module.remove.outward();
  291. module.remove.visible();
  292. $.proxy(callback, element)();
  293. }
  294. })
  295. ;
  296. module.set.outward();
  297. module.remove.active();
  298. module.remove.pushed();
  299. },
  300. set: {
  301. active: function() {
  302. $context.addClass(className.active);
  303. },
  304. direction: function(direction) {
  305. direction = direction || module.get.direction();
  306. $context.addClass(className[direction]);
  307. },
  308. visible: function() {
  309. $module.addClass(className.visible);
  310. },
  311. transition: function(transition) {
  312. transition = transition || ( module.is.mobile() )
  313. ? settings.mobileTransition
  314. : settings.transition
  315. ;
  316. $context.addClass(transition);
  317. },
  318. inward: function() {
  319. $context.addClass(className.inward);
  320. },
  321. outward: function() {
  322. $context.addClass(className.outward);
  323. },
  324. pushed: function() {
  325. $context.addClass(className.pushed);
  326. }
  327. },
  328. remove: {
  329. active: function() {
  330. $context.removeClass(className.active);
  331. },
  332. visible: function() {
  333. $module.removeClass(className.visible);
  334. },
  335. allVisible: function() {
  336. if($sidebars.hasClass(className.visible)) {
  337. module.debug('Other sidebars visible, hiding');
  338. $sidebars.removeClass(className.visible);
  339. }
  340. },
  341. transition: function(transition) {
  342. transition = transition || ( module.is.mobile() )
  343. ? settings.mobileTransition
  344. : settings.transition
  345. ;
  346. $context.removeClass(transition);
  347. },
  348. pushed: function() {
  349. $context.removeClass(className.pushed);
  350. },
  351. inward: function() {
  352. $context.removeClass(className.inward);
  353. },
  354. outward: function() {
  355. $context.removeClass(className.outward);
  356. },
  357. direction: function(direction) {
  358. direction = direction || module.get.direction();
  359. $context.removeClass(className[direction]);
  360. }
  361. },
  362. get: {
  363. direction: function() {
  364. if($module.hasClass(className.top)) {
  365. return className.top;
  366. }
  367. else if($module.hasClass(className.right)) {
  368. return className.right;
  369. }
  370. else if($module.hasClass(className.bottom)) {
  371. return className.bottom;
  372. }
  373. else {
  374. return className.left;
  375. }
  376. },
  377. transitionEvent: function() {
  378. var
  379. element = document.createElement('element'),
  380. transitions = {
  381. 'transition' :'transitionend',
  382. 'OTransition' :'oTransitionEnd',
  383. 'MozTransition' :'transitionend',
  384. 'WebkitTransition' :'webkitTransitionEnd'
  385. },
  386. transition
  387. ;
  388. for(transition in transitions){
  389. if( element.style[transition] !== undefined ){
  390. return transitions[transition];
  391. }
  392. }
  393. }
  394. },
  395. is: {
  396. mobile: function() {
  397. var
  398. userAgent = navigator.userAgent,
  399. mobileRegExp = /Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/,
  400. isMobile = mobileRegExp.test(userAgent)
  401. ;
  402. if(isMobile) {
  403. module.verbose('Browser was found to be mobile', userAgent);
  404. return true;
  405. }
  406. else {
  407. module.verbose('Browser is not mobile, using regular transition', userAgent);
  408. return false;
  409. }
  410. },
  411. closed: function() {
  412. return !module.is.visible();
  413. },
  414. visible: function() {
  415. return $module.hasClass(className.visible);
  416. },
  417. vertical: function() {
  418. return $module.hasClass(className.top);
  419. },
  420. inward: function() {
  421. return $context.hasClass(className.inward);
  422. },
  423. outward: function() {
  424. return $context.hasClass(className.outward);
  425. },
  426. animating: function() {
  427. return module.is.inward() || module.is.outward();
  428. }
  429. },
  430. setting: function(name, value) {
  431. module.debug('Changing setting', name, value);
  432. if( $.isPlainObject(name) ) {
  433. $.extend(true, settings, name);
  434. }
  435. else if(value !== undefined) {
  436. settings[name] = value;
  437. }
  438. else {
  439. return settings[name];
  440. }
  441. },
  442. internal: function(name, value) {
  443. if( $.isPlainObject(name) ) {
  444. $.extend(true, module, name);
  445. }
  446. else if(value !== undefined) {
  447. module[name] = value;
  448. }
  449. else {
  450. return module[name];
  451. }
  452. },
  453. debug: function() {
  454. if(settings.debug) {
  455. if(settings.performance) {
  456. module.performance.log(arguments);
  457. }
  458. else {
  459. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  460. module.debug.apply(console, arguments);
  461. }
  462. }
  463. },
  464. verbose: function() {
  465. if(settings.verbose && settings.debug) {
  466. if(settings.performance) {
  467. module.performance.log(arguments);
  468. }
  469. else {
  470. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  471. module.verbose.apply(console, arguments);
  472. }
  473. }
  474. },
  475. error: function() {
  476. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  477. module.error.apply(console, arguments);
  478. },
  479. performance: {
  480. log: function(message) {
  481. var
  482. currentTime,
  483. executionTime,
  484. previousTime
  485. ;
  486. if(settings.performance) {
  487. currentTime = new Date().getTime();
  488. previousTime = time || currentTime;
  489. executionTime = currentTime - previousTime;
  490. time = currentTime;
  491. performance.push({
  492. 'Element' : element,
  493. 'Name' : message[0],
  494. 'Arguments' : [].slice.call(message, 1) || '',
  495. 'Execution Time' : executionTime
  496. });
  497. }
  498. clearTimeout(module.performance.timer);
  499. module.performance.timer = setTimeout(module.performance.display, 100);
  500. },
  501. display: function() {
  502. var
  503. title = settings.name + ':',
  504. totalTime = 0
  505. ;
  506. time = false;
  507. clearTimeout(module.performance.timer);
  508. $.each(performance, function(index, data) {
  509. totalTime += data['Execution Time'];
  510. });
  511. title += ' ' + totalTime + 'ms';
  512. if(moduleSelector) {
  513. title += ' \'' + moduleSelector + '\'';
  514. }
  515. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  516. console.groupCollapsed(title);
  517. if(console.table) {
  518. console.table(performance);
  519. }
  520. else {
  521. $.each(performance, function(index, data) {
  522. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  523. });
  524. }
  525. console.groupEnd();
  526. }
  527. performance = [];
  528. }
  529. },
  530. invoke: function(query, passedArguments, context) {
  531. var
  532. object = instance,
  533. maxDepth,
  534. found,
  535. response
  536. ;
  537. passedArguments = passedArguments || queryArguments;
  538. context = element || context;
  539. if(typeof query == 'string' && object !== undefined) {
  540. query = query.split(/[\. ]/);
  541. maxDepth = query.length - 1;
  542. $.each(query, function(depth, value) {
  543. var camelCaseValue = (depth != maxDepth)
  544. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  545. : query
  546. ;
  547. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  548. object = object[camelCaseValue];
  549. }
  550. else if( object[camelCaseValue] !== undefined ) {
  551. found = object[camelCaseValue];
  552. return false;
  553. }
  554. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  555. object = object[value];
  556. }
  557. else if( object[value] !== undefined ) {
  558. found = object[value];
  559. return false;
  560. }
  561. else {
  562. module.error(error.method, query);
  563. return false;
  564. }
  565. });
  566. }
  567. if ( $.isFunction( found ) ) {
  568. response = found.apply(context, passedArguments);
  569. }
  570. else if(found !== undefined) {
  571. response = found;
  572. }
  573. if($.isArray(returnedValue)) {
  574. returnedValue.push(response);
  575. }
  576. else if(returnedValue !== undefined) {
  577. returnedValue = [returnedValue, response];
  578. }
  579. else if(response !== undefined) {
  580. returnedValue = response;
  581. }
  582. return found;
  583. }
  584. }
  585. ;
  586. if(methodInvoked) {
  587. if(instance === undefined) {
  588. module.initialize();
  589. }
  590. module.invoke(query);
  591. }
  592. else {
  593. if(instance !== undefined) {
  594. module.destroy();
  595. }
  596. module.initialize();
  597. }
  598. });
  599. return (returnedValue !== undefined)
  600. ? returnedValue
  601. : this
  602. ;
  603. };
  604. $.fn.sidebar.settings = {
  605. name : 'Sidebar',
  606. namespace : 'sidebar',
  607. debug : false,
  608. verbose : false,
  609. performance : false,
  610. transition : 'overlay',
  611. mobileTransition : 'slide along',
  612. context : 'body',
  613. useCSS : true,
  614. duration : 300,
  615. dimPage : true,
  616. exclusive : true,
  617. onChange : function(){},
  618. onShow : function(){},
  619. onHide : function(){},
  620. onHidden : function(){},
  621. onVisible : function(){},
  622. workaround : 'scroll',
  623. className : {
  624. pushable : 'pushable',
  625. active : 'active',
  626. visible : 'visible',
  627. pushed : 'pushed',
  628. inward : 'show',
  629. outward : 'hide'
  630. },
  631. selector: {
  632. sidebar : '.ui.sidebar',
  633. pusher : '.pusher',
  634. fixed : '.ui.fixed',
  635. page : '.page',
  636. omitted : 'script, link, style, .ui.modal, .ui.nag'
  637. },
  638. error : {
  639. method : 'The method you called is not defined.',
  640. notFound : 'There were no elements that matched the specified selector'
  641. }
  642. };
  643. })( jQuery, window , document );