crowi.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. /* eslint-disable react/jsx-filename-extension */
  2. require('jquery.cookie');
  3. require('./thirdparty-js/waves');
  4. const Crowi = {};
  5. if (!window) {
  6. window = {};
  7. }
  8. window.Crowi = Crowi;
  9. /**
  10. * set 'data-caret-line' attribute that will be processed when 'shown.bs.tab' event fired
  11. * @param {number} line
  12. */
  13. Crowi.setCaretLineData = function(line) {
  14. const { appContainer } = window;
  15. const navigationContainer = appContainer.getContainer('NavigationContainer');
  16. navigationContainer.setEditorMode('edit');
  17. const pageEditorDom = document.querySelector('#page-editor');
  18. pageEditorDom.setAttribute('data-caret-line', line);
  19. };
  20. /**
  21. * invoked when;
  22. *
  23. * 1. 'shown.bs.tab' event fired
  24. */
  25. Crowi.setCaretLineAndFocusToEditor = function() {
  26. // get 'data-caret-line' attributes
  27. const pageEditorDom = document.querySelector('#page-editor');
  28. if (pageEditorDom == null) {
  29. return;
  30. }
  31. const { appContainer } = window;
  32. const editorContainer = appContainer.getContainer('EditorContainer');
  33. const line = pageEditorDom.getAttribute('data-caret-line') || 0;
  34. editorContainer.setCaretLine(+line);
  35. // reset data-caret-line attribute
  36. pageEditorDom.removeAttribute('data-caret-line');
  37. // focus
  38. editorContainer.focusToEditor();
  39. };
  40. // original: middleware.swigFilter
  41. Crowi.userPicture = function(user) {
  42. if (!user) {
  43. return '/images/icons/user.svg';
  44. }
  45. return user.image || '/images/icons/user.svg';
  46. };
  47. Crowi.modifyScrollTop = function() {
  48. const offset = 10;
  49. const hash = window.location.hash;
  50. if (hash === '') {
  51. return;
  52. }
  53. const pageHeader = document.querySelector('#page-header');
  54. if (!pageHeader) {
  55. return;
  56. }
  57. const pageHeaderRect = pageHeader.getBoundingClientRect();
  58. const sectionHeader = Crowi.findSectionHeader(hash);
  59. if (sectionHeader === null) {
  60. return;
  61. }
  62. let timeout = 0;
  63. if (window.scrollY === 0) {
  64. timeout = 200;
  65. }
  66. setTimeout(() => {
  67. const sectionHeaderRect = sectionHeader.getBoundingClientRect();
  68. if (sectionHeaderRect.top >= pageHeaderRect.bottom) {
  69. return;
  70. }
  71. window.scrollTo(0, (window.scrollY - pageHeaderRect.height - offset));
  72. }, timeout);
  73. };
  74. Crowi.initClassesByOS = function() {
  75. // add classes to cmd-key by OS
  76. const platform = navigator.platform.toLowerCase();
  77. const isMac = (platform.indexOf('mac') > -1);
  78. document.querySelectorAll('.system-version .cmd-key').forEach((element) => {
  79. if (isMac) {
  80. element.classList.add('mac');
  81. }
  82. else {
  83. element.classList.add('win');
  84. }
  85. });
  86. document.querySelectorAll('#shortcuts-modal .cmd-key').forEach((element) => {
  87. if (isMac) {
  88. element.classList.add('mac');
  89. }
  90. else {
  91. element.classList.add('win', 'key-longer');
  92. }
  93. });
  94. };
  95. Crowi.findHashFromUrl = function(url) {
  96. let match;
  97. /* eslint-disable no-cond-assign */
  98. if (match = url.match(/#(.+)$/)) {
  99. return `#${match[1]}`;
  100. }
  101. /* eslint-enable no-cond-assign */
  102. return '';
  103. };
  104. Crowi.findSectionHeader = function(hash) {
  105. if (hash.length === 0) {
  106. return;
  107. }
  108. // omit '#'
  109. const id = hash.replace('#', '');
  110. // don't use jQuery and document.querySelector
  111. // because hash may containe Base64 encoded strings
  112. const elem = document.getElementById(id);
  113. if (elem != null && elem.tagName.match(/h\d+/i)) { // match h1, h2, h3...
  114. return elem;
  115. }
  116. return null;
  117. };
  118. Crowi.unhighlightSelectedSection = function(hash) {
  119. const elem = Crowi.findSectionHeader(hash);
  120. if (elem != null) {
  121. elem.classList.remove('highlighted');
  122. }
  123. };
  124. Crowi.highlightSelectedSection = function(hash) {
  125. const elem = Crowi.findSectionHeader(hash);
  126. if (elem != null) {
  127. elem.classList.add('highlighted');
  128. }
  129. };
  130. $(() => {
  131. const pageId = $('#content-main').data('page-id');
  132. // const revisionId = $('#content-main').data('page-revision-id');
  133. // const revisionCreatedAt = $('#content-main').data('page-revision-created');
  134. // const currentUser = $('#content-main').data('current-user');
  135. const isSeen = $('#content-main').data('page-is-seen');
  136. $('[data-toggle="popover"]').popover();
  137. $('[data-toggle="tooltip"]').tooltip();
  138. $('[data-tooltip-stay]').tooltip('show');
  139. $('#toggle-crowi-sidebar').click((e) => {
  140. const $body = $('body');
  141. if ($body.hasClass('aside-hidden')) {
  142. $body.removeClass('aside-hidden');
  143. $.cookie('aside-hidden', 0, { expires: 30, path: '/' });
  144. }
  145. else {
  146. $body.addClass('aside-hidden');
  147. $.cookie('aside-hidden', 1, { expires: 30, path: '/' });
  148. }
  149. return false;
  150. });
  151. if ($.cookie('aside-hidden') === 1) {
  152. $('body').addClass('aside-hidden');
  153. }
  154. $('.copy-link').on('click', function() {
  155. $(this).select();
  156. });
  157. if (pageId) {
  158. if (!isSeen) {
  159. $.post('/_api/pages.seen', { page_id: pageId }, (res) => {
  160. // ignore unless response has error
  161. if (res.ok && res.seenUser) {
  162. $('#content-main').data('page-is-seen', 1);
  163. }
  164. });
  165. }
  166. } // end if pageId
  167. });
  168. window.addEventListener('load', (e) => {
  169. const { appContainer } = window;
  170. // do nothing if user is guest
  171. if (appContainer.currentUser == null) {
  172. return;
  173. }
  174. // hash on page
  175. if (window.location.hash) {
  176. const navigationContainer = appContainer.getContainer('NavigationContainer');
  177. if (window.location.hash === '#edit') {
  178. navigationContainer.setEditorMode('edit');
  179. // focus
  180. Crowi.setCaretLineAndFocusToEditor();
  181. }
  182. else if (window.location.hash === '#hackmd') {
  183. navigationContainer.setEditorMode('hackmd');
  184. }
  185. }
  186. });
  187. window.addEventListener('load', (e) => {
  188. const crowi = window.crowi;
  189. if (crowi && crowi.users && crowi.users.length !== 0) {
  190. const totalUsers = crowi.users.length;
  191. const $listLiker = $('.page-list-liker');
  192. $listLiker.each((i, liker) => {
  193. const count = $(liker).data('count') || 0;
  194. if (count / totalUsers > 0.05) {
  195. $(liker).addClass('popular-page-high');
  196. // 5%
  197. }
  198. else if (count / totalUsers > 0.02) {
  199. $(liker).addClass('popular-page-mid');
  200. // 2%
  201. }
  202. else if (count / totalUsers > 0.005) {
  203. $(liker).addClass('popular-page-low');
  204. // 0.5%
  205. }
  206. });
  207. const $listSeer = $('.page-list-seer');
  208. $listSeer.each((i, seer) => {
  209. const count = $(seer).data('count') || 0;
  210. if (count / totalUsers > 0.10) {
  211. // 10%
  212. $(seer).addClass('popular-page-high');
  213. }
  214. else if (count / totalUsers > 0.05) {
  215. // 5%
  216. $(seer).addClass('popular-page-mid');
  217. }
  218. else if (count / totalUsers > 0.02) {
  219. // 2%
  220. $(seer).addClass('popular-page-low');
  221. }
  222. });
  223. }
  224. Crowi.highlightSelectedSection(window.location.hash);
  225. Crowi.modifyScrollTop();
  226. Crowi.initClassesByOS();
  227. });
  228. window.addEventListener('hashchange', (e) => {
  229. Crowi.unhighlightSelectedSection(Crowi.findHashFromUrl(e.oldURL));
  230. Crowi.highlightSelectedSection(Crowi.findHashFromUrl(e.newURL));
  231. Crowi.modifyScrollTop();
  232. const { appContainer } = window;
  233. const navigationContainer = appContainer.getContainer('NavigationContainer');
  234. // hash on page
  235. if (window.location.hash) {
  236. if (window.location.hash === '#edit') {
  237. navigationContainer.setEditorMode('edit');
  238. Crowi.setCaretLineAndFocusToEditor();
  239. }
  240. else if (window.location.hash === '#hackmd') {
  241. navigationContainer.setEditorMode('hackmd');
  242. }
  243. }
  244. });
  245. // adjust min-height of page for print temporarily
  246. window.onbeforeprint = function() {
  247. $('#page-wrapper').css('min-height', '0px');
  248. };