PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/public/src/app.js

https://gitlab.com/Mashamba/NodeBB
JavaScript | 516 lines | 440 code | 73 blank | 3 comment | 57 complexity | 18aa5a79069d449065768174b45e8199 MD5 | raw file
  1. "use strict";
  2. /*global io, templates, ajaxify, utils, bootbox, overrides, socket, config, Visibility*/
  3. var app = app || {};
  4. app.isFocused = true;
  5. app.currentRoom = null;
  6. app.widgets = {};
  7. app.cacheBuster = null;
  8. (function () {
  9. var showWelcomeMessage = !!utils.params().loggedin;
  10. templates.setGlobal('config', config);
  11. app.cacheBuster = config['cache-buster'];
  12. require(['csrf'], function(csrf) {
  13. csrf.set(config.csrf_token);
  14. });
  15. bootbox.setDefaults({
  16. locale: config.userLang
  17. });
  18. app.load = function() {
  19. $('document').ready(function () {
  20. var url = ajaxify.start(window.location.pathname.slice(1) + window.location.search, true);
  21. ajaxify.end(url, app.template);
  22. handleStatusChange();
  23. if (config.searchEnabled) {
  24. app.handleSearch();
  25. }
  26. handleNewTopic();
  27. require(['components'], function(components) {
  28. components.get('user/logout').on('click', app.logout);
  29. });
  30. Visibility.change(function(e, state){
  31. if (state === 'visible') {
  32. app.isFocused = true;
  33. app.alternatingTitle('');
  34. } else if (state === 'hidden') {
  35. app.isFocused = false;
  36. }
  37. });
  38. overrides.overrideBootbox();
  39. overrides.overrideTimeago();
  40. createHeaderTooltips();
  41. app.showEmailConfirmWarning();
  42. socket.removeAllListeners('event:nodebb.ready');
  43. socket.on('event:nodebb.ready', function(data) {
  44. if (!app.cacheBusters || app.cacheBusters['cache-buster'] !== data['cache-buster']) {
  45. app.cacheBusters = data;
  46. app.alert({
  47. alert_id: 'forum_updated',
  48. title: '[[global:updated.title]]',
  49. message: '[[global:updated.message]]',
  50. clickfn: function() {
  51. window.location.reload();
  52. },
  53. type: 'warning'
  54. });
  55. }
  56. });
  57. require(['taskbar', 'helpers', 'forum/pagination'], function(taskbar, helpers, pagination) {
  58. taskbar.init();
  59. // templates.js helpers
  60. helpers.register();
  61. pagination.init();
  62. $(window).trigger('action:app.load');
  63. });
  64. });
  65. };
  66. app.logout = function() {
  67. require(['csrf'], function(csrf) {
  68. $.ajax(config.relative_path + '/logout', {
  69. type: 'POST',
  70. headers: {
  71. 'x-csrf-token': csrf.get()
  72. },
  73. success: function() {
  74. window.location.href = config.relative_path + '/';
  75. }
  76. });
  77. });
  78. };
  79. app.alert = function (params) {
  80. require(['alerts'], function(alerts) {
  81. alerts.alert(params);
  82. });
  83. };
  84. app.removeAlert = function(id) {
  85. require(['alerts'], function(alerts) {
  86. alerts.remove(id);
  87. });
  88. };
  89. app.alertSuccess = function (message, timeout) {
  90. app.alert({
  91. title: '[[global:alert.success]]',
  92. message: message,
  93. type: 'success',
  94. timeout: timeout ? timeout : 2000
  95. });
  96. };
  97. app.alertError = function (message, timeout) {
  98. app.alert({
  99. title: '[[global:alert.error]]',
  100. message: message,
  101. type: 'danger',
  102. timeout: timeout ? timeout : 5000
  103. });
  104. };
  105. app.enterRoom = function (room, callback) {
  106. callback = callback || function() {};
  107. if (socket) {
  108. if (app.currentRoom === room) {
  109. return;
  110. }
  111. socket.emit('meta.rooms.enter', {
  112. enter: room,
  113. username: app.user.username,
  114. userslug: app.user.userslug,
  115. picture: app.user.picture,
  116. status: app.user.status,
  117. 'icon:bgColor': app.user['icon:bgColor'],
  118. 'icon:text': app.user['icon:text']
  119. }, function(err) {
  120. if (err) {
  121. return app.alertError(err.message);
  122. }
  123. app.currentRoom = room;
  124. });
  125. }
  126. };
  127. app.leaveCurrentRoom = function() {
  128. if (!socket) {
  129. return;
  130. }
  131. socket.emit('meta.rooms.leaveCurrent', function(err) {
  132. if (err) {
  133. return app.alertError(err.message);
  134. }
  135. app.currentRoom = '';
  136. });
  137. }
  138. function highlightNavigationLink() {
  139. var path = window.location.pathname;
  140. $('#main-nav li').removeClass('active');
  141. if (path) {
  142. $('#main-nav li').removeClass('active').find('a[href="' + path + '"]').parent().addClass('active');
  143. }
  144. }
  145. app.createUserTooltips = function(els) {
  146. els = els || $('body');
  147. els.find('img[title].teaser-pic,img[title].user-img,div.user-icon,span.user-icon').each(function() {
  148. $(this).tooltip({
  149. placement: 'top',
  150. title: $(this).attr('title')
  151. });
  152. });
  153. };
  154. app.createStatusTooltips = function() {
  155. $('body').tooltip({
  156. selector:'.fa-circle.status',
  157. placement: 'top'
  158. });
  159. };
  160. app.replaceSelfLinks = function(selector) {
  161. selector = selector || $('a');
  162. selector.each(function() {
  163. var href = $(this).attr('href');
  164. if (href && app.user.userslug && href.indexOf('user/_self_') !== -1) {
  165. $(this).attr('href', href.replace(/user\/_self_/g, 'user/' + app.user.userslug));
  166. }
  167. });
  168. };
  169. app.processPage = function () {
  170. highlightNavigationLink();
  171. $('.timeago').timeago();
  172. utils.makeNumbersHumanReadable($('.human-readable-number'));
  173. utils.addCommasToNumbers($('.formatted-number'));
  174. app.createUserTooltips();
  175. app.createStatusTooltips();
  176. app.replaceSelfLinks();
  177. // Scroll back to top of page
  178. window.scrollTo(0, 0);
  179. };
  180. app.showLoginMessage = function () {
  181. function showAlert() {
  182. app.alert({
  183. type: 'success',
  184. title: '[[global:welcome_back]] ' + app.user.username + '!',
  185. message: '[[global:you_have_successfully_logged_in]]',
  186. timeout: 5000
  187. });
  188. }
  189. if (showWelcomeMessage) {
  190. showWelcomeMessage = false;
  191. if (document.readyState !== 'complete') {
  192. $(document).ready(showAlert);
  193. } else {
  194. showAlert();
  195. }
  196. }
  197. };
  198. app.openChat = function (username, touid) {
  199. if (username === app.user.username) {
  200. return app.alertError('[[error:cant-chat-with-yourself]]');
  201. }
  202. if (!app.user.uid) {
  203. return app.alertError('[[error:not-logged-in]]');
  204. }
  205. require(['chat'], function (chat) {
  206. function loadAndCenter(chatModal) {
  207. chat.load(chatModal.attr('UUID'));
  208. chat.center(chatModal);
  209. chat.focusInput(chatModal);
  210. }
  211. if (!chat.modalExists(touid)) {
  212. chat.createModal({
  213. username: username,
  214. touid: touid
  215. }, loadAndCenter);
  216. } else {
  217. loadAndCenter(chat.getModal(touid));
  218. }
  219. });
  220. };
  221. var titleObj = {
  222. active: false,
  223. interval: undefined,
  224. titles: []
  225. };
  226. app.alternatingTitle = function (title) {
  227. if (typeof title !== 'string') {
  228. return;
  229. }
  230. if (title.length > 0 && !app.isFocused) {
  231. if (!titleObj.titles[0]) {
  232. titleObj.titles[0] = window.document.title;
  233. }
  234. require(['translator'], function(translator) {
  235. translator.translate(title, function(translated) {
  236. titleObj.titles[1] = translated;
  237. if (titleObj.interval) {
  238. clearInterval(titleObj.interval);
  239. }
  240. titleObj.interval = setInterval(function() {
  241. var title = titleObj.titles[titleObj.titles.indexOf(window.document.title) ^ 1];
  242. if (title) {
  243. window.document.title = $('<div/>').html(title).text();
  244. }
  245. }, 2000);
  246. });
  247. });
  248. } else {
  249. if (titleObj.interval) {
  250. clearInterval(titleObj.interval);
  251. }
  252. if (titleObj.titles[0]) {
  253. window.document.title = $('<div/>').html(titleObj.titles[0]).text();
  254. }
  255. }
  256. };
  257. app.refreshTitle = function(title) {
  258. if (!title) {
  259. return;
  260. }
  261. require(['translator'], function(translator) {
  262. title = config.titleLayout.replace(/&#123;/g, '{').replace(/&#125;/g, '}').replace('{pageTitle}', title).replace('{browserTitle}', config.browserTitle);
  263. translator.translate(title, function(translated) {
  264. titleObj.titles[0] = translated;
  265. app.alternatingTitle('');
  266. });
  267. });
  268. };
  269. app.toggleNavbar = function(state) {
  270. var navbarEl = $('.navbar');
  271. if (navbarEl) {
  272. navbarEl.toggleClass('hidden', !!!state);
  273. }
  274. };
  275. function createHeaderTooltips() {
  276. var env = utils.findBootstrapEnvironment();
  277. if (env === 'xs' || env === 'sm') {
  278. return;
  279. }
  280. $('#header-menu li a[title]').each(function() {
  281. $(this).tooltip({
  282. placement: 'bottom',
  283. title: $(this).attr('title')
  284. });
  285. });
  286. $('#search-form').parent().tooltip({
  287. placement: 'bottom',
  288. title: $('#search-button i').attr('title')
  289. });
  290. $('#user_dropdown').tooltip({
  291. placement: 'bottom',
  292. title: $('#user_dropdown').attr('title')
  293. });
  294. }
  295. app.handleSearch = function () {
  296. var searchButton = $("#search-button"),
  297. searchFields = $("#search-fields"),
  298. searchInput = $('#search-fields input');
  299. $('#search-form .advanced-search-link').on('mousedown', function() {
  300. ajaxify.go('/search');
  301. });
  302. $('#search-form').on('submit', dismissSearch);
  303. searchInput.on('blur', dismissSearch);
  304. function dismissSearch(){
  305. searchFields.addClass('hidden');
  306. searchButton.removeClass('hidden');
  307. }
  308. searchButton.on('click', function(e) {
  309. if (!config.loggedIn && !config.allowGuestSearching) {
  310. app.alert({
  311. message:'[[error:search-requires-login]]',
  312. timeout: 3000
  313. });
  314. ajaxify.go('login');
  315. return false;
  316. }
  317. e.stopPropagation();
  318. app.prepareSearch();
  319. return false;
  320. });
  321. $('#search-form').on('submit', function () {
  322. var input = $(this).find('input');
  323. require(['search'], function(search) {
  324. search.query({term: input.val()}, function() {
  325. input.val('');
  326. });
  327. });
  328. return false;
  329. });
  330. };
  331. app.prepareSearch = function() {
  332. $("#search-fields").removeClass('hidden');
  333. $("#search-button").addClass('hidden');
  334. $('#search-fields input').focus();
  335. };
  336. function handleStatusChange() {
  337. $('[component="header/usercontrol"] [data-status]').off('click').on('click', function(e) {
  338. var status = $(this).attr('data-status');
  339. socket.emit('user.setStatus', status, function(err, data) {
  340. if(err) {
  341. return app.alertError(err.message);
  342. }
  343. $('[data-uid="' + app.user.uid + '"] [component="user/status"], [component="header/profilelink"] [component="user/status"]')
  344. .removeClass('away online dnd offline')
  345. .addClass(status);
  346. app.user.status = status;
  347. });
  348. e.preventDefault();
  349. });
  350. }
  351. app.updateUserStatus = function(el, status) {
  352. if (!el.length) {
  353. return;
  354. }
  355. require(['translator'], function(translator) {
  356. translator.translate('[[global:' + status + ']]', function(translated) {
  357. el.removeClass('online offline dnd away')
  358. .addClass(status)
  359. .attr('title', translated)
  360. .attr('data-original-title', translated);
  361. });
  362. });
  363. };
  364. function handleNewTopic() {
  365. $('#content').on('click', '#new_topic', function() {
  366. var cid = ajaxify.data.cid;
  367. if (cid) {
  368. $(window).trigger('action:composer.topic.new', {
  369. cid: cid
  370. });
  371. } else {
  372. socket.emit('categories.getCategoriesByPrivilege', 'topics:create', function(err, categories) {
  373. if (err) {
  374. return app.alertError(err.message);
  375. }
  376. categories = categories.filter(function(category) {
  377. return !category.link && !parseInt(category.parentCid, 10);
  378. });
  379. if (categories.length) {
  380. $(window).trigger('action:composer.topic.new', {
  381. cid: categories[0].cid
  382. });
  383. }
  384. });
  385. }
  386. });
  387. }
  388. app.loadJQueryUI = function(callback) {
  389. if (typeof $().autocomplete === 'function') {
  390. return callback();
  391. }
  392. $.getScript(config.relative_path + '/vendor/jquery/js/jquery-ui-1.10.4.custom.js', callback);
  393. };
  394. app.showEmailConfirmWarning = function(err) {
  395. if (!config.requireEmailConfirmation || !app.user.uid) {
  396. return;
  397. }
  398. if (!app.user.email) {
  399. app.alert({
  400. alert_id: 'email_confirm',
  401. message: '[[error:no-email-to-confirm]]',
  402. type: 'warning',
  403. timeout: 0,
  404. clickfn: function() {
  405. app.removeAlert('email_confirm');
  406. ajaxify.go('user/' + app.user.userslug + '/edit');
  407. }
  408. });
  409. } else if (!app.user['email:confirmed']) {
  410. app.alert({
  411. alert_id: 'email_confirm',
  412. message: err ? err.message : '[[error:email-not-confirmed]]',
  413. type: 'warning',
  414. timeout: 0,
  415. clickfn: function() {
  416. app.removeAlert('email_confirm');
  417. socket.emit('user.emailConfirm', {}, function(err) {
  418. if (err) {
  419. return app.alertError(err.message);
  420. }
  421. app.alertSuccess('[[notifications:email-confirm-sent]]');
  422. });
  423. }
  424. });
  425. }
  426. };
  427. app.parseAndTranslate = function(template, blockName, data, callback) {
  428. if (typeof blockName === 'string') {
  429. templates.parse(template, blockName, data, function(html) {
  430. translator.translate(html, function(translatedHTML) {
  431. callback($(translatedHTML));
  432. });
  433. });
  434. } else {
  435. callback = data, data = blockName;
  436. templates.parse(template, data, function(html) {
  437. translator.translate(html, function(translatedHTML) {
  438. callback($(translatedHTML));
  439. });
  440. });
  441. }
  442. };
  443. }());