ui.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. elgg.provide('elgg.ui');
  2. elgg.ui.init = function () {
  3. // @todo we need better documentation for this hack
  4. // iOS Hover Event Class Fix
  5. $('.elgg-page').attr("onclick", "return true");
  6. // add user hover menus
  7. elgg.ui.initHoverMenu();
  8. //if the user clicks a system message, make it disappear
  9. $('.elgg-system-messages li').live('click', function() {
  10. $(this).stop().fadeOut('fast');
  11. });
  12. $('.elgg-system-messages li').animate({opacity: 0.9}, 6000);
  13. $('.elgg-system-messages li.elgg-state-success').fadeOut('slow');
  14. $('[rel=toggle]').live('click', elgg.ui.toggles);
  15. $('[rel=popup]').live('click', elgg.ui.popupOpen);
  16. $('.elgg-menu-page .elgg-menu-parent').live('click', elgg.ui.toggleMenu);
  17. $('*[data-confirm], .elgg-requires-confirmation').live('click', elgg.ui.requiresConfirmation);
  18. if ($('.elgg-requires-confirmation').length > 0) {
  19. elgg.deprecated_notice('Use of .elgg-requires-confirmation is deprecated by data-confirm', '1.10');
  20. }
  21. $('.elgg-autofocus').focus();
  22. if ($('.elgg-autofocus').length > 0) {
  23. elgg.deprecated_notice('Use of .elgg-autofocus is deprecated by html5 autofocus', 1.9);
  24. }
  25. elgg.ui.initAccessInputs();
  26. // Allow element to be highlighted using CSS if its id is found from the URL
  27. var elementId = elgg.getSelectorFromUrlFragment(document.URL);
  28. $(elementId).addClass('elgg-state-highlight');
  29. };
  30. /**
  31. * Toggles an element based on clicking a separate element
  32. *
  33. * Use rel="toggle" on the toggler element
  34. * Set the href to target the item you want to toggle (<a rel="toggle" href="#id-of-target">)
  35. * or use data-toggle-selector="your_jquery_selector" to have an advanced selection method
  36. *
  37. * By default elements perform a slideToggle.
  38. * If you want a normal toggle (hide/show) you can add data-toggle-slide="0" on the elements to prevent a slide.
  39. *
  40. * @param {Object} event
  41. * @return void
  42. */
  43. elgg.ui.toggles = function(event) {
  44. event.preventDefault();
  45. var $this = $(this),
  46. target = $this.data().toggleSelector;
  47. if (!target) {
  48. // @todo we can switch to elgg.getSelectorFromUrlFragment() in 1.x if
  49. // we also extend it to support href=".some-class"
  50. target = $this.attr('href');
  51. }
  52. $this.toggleClass('elgg-state-active');
  53. $(target).each(function(index, elem) {
  54. var $elem = $(elem);
  55. if ($elem.data().toggleSlide != false) {
  56. $elem.slideToggle('medium');
  57. } else {
  58. $elem.toggle();
  59. }
  60. });
  61. };
  62. /**
  63. * Pops up an element based on clicking a separate element
  64. *
  65. * Set the rel="popup" on the popper and set the href to target the
  66. * item you want to toggle (<a rel="popup" href="#id-of-target">)
  67. *
  68. * This function emits the getOptions, ui.popup hook that plugins can register for to provide custom
  69. * positioning for elements. The handler is passed the following params:
  70. * targetSelector: The selector used to find the popup
  71. * target: The popup jQuery element as found by the selector
  72. * source: The jquery element whose click event initiated a popup.
  73. *
  74. * The return value of the function is used as the options object to .position().
  75. * Handles can also return false to abort the default behvior and override it with their own.
  76. *
  77. * @param {Object} event
  78. * @return void
  79. */
  80. elgg.ui.popupOpen = function(event) {
  81. event.preventDefault();
  82. event.stopPropagation();
  83. var target = elgg.getSelectorFromUrlFragment($(this).toggleClass('elgg-state-active').attr('href'));
  84. var $target = $(target);
  85. // emit a hook to allow plugins to position and control popups
  86. var params = {
  87. targetSelector: target,
  88. target: $target,
  89. source: $(this)
  90. };
  91. var options = {
  92. my: 'center top',
  93. at: 'center bottom',
  94. of: $(this),
  95. collision: 'fit fit'
  96. };
  97. options = elgg.trigger_hook('getOptions', 'ui.popup', params, options);
  98. // allow plugins to cancel event
  99. if (!options) {
  100. return;
  101. }
  102. // hide if already open
  103. if ($target.is(':visible')) {
  104. $target.fadeOut();
  105. $('body').die('click', elgg.ui.popupClose);
  106. return;
  107. }
  108. $target.appendTo('body')
  109. .fadeIn()
  110. .position(options);
  111. $('body')
  112. .die('click', elgg.ui.popupClose)
  113. .live('click', elgg.ui.popupClose);
  114. };
  115. /**
  116. * Catches clicks that aren't in a popup and closes all popups.
  117. */
  118. elgg.ui.popupClose = function(event) {
  119. $eventTarget = $(event.target);
  120. var inTarget = false;
  121. var $popups = $('[rel=popup]');
  122. // if the click event target isn't in a popup target, fade all of them out.
  123. $popups.each(function(i, e) {
  124. var target = elgg.getSelectorFromUrlFragment($(e).attr('href')) + ':visible';
  125. var $target = $(target);
  126. if (!$target.is(':visible')) {
  127. return;
  128. }
  129. // didn't click inside the target
  130. if ($eventTarget.closest(target).length > 0) {
  131. inTarget = true;
  132. return false;
  133. }
  134. });
  135. if (!inTarget) {
  136. $popups.each(function(i, e) {
  137. var $e = $(e);
  138. var $target = $(elgg.getSelectorFromUrlFragment($e.attr('href')) + ':visible');
  139. if ($target.length > 0) {
  140. $target.fadeOut();
  141. $e.removeClass('elgg-state-active');
  142. }
  143. });
  144. $('body').die('click', elgg.ui.popClose);
  145. }
  146. };
  147. /**
  148. * Toggles a child menu when the parent is clicked
  149. *
  150. * @param {Object} event
  151. * @return void
  152. */
  153. elgg.ui.toggleMenu = function(event) {
  154. $(this).siblings().slideToggle('medium');
  155. $(this).toggleClass('elgg-menu-closed elgg-menu-opened');
  156. event.preventDefault();
  157. };
  158. /**
  159. * Initialize the hover menu
  160. *
  161. * @param {Object} parent
  162. * @return void
  163. */
  164. elgg.ui.initHoverMenu = function(parent) {
  165. /**
  166. * For a menu clicked, load the menu into all matching placeholders
  167. *
  168. * @param {String} mac Machine authorization code for the menu clicked
  169. */
  170. function loadMenu(mac) {
  171. var $all_placeholders = $(".elgg-menu-hover[rel='" + mac + "']");
  172. // find the <ul> that contains data for this menu
  173. var $ul = $all_placeholders.filter('[data-elgg-menu-data]');
  174. if (!$ul.length) {
  175. return;
  176. }
  177. elgg.get('ajax/view/navigation/menu/user_hover/contents', {
  178. data: $ul.data('elggMenuData'),
  179. success: function(data) {
  180. if (data) {
  181. // replace all existing placeholders with new menu
  182. $all_placeholders.removeClass('elgg-ajax-loader')
  183. .html($(data).children());
  184. }
  185. }
  186. });
  187. }
  188. if (!parent) {
  189. parent = document;
  190. }
  191. // avatar image menu link
  192. $(parent).find(".elgg-avatar").live('mouseover', function() {
  193. $(this).children(".elgg-icon-hover-menu").show();
  194. })
  195. .live('mouseout', function() {
  196. $(this).children(".elgg-icon-hover-menu").hide();
  197. });
  198. // avatar contextual menu
  199. $(".elgg-avatar > .elgg-icon-hover-menu").live('click', function(e) {
  200. var $placeholder = $(this).parent().find(".elgg-menu-hover.elgg-ajax-loader");
  201. if ($placeholder.length) {
  202. loadMenu($placeholder.attr("rel"));
  203. }
  204. // check if we've attached the menu to this element already
  205. var $hovermenu = $(this).data('hovermenu') || null;
  206. if (!$hovermenu) {
  207. $hovermenu = $(this).parent().find(".elgg-menu-hover");
  208. $(this).data('hovermenu', $hovermenu);
  209. }
  210. // close hovermenu if arrow is clicked & menu already open
  211. if ($hovermenu.css('display') == "block") {
  212. $hovermenu.fadeOut();
  213. } else {
  214. $avatar = $(this).closest(".elgg-avatar");
  215. // @todo Use jQuery-ui position library instead -- much simpler
  216. var offset = $avatar.offset();
  217. var top = $avatar.height() + offset.top + 'px';
  218. var left = $avatar.width() - 15 + offset.left + 'px';
  219. $hovermenu.appendTo('body')
  220. .css('position', 'absolute')
  221. .css("top", top)
  222. .css("left", left)
  223. .fadeIn('normal');
  224. }
  225. // hide any other open hover menus
  226. $(".elgg-menu-hover:visible").not($hovermenu).fadeOut();
  227. });
  228. // hide avatar menu when user clicks elsewhere
  229. $(document).click(function(event) {
  230. if ($(event.target).parents(".elgg-avatar").length === 0) {
  231. $(".elgg-menu-hover").fadeOut();
  232. }
  233. });
  234. };
  235. /**
  236. * Calls a confirm() and prevents default if denied.
  237. *
  238. * @param {Object} e
  239. * @return void
  240. */
  241. elgg.ui.requiresConfirmation = function(e) {
  242. var confirmText = $(this).data('confirm') || elgg.echo('question:areyousure');
  243. if (!confirm(confirmText)) {
  244. e.preventDefault();
  245. }
  246. };
  247. /**
  248. * Repositions the login popup
  249. *
  250. * @param {String} hook 'getOptions'
  251. * @param {String} type 'ui.popup'
  252. * @param {Object} params An array of info about the target and source.
  253. * @param {Object} options Options to pass to
  254. *
  255. * @return {Object}
  256. */
  257. elgg.ui.loginHandler = function(hook, type, params, options) {
  258. if (params.target.attr('id') == 'login-dropdown-box') {
  259. options.my = 'right top';
  260. options.at = 'right bottom';
  261. return options;
  262. }
  263. return null;
  264. };
  265. /**
  266. * Initialize the date picker
  267. *
  268. * Uses the class .elgg-input-date as the selector.
  269. *
  270. * If the class .elgg-input-timestamp is set on the input element, the onSelect
  271. * method converts the date text to a unix timestamp in seconds. That value is
  272. * stored in a hidden element indicated by the id on the input field.
  273. *
  274. * @return void
  275. * @requires jqueryui.datepicker
  276. */
  277. elgg.ui.initDatePicker = function() {
  278. function loadDatePicker() {
  279. $('.elgg-input-date').datepicker({
  280. // ISO-8601
  281. dateFormat: 'yy-mm-dd',
  282. onSelect: function(dateText) {
  283. if ($(this).is('.elgg-input-timestamp')) {
  284. // convert to unix timestamp
  285. var dateParts = dateText.split("-");
  286. var timestamp = Date.UTC(dateParts[0], dateParts[1] - 1, dateParts[2]);
  287. timestamp = timestamp / 1000;
  288. var id = $(this).attr('id');
  289. $('input[name="' + id + '"]').val(timestamp);
  290. }
  291. },
  292. nextText: '&#xBB;',
  293. prevText: '&#xAB;',
  294. changeMonth: true,
  295. changeYear: true
  296. });
  297. }
  298. if (!$('.elgg-input-date').length) {
  299. return;
  300. }
  301. if (elgg.get_language() == 'en') {
  302. loadDatePicker();
  303. } else {
  304. // load language first
  305. elgg.get({
  306. url: elgg.config.wwwroot + 'vendors/jquery/i18n/jquery.ui.datepicker-'+ elgg.get_language() +'.js',
  307. dataType: "script",
  308. cache: true,
  309. success: loadDatePicker,
  310. error: loadDatePicker // english language is already loaded.
  311. });
  312. }
  313. };
  314. /**
  315. * This function registers two menu items that are actions that are the opposite
  316. * of each other and ajaxifies them. E.g. like/unlike, friend/unfriend, ban/unban, etc.
  317. *
  318. * Note the menu item names must be given in their normalized form. So if the
  319. * name is remove_friend, you should call this function with "remove-friend" instead.
  320. */
  321. elgg.ui.registerTogglableMenuItems = function(menuItemNameA, menuItemNameB) {
  322. // Handles clicking the first button.
  323. $('.elgg-menu-item-' + menuItemNameA + ' a').live('click', function() {
  324. var $menu = $(this).closest('.elgg-menu');
  325. // Be optimistic about success
  326. elgg.ui.toggleMenuItems($menu, menuItemNameB, menuItemNameA);
  327. // Send the ajax request
  328. elgg.action($(this).attr('href'), {
  329. success: function(json) {
  330. if (json.system_messages.error.length) {
  331. // Something went wrong, so undo the optimistic changes
  332. elgg.ui.toggleMenuItems($menu, menuItemNameA, menuItemNameB);
  333. }
  334. },
  335. error: function() {
  336. // Something went wrong, so undo the optimistic changes
  337. elgg.ui.toggleMenuItems($menu, menuItemNameA, menuItemNameB);
  338. }
  339. });
  340. // Don't want to actually click the link
  341. return false;
  342. });
  343. // Handles clicking the second button
  344. $('.elgg-menu-item-' + menuItemNameB + ' a').live('click', function() {
  345. var $menu = $(this).closest('.elgg-menu');
  346. // Be optimistic about success
  347. elgg.ui.toggleMenuItems($menu, menuItemNameA, menuItemNameB);
  348. // Send the ajax request
  349. elgg.action($(this).attr('href'), {
  350. success: function(json) {
  351. if (json.system_messages.error.length) {
  352. // Something went wrong, so undo the optimistic changes
  353. elgg.ui.toggleMenuItems($menu, menuItemNameB, menuItemNameA);
  354. }
  355. },
  356. error: function() {
  357. // Something went wrong, so undo the optimistic changes
  358. elgg.ui.toggleMenuItems($menu, menuItemNameB, menuItemNameA);
  359. }
  360. });
  361. // Don't want to actually click the link
  362. return false;
  363. });
  364. };
  365. elgg.ui.toggleMenuItems = function($menu, nameOfItemToShow, nameOfItemToHide) {
  366. $menu.find('.elgg-menu-item-' + nameOfItemToShow).removeClass('hidden').find('a').focus();
  367. $menu.find('.elgg-menu-item-' + nameOfItemToHide).addClass('hidden');
  368. };
  369. /**
  370. * Initialize input/access for dynamic display of members only warning
  371. *
  372. * If a select.elgg-input-access is accompanied by a note (.elgg-input-access-membersonly),
  373. * then hide the note when the select value is PRIVATE or group members.
  374. *
  375. * @return void
  376. * @since 1.9.0
  377. */
  378. elgg.ui.initAccessInputs = function () {
  379. $('.elgg-input-access').each(function () {
  380. function updateMembersonlyNote() {
  381. var val = $select.val();
  382. if (val != acl && val !== 0) {
  383. // .show() failed in Chrome. Maybe a float/jQuery bug
  384. $note.css('visibility', 'visible');
  385. } else {
  386. $note.css('visibility', 'hidden');
  387. }
  388. }
  389. var $select = $(this),
  390. acl = $select.data('group-acl'),
  391. $note = $('.elgg-input-access-membersonly', this.parentNode),
  392. commentCount = $select.data('comment-count'),
  393. originalValue = $select.data('original-value');
  394. if ($note) {
  395. updateMembersonlyNote();
  396. $select.change(updateMembersonlyNote);
  397. }
  398. if (commentCount) {
  399. $select.change(function(e) {
  400. if ($(this).val() != originalValue) {
  401. if (!confirm(elgg.echo('access:comments:change', [commentCount]))) {
  402. $(this).val(originalValue);
  403. }
  404. }
  405. });
  406. }
  407. });
  408. };
  409. elgg.register_hook_handler('init', 'system', elgg.ui.init);
  410. elgg.register_hook_handler('init', 'system', elgg.ui.initDatePicker);
  411. elgg.register_hook_handler('getOptions', 'ui.popup', elgg.ui.loginHandler);
  412. elgg.ui.registerTogglableMenuItems('add-friend', 'remove-friend');