crowi.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /* jshint browser: true, jquery: true */
  2. /* global FB, marked */
  3. /* Author: Sotaro KARASAWA <sotarok@crocos.co.jp>
  4. */
  5. var Crowi = {};
  6. Crowi.createErrorView = function(msg) {
  7. $('#main').prepend($('<p class="alert-message error">' + msg + '</p>'));
  8. };
  9. Crowi.linkPath = function(revisionPath) {
  10. var $revisionPath = revisionPath || '#revision-path';
  11. var $title = $($revisionPath);
  12. if (!$title.get(0)) {
  13. return;
  14. }
  15. var path = '';
  16. var pathHtml = '';
  17. var splittedPath = $title.html().split(/\//);
  18. splittedPath.shift();
  19. splittedPath.forEach(function(sub) {
  20. path += '/';
  21. pathHtml += ' <a href="' + path + '">/</a> ';
  22. if (sub) {
  23. path += sub;
  24. pathHtml += '<a href="' + path + '">' + sub + '</a>';
  25. }
  26. });
  27. if (path.substr(-1, 1) != '/') {
  28. path += '/';
  29. pathHtml += ' <a href="' + path + '" class="last-path">/</a>';
  30. }
  31. $title.html(pathHtml);
  32. };
  33. Crowi.correctHeaders = function(contentId) {
  34. // h1 ~ h6 の id 名を補正する
  35. var $content = $(contentId || '#revision-body-content');
  36. var i = 0;
  37. $('h1,h2,h3,h4,h5,h6', $content).each(function(idx, elm) {
  38. var id = 'head' + i++ + '-' + $(this).text().replace(/\/|\(|\)|\s|\?|\!|\.|\+|\*|\-|\=|\#|\~|\&|\^/g, '');
  39. $(this).attr('id', id);
  40. $(this).addClass('revision-head');
  41. $(this).append('<span class="revision-head-link"><a href="#' + id +'"><i class="fa fa-link"></i></a></span>');
  42. });
  43. };
  44. Crowi.revisionToc = function(contentId, tocId) {
  45. var $content = $(contentId || '#revision-body-content');
  46. var $tocId = $(tocId || '#revision-toc');
  47. var $tocContent = $('<div id="revision-toc-content" class="revision-toc-content collapse"></div>');
  48. $tocId.append($tocContent);
  49. $('h1', $content).each(function(idx, elm) {
  50. var id = $(this).attr('id');
  51. var title = $(this).text();
  52. var selector = '#' + id + ' ~ h2:not(#' + id + ' ~ h1 ~ h2)';
  53. var $toc = $('<ul></ul>');
  54. var $tocLi = $('<li><a href="#' + id +'">' + title + '</a></li>');
  55. $tocContent.append($toc);
  56. $toc.append($tocLi);
  57. $(selector).each(function()
  58. {
  59. var id2 = $(this).attr('id');
  60. var title2 = $(this).text();
  61. var selector2 = '#' + id2 + ' ~ h3:not(#' + id2 + ' ~ h2 ~ h3)';
  62. var $toc2 = $('<ul></ul>');
  63. var $tocLi2 = $('<li><a href="#' + id2 +'">' + title2 + '</a></li>');
  64. $tocLi.append($toc2);
  65. $toc2.append($tocLi2);
  66. $(selector2).each(function()
  67. {
  68. var id3 = $(this).attr('id');
  69. var title3 = $(this).text();
  70. var $toc3 = $('<ul></ul>');
  71. var $tocLi3 = $('<li><a href="#' + id3 +'">' + title3 + '</a></li>');
  72. $tocLi2.append($toc3);
  73. $toc3.append($tocLi3);
  74. });
  75. });
  76. });
  77. };
  78. Crowi.escape = function(s) {
  79. s = s.replace(/&/g, '&amp;');
  80. s = s.replace(/</g, '&lt;');
  81. s = s.replace(/>/g, '&gt;');
  82. s = s.replace(/"/g, '&quot;');
  83. return s;
  84. };
  85. Crowi.unescape = function(s) {
  86. s = s.replace(/&nbsp;/g, ' ');
  87. s = s.replace(/&amp;/g, '&');
  88. s = s.replace(/&lt;(?!\?)/g, '<');
  89. s = s.replace(/([^\?])&gt;/g, '$1>');
  90. s = s.replace(/&quot;/g, '"');
  91. return s;
  92. };
  93. Crowi.getRendererType = function(format) {
  94. if (!Crowi.rendererType[format]) {
  95. throw new Error('no such renderer');
  96. }
  97. return new Crowi.rendererType[format]();
  98. };
  99. Crowi.rendererType = {};
  100. Crowi.rendererType.text = function(){};
  101. Crowi.rendererType.markdown = function(){};
  102. Crowi.rendererType.text.prototype = {
  103. render: function($content) {
  104. var $revisionHtml = this.$revisionBody.children('pre');
  105. this.$content = $content;
  106. $revisionHtml.html(this.$content.html());
  107. this.expandImage();
  108. this.link();
  109. },
  110. link: function () {
  111. this.$revisionBody.html(this.$revisionBody.html().replace(/\s(https?:\/\/[\S]+)/g, ' <a href="$1">$1</a>'));
  112. },
  113. expandImage: function () {
  114. this.$revisionBody.html(this.$revisionBody.html().replace(/\s(https?:\/\/[\S]+\.(jpg|jpeg|gif|png))/g, ' <img src="$1" class="auto-expanded-image" />'));
  115. }
  116. };
  117. Crowi.rendererType.markdown.prototype = {
  118. render: function($content) {
  119. marked.setOptions({
  120. gfm: true,
  121. highlight: function (code, lang, callback) {
  122. callback(null, code);
  123. // あとで
  124. //highlight: function (code, lang, callback) {
  125. // pygmentize({ lang: lang, format: 'html' }, code, function (err, result) {
  126. // if (err) return callback(err);
  127. // callback(null, result.toString());
  128. // });
  129. //},
  130. },
  131. tables: true,
  132. breaks: true,
  133. pedantic: false,
  134. sanitize: false,
  135. smartLists: true,
  136. smartypants: false,
  137. langPrefix: 'lang-'
  138. });
  139. var contentHtml = Crowi.unescape(Crowi.escape($content.val()) || $content.html());
  140. contentHtml = this.expandImage(contentHtml);
  141. contentHtml = this.link(contentHtml);
  142. var $body = this.$revisionBody;
  143. // Using async version of marked
  144. marked(contentHtml, {}, function (err, content) {
  145. if (err) {
  146. throw err;
  147. }
  148. $body.html(content);
  149. //console.log(content);
  150. });
  151. },
  152. link: function (content) {
  153. return content
  154. //.replace(/\s(https?:\/\/[\S]+)/g, ' <a href="$1">$1</a>') // リンク
  155. .replace(/\s<((\/[^>]+?){2,})>/g, ' <a href="$1">$1</a>') // ページ間リンク: <> でかこまれてて / から始まり、 / が2個以上
  156. ;
  157. },
  158. expandImage: function (content) {
  159. return content.replace(/\s(https?:\/\/[\S]+\.(jpg|jpeg|gif|png))/g, ' <a href="$1"><img src="$1" class="auto-expanded-image"></a>');
  160. }
  161. };
  162. Crowi.renderer = function (contentId, format, revisionBody) {
  163. var $revisionBody = revisionBody || '#revision-body-content';
  164. this.$content = $(contentId);
  165. this.$revisionBody = $($revisionBody);
  166. this.format = format;
  167. this.renderer = Crowi.getRendererType(format);
  168. this.renderer.$revisionBody = this.$revisionBody;
  169. };
  170. Crowi.renderer.prototype = {
  171. render: function() {
  172. this.renderer.render(this.$content);
  173. }
  174. };
  175. $(function() {
  176. Crowi.linkPath();
  177. $('[data-toggle="tooltip"]').tooltip();
  178. $('[data-tooltip-stay]').tooltip('show');
  179. $('.copy-link').on('click', function () {
  180. $(this).select();
  181. });
  182. $('#createMemo').on('shown.bs.modal', function (e) {
  183. $('#memoName').focus();
  184. });
  185. $('#createMemoForm').submit(function(e)
  186. {
  187. var prefix = $('[name=memoNamePrefix]', this).val();
  188. var name = $('[name=memoName]', this).val();
  189. if (name === '') {
  190. prefix = prefix.slice(0, -1);
  191. }
  192. top.location.href = prefix + name;
  193. return false;
  194. });
  195. $('#renamePage').on('shown.bs.modal', function (e) {
  196. $('#newPageName').focus();
  197. });
  198. $('#renamePageForm').submit(function(e) {
  199. var path = $('#pagePath').html();
  200. $.ajax({
  201. type: 'POST',
  202. url: '/_api/page_rename' + path,
  203. data: $('#renamePageForm').serialize(),
  204. dataType: 'json'
  205. }).done(function(data) {
  206. if (!data.status) {
  207. $('#newPageNameCheck').html('<i class="fa fa-times-circle"></i> ' + data.message);
  208. $('#newPageNameCheck').addClass('alert-danger');
  209. } else {
  210. $('#newPageNameCheck').removeClass('alert-danger');
  211. $('#newPageNameCheck').html('<img src="/images/loading_s.gif"> 移動しました。移動先にジャンプします。');
  212. setTimeout(function() {
  213. top.location.href = data.page.path + '?renamed=' + path;
  214. }, 1000);
  215. }
  216. });
  217. return false;
  218. });
  219. });