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.

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