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.

701 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
11 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. },
  85. bind: {
  86. clickaway: function() {
  87. $(window).on('DOMMouseScroll' + eventNamespace, function(event){
  88. event.preventDefault();
  89. });
  90. $context
  91. .on('click' + eventNamespace, module.event.clickaway)
  92. .on('touchend' + eventNamespace, module.event.clickaway)
  93. ;
  94. }
  95. },
  96. unbind: {
  97. clickaway: function() {
  98. $context
  99. .off(eventNamespace)
  100. ;
  101. $(window).off('DOMMouseScroll' + eventNamespace);
  102. }
  103. },
  104. refresh: function() {
  105. module.verbose('Refreshing selector cache');
  106. $context = $(settings.context);
  107. $style = $('style[title=' + namespace + ']');
  108. $sidebars = $context.children(selector.sidebar);
  109. $pusher = $context.children(selector.pusher);
  110. $page = $pusher.children(selector.page);
  111. $fixed = $pusher.find(selector.fixed);
  112. },
  113. repaint: function() {
  114. module.verbose('Forcing repaint event');
  115. var fakeAssignment = $context[0].offsetWidth;
  116. },
  117. setup: {
  118. layout: function() {
  119. if( $context.find(selector.pusher).size() === 0 ) {
  120. module.debug('Adding wrapper element for sidebar');
  121. $pusher = $('<div class="pusher" />');
  122. $page = $('<div class="page" />');
  123. $pusher.append($page);
  124. $context
  125. .children()
  126. .not(selector.omitted)
  127. .not($sidebars)
  128. .wrapAll($pusher)
  129. ;
  130. }
  131. if($module.prevAll($page)[0] !== $page[0]) {
  132. module.debug('Moved sidebar to correct parent element');
  133. $module.detach().prependTo($context);
  134. }
  135. $fixed
  136. .css({
  137. transform: 'translate3d(0, 0px, 0px)'
  138. })
  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. $.proxy(callback, element)();
  181. $.proxy(settings.onShow, element)();
  182. });
  183. $.proxy(settings.onChange, element)();
  184. $.proxy(settings.onVisible, element)();
  185. }
  186. else {
  187. module.debug('Sidebar is already visible');
  188. }
  189. },
  190. hide: function(callback) {
  191. callback = $.isFunction(callback)
  192. ? callback
  193. : function(){}
  194. ;
  195. module.debug('Hiding sidebar', callback);
  196. if(module.is.visible()) {
  197. module.pullPage(function() {
  198. $.proxy(callback, element)();
  199. $.proxy(settings.onHidden, element)();
  200. });
  201. $.proxy(settings.onChange, element)();
  202. $.proxy(settings.onHide, element)();
  203. }
  204. },
  205. hideAll: function() {
  206. var
  207. $visibleSidebars = $sidebars.find('.' + className.visible)
  208. ;
  209. $visibleSidebars
  210. .sidebar('hide')
  211. ;
  212. },
  213. toggle: function() {
  214. module.verbose('Determining toggled direction');
  215. if(module.is.closed()) {
  216. module.show();
  217. }
  218. else {
  219. module.hide();
  220. }
  221. },
  222. pushPage: function(callback) {
  223. var
  224. $transition = (settings.transition == 'safe')
  225. ? $context
  226. : (settings.transition == 'overlay')
  227. ? $module
  228. : $pusher,
  229. transition
  230. ;
  231. callback = $.isFunction(callback)
  232. ? callback
  233. : function(){}
  234. ;
  235. transition = function() {
  236. module.set.visible();
  237. module.set.transition();
  238. module.set.direction();
  239. requestAnimationFrame(function() {
  240. module.set.active();
  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 );