notice.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /**
  2. * @fileOverview
  3. *
  4. * 通知小组件
  5. *
  6. * @author: techird
  7. * @copyright: Baidu FEX, 2014
  8. */
  9. KityMinder.registerUI('widget/notice', function(minder) {
  10. var errorMessage = minder.getLang('error_message');
  11. var memory = minder.getUI('memory');
  12. var $notice = $('<div>')
  13. .addClass('notice-widget')
  14. .appendTo('#content-wrapper');
  15. var $mask = $('<div>')
  16. .addClass('error-mask');
  17. var $error = new FUI.Dialog({
  18. width: 500,
  19. height: 'auto',
  20. prompt: true,
  21. caption: errorMessage.title,
  22. className: 'error-dialog'
  23. }).appendTo(document.getElementById('content-wrapper'));
  24. $error.on('ok cancel', function(e) {
  25. if (error.resolve) error.resolve(e);
  26. });
  27. var $error_body = $($error.getBodyElement());
  28. var isBuilded = (function() {
  29. var scripts = [].slice.apply(document.getElementsByTagName('script'));
  30. var s, m;
  31. while ((s = scripts.pop())) {
  32. if ((m = /kityminder.*\.min\.js/.exec(s.src))) return m[0];
  33. }
  34. return false;
  35. })();
  36. // concatMap: sperate files -> join file
  37. // minMap: join file -> min file
  38. var concatMap, minMap;
  39. function fixSourceSymbol($ta, $mask) {
  40. function fix() {
  41. var text = $ta.text();
  42. var pattern = new RegExp('at.+' + isBuilded + '.+\\:(\\d+)\\:(\\d+)\\)?', 'g');
  43. var match;
  44. $ta.text(text.replace(pattern, function(match, $1, $2) {
  45. var lookup = {
  46. line: +$1,
  47. column: +$2
  48. };
  49. var info = minMap.originalPositionFor(lookup);
  50. var name = info.name;
  51. lookup = {
  52. line: info.line,
  53. column: info.column
  54. };
  55. info = concatMap.originalPositionFor(lookup);
  56. name = name || '<Anonymous>';
  57. var replaced = 'at ' + name + ' (' +
  58. info.source.replace('../', '') + ':' + info.line + ':' + info.column + ')';
  59. if (replaced.indexOf('promise') != -1) {
  60. replaced = 'at <async> Promise.' + name;
  61. }
  62. return replaced;
  63. }));
  64. }
  65. if (isBuilded) {
  66. if (concatMap) return fix();
  67. $mask.addClass('loading');
  68. setTimeout(function() {
  69. $mask.removeClass('loading');
  70. }, 5000);
  71. var script = document.createElement('script');
  72. script.onload = function() {
  73. Promise.all([
  74. $.pajax({
  75. url: isBuilded.replace('min.js', 'js.map'),
  76. dataType: 'json'
  77. }),
  78. $.pajax({
  79. url: isBuilded.replace('.js', '.map'),
  80. dataType: 'json'
  81. })
  82. ]).then(function(files) {
  83. concatMap = new window.sourceMap.SourceMapConsumer(files[0]);
  84. minMap = new window.sourceMap.SourceMapConsumer(files[1]);
  85. fix();
  86. $mask.removeClass('loading');
  87. });
  88. };
  89. script.src = 'lib/source-map.min.js';
  90. document.head.appendChild(script);
  91. }
  92. }
  93. $error_body.delegate('.error-detail a.expander', 'click', function(e) {
  94. var $detail = $(e.target).closest('.error-detail').toggleClass('expanded');
  95. var showDetail = $detail.hasClass('expanded');
  96. memory.set('show-error-detail', showDetail);
  97. });
  98. function info(msg, warn, time) {
  99. if (!$notice.hasClass('show')) $notice.empty();
  100. clearTimeout(info.ttl2);
  101. if (warn) $notice.addClass('warn');
  102. else $notice.removeClass('warn');
  103. var $menu = minder.getUI('menu/menu');
  104. $notice.css({
  105. top: $menu && $menu.isVisible() ?
  106. $('#main-menu .main-menu-level1').offset().top :
  107. $('#kityminder').offset().top + 20
  108. });
  109. $notice.append($('<p>').text(msg));
  110. $notice.addClass('show');
  111. clearTimeout(info.ttl);
  112. time = time || (warn ? 5000 : 3000);
  113. info.ttl = setTimeout(function() {
  114. $notice.removeClass('show');
  115. info.ttl2 = setTimeout(function() {
  116. $notice.empty();
  117. }, 1000);
  118. }, time);
  119. }
  120. function warn(msg) {
  121. info(msg, warn);
  122. }
  123. function descriptReason(e) {
  124. e = e || new Error();
  125. if (typeof(e) == 'string') {
  126. e = new Error(e);
  127. }
  128. if (e.getDetail) return e;
  129. // 文件访问错误
  130. if (typeof(fio) != 'undefined' && (e instanceof fio.FileRequestError)) {
  131. if (!e.status) {
  132. e.description = errorMessage.err_network;
  133. } else {
  134. e.description = errorMessage.pcs_code[e.detail.error_code];
  135. }
  136. e.getDetail = function() {
  137. return JSON.stringify(e, null, 4);
  138. };
  139. }
  140. // jqXhr
  141. else if ('readyState' in e) {
  142. } else {
  143. e.getDetail = function() {
  144. return e.stack || new Error().stack;
  145. };
  146. }
  147. return e;
  148. }
  149. function error(name, e) {
  150. if (arguments.length == 1) {
  151. e = name;
  152. name = 'unknown';
  153. }
  154. $error_body.empty();
  155. e = descriptReason(e);
  156. var $content = $('<div>')
  157. .addClass('error-content')
  158. .appendTo($error_body);
  159. var $msg = $('<h3>')
  160. .text(errorMessage[name] || errorMessage.err_unknown)
  161. .appendTo($content);
  162. var $reason = $('<p>')
  163. .text(e.message || e.description || errorMessage.unknownreason)
  164. .appendTo($content);
  165. if (e.getDetail) {
  166. var $detail = $('<div>')
  167. .addClass('error-detail')
  168. .append($('<a class="expander"></a>').text(minder.getLang('ui.error_detail')))
  169. .appendTo($error_body);
  170. var $detailContent = $('<div>')
  171. .addClass('error-detail-wrapper')
  172. .appendTo($detail);
  173. var $textarea = $('<textarea>')
  174. .attr('id', 'error-detail-content')
  175. .text(e.getDetail() + '\n\n浏览器信息:' + navigator.userAgent)
  176. .appendTo($detailContent);
  177. fixSourceSymbol($textarea, $detailContent);
  178. var $copy = $('<button>')
  179. .addClass('copy-and-feedback')
  180. .text(minder.getLang('ui.copy_and_feedback'))
  181. .appendTo($detailContent);
  182. $copy.attr('data-clipboard-target', 'error-detail-content');
  183. zeroCopy($copy);
  184. if (memory.get('show-error-detail')) $detail.addClass('expanded');
  185. }
  186. $error.show();
  187. $error.getElement().style.top = '180px';
  188. return new Promise(function(resolve) {
  189. error.resolve = resolve;
  190. });
  191. }
  192. function zeroCopy($target) {
  193. /* global ZeroClipboard:true */
  194. if (window.ZeroClipboard) {
  195. ZeroClipboard.config({
  196. swfPath: 'lib/ZeroClipboard.swf',
  197. hoverClass: 'hover',
  198. activeClass: 'active'
  199. });
  200. var clip = new window.ZeroClipboard($target);
  201. clip.on('ready', function() {
  202. clip.on('aftercopy', function() {
  203. $error.hide();
  204. minder.getUI('topbar/feedback').click();
  205. });
  206. });
  207. } else {
  208. $target.remove();
  209. }
  210. }
  211. return {
  212. info: info,
  213. error: error,
  214. warn: warn
  215. };
  216. });