├── answerSearch └── main.js ├── background.js ├── chatOnline ├── Markdown.Converter.js ├── SaeChannel.js ├── at.js ├── chatroom.css ├── desert.css ├── emotion.js ├── jquery-2.0.3.min.js ├── main.js └── prettify.js ├── cmt-enhanced ├── cmt-enhanced.css └── main.js ├── favicon.ico ├── hotkey └── main.js ├── img ├── icon128.png ├── icon16.png ├── icon24.png └── icon48.png ├── jquery-2.0.3.min.js ├── main.js ├── manifest.json ├── options.html ├── options.js ├── popup.html ├── readme.md └── reputation-chart-3d ├── iso.js ├── main.js └── obelisk.js /answerSearch/main.js: -------------------------------------------------------------------------------- 1 | chrome.storage.sync.get('answerSearch', function(d) { 2 | if(+d.answerSearch) return false; 3 | if(location.pathname.split('/').pop() != "answers") return false; 4 | 5 | var _t = $("h2.h4"); 6 | _t.after(''+ 10 | '
'+ 11 | '时间排序'+ 12 | '评分排序'+ 13 | '
'); 14 | $(document).on('keydown', '.answer-search input', function(e) {if(e.keyCode == 13) $('.answer-search button').click();}) 15 | 16 | $('.answer-search button').click(function() { 17 | var _s = $('.answer-search input').val(); 18 | if(!_s) { 19 | alert('Please input keywords!'); 20 | return false; 21 | } 22 | 23 | var _m = $(this).parent().next().next(); 24 | _m.html('Searching...'); 25 | _m.next().remove(); 26 | 27 | getAnswers(function(answers) { 28 | var _k = _s.split(' '); 29 | _k.forEach(function(key) { 30 | answers = answers.filter(function(answer) { 31 | return (answer.parent.title+answer.parent.content+answer.content).match(key); 32 | }); 33 | }) 34 | $('.searching', _m).remove(); 35 | answers.length ? answers.forEach(function(answer) {_m.append(answer.obj);}) : _m.append('没有搜索结果'); 36 | return false; 37 | }) 38 | }); 39 | 40 | $('.answer-sort .sortby-modified').click(function(e) { 41 | e.preventDefault(); 42 | $('.answer-sort .active').removeClass('active'); 43 | $(this).addClass('active'); 44 | var _m = $(this).parent().next(); 45 | _m.html('Sorting...'); 46 | _m.next().remove(); 47 | getAnswers(function(answers) { 48 | $(".sorting", _m).remove(); 49 | answers.length ? answers.forEach(function(answer) { 50 | _m.append(answer.obj); 51 | }) : _m.append('该用户暂时没有回答'); 52 | }) 53 | return false; 54 | }) 55 | 56 | $('.answer-sort .sortby-rank').click(function(e) { 57 | e.preventDefault(); 58 | $('.answer-sort .active').removeClass('active'); 59 | $(this).addClass('active'); 60 | var _m = $(this).parent().next(); 61 | _m.html('Sorting...'); 62 | _m.next().remove(); 63 | getAnswers(function(answers) { 64 | var answersByRank = JSON.parse( JSON.stringify(answers) ); 65 | answersByRank.sort(function(r,n) {return n.like - r.like}); 66 | $(".sorting", _m).remove(); 67 | answersByRank.length ? answersByRank.forEach(function(answer) { 68 | _m.append(answer.obj); 69 | }) : _m.append('该用户暂时没有回答'); 70 | }) 71 | return false; 72 | }) 73 | 74 | function getAnswers(callback) { 75 | callback = callback || function() {}; 76 | var baseUrl = "http://segmentfault.com"; 77 | var _t = $("h2.h4"), 78 | _n = _t.text().match(/\d+/g)[0]/1, 79 | _o = Math.ceil(_n/20), 80 | _u = location.pathname.split('/')[1], 81 | prefixUrl = baseUrl+location.pathname+'?page='; 82 | var answers = localStorage['AnswersOf'+_u] ? JSON.parse(localStorage['AnswersOf'+_u]) : []; 83 | 84 | (function getAnswersList(page) { 85 | if(page >_o || answers.length >= _n) { 86 | localStorage['AnswersOf'+_u] = JSON.stringify(answers); 87 | callback(answers); 88 | } 89 | 90 | $.get(prefixUrl+page, function(res) { 91 | console.log('Load page '+page+' of answers list success!'); 92 | 93 | var articles = $('.stream-list section', res); 94 | if(articles.length == 0) return false; 95 | (function getAnswer(article) { 96 | if(article.length==0) { 97 | getAnswersList(page+1); 98 | return false; 99 | } 100 | var answerUrl = baseUrl+$(".title a", article).attr('href'); 101 | $.ajax({ 102 | url: answerUrl, 103 | type: "GET", 104 | dataType: "html", 105 | success: function(res) { 106 | console.log('Load Answer #'+answerUrl+' success!'); 107 | answers.push({ 108 | url: answerUrl, 109 | obj: article[0].outerHTML, 110 | like: $(".widget-answers .count", res).html()/1, 111 | content: $(".answer", res).text(), 112 | parent: { 113 | title: $('.title a', article).text(), 114 | url: baseUrl+$(".main .text-center .btn-primary", res).attr('href'), 115 | content: $('.question', res).text() 116 | }, 117 | }); 118 | 119 | }, 120 | error: function() { 121 | 122 | }, 123 | complete: function() { 124 | getAnswer(article.next()); 125 | } 126 | }) 127 | }($(articles[0])) ); 128 | }) 129 | }(1)) 130 | } 131 | }) 132 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | // chrome.webRequest.onBeforeSendHeaders.addListener(function(details) { 2 | // if (details.type === 'xmlhttprequest') { 3 | // var exists = false; 4 | // for (var i = 0; i < details.requestHeaders.length; ++i) { 5 | // if (details.requestHeaders[i].name === 'Referer') { 6 | // exists = true; 7 | // details.requestHeaders[i].value = 'http://segmentfault.com/'; 8 | // break; 9 | // } 10 | // } 11 | 12 | // if (!exists) { 13 | // details.requestHeaders.push({ name: 'Referer', value: 'http://segmentfault.com/'}); 14 | // } 15 | 16 | // return { requestHeaders: details.requestHeaders }; 17 | // } 18 | // }, {urls: ["http://segmentfault.com/api/user*"]}, ["blocking", "requestHeaders"]) 19 | // var getOption = function() { 20 | // return { 21 | // url: 'http://segmentfault.com', 22 | // name: 'sfsess' 23 | // } 24 | // }, 25 | // simpleRequest = function(url, onsuccess, onnotsignin, onerror) { 26 | // var xhr = new XMLHttpRequest(); 27 | // xhr.onreadystatechange = function() { 28 | // if(xhr.readyState == 4) { 29 | // if(xhr.status == 200) { 30 | // var data = xhr.responseText; 31 | // //the server redirect to signin page when haven't signin 32 | // if(data.indexOf('jsonp') !== 0) { 33 | // (onnotsignin && typeof onnotsignin === 'function') && onnotsignin.call(null); 34 | // return; 35 | // } 36 | // onsuccess.call(null, data); 37 | // } else { 38 | // if(onerror && (typeof onerror === 'function')) { 39 | // onerror.call(); 40 | // } 41 | // } 42 | // } 43 | // } 44 | // xhr.open('GET', url, true); 45 | // xhr.send(null); 46 | // }, 47 | // setBadgeText = function(text) { 48 | // chrome.browserAction.setBadgeText({ 49 | // text: text === '0' ? '' : text 50 | // }); 51 | // }, 52 | // getCookie = function(option) { 53 | // var sfsess = localStorage.getItem('sfsess'); 54 | // if(sfsess) { 55 | // return sfsess; 56 | // } else { 57 | // chrome.cookies.get(option, function(cookie) { 58 | // if(cookie) { 59 | // sfsess = cookie.value; 60 | // localStorage.setItem('sfsess', sfsess); 61 | // } else { 62 | // setBadgeText('x'); 63 | // } 64 | 65 | // }) 66 | // } 67 | // return sfsess; 68 | // } 69 | 70 | // var url = "http://segmentfault.com/api/user?do=stat"; 71 | // setInterval(function() { 72 | // simpleRequest(url, function(data) { 73 | // var data = JSON.parse(data).data; 74 | // setBadgeText(data.events+''); 75 | // }, function() { 76 | // setBadgeText('x'); 77 | // }) 78 | // }, 10*1000); -------------------------------------------------------------------------------- /chatOnline/Markdown.Converter.js: -------------------------------------------------------------------------------- 1 | var Markdown; 2 | 3 | if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module 4 | Markdown = exports; 5 | else 6 | Markdown = {}; 7 | 8 | // The following text is included for historical reasons, but should 9 | // be taken with a pinch of salt; it's not all true anymore. 10 | 11 | // 12 | // Wherever possible, Showdown is a straight, line-by-line port 13 | // of the Perl version of Markdown. 14 | // 15 | // This is not a normal parser design; it's basically just a 16 | // series of string substitutions. It's hard to read and 17 | // maintain this way, but keeping Showdown close to the original 18 | // design makes it easier to port new features. 19 | // 20 | // More importantly, Showdown behaves like markdown.pl in most 21 | // edge cases. So web applications can do client-side preview 22 | // in Javascript, and then build identical HTML on the server. 23 | // 24 | // This port needs the new RegExp functionality of ECMA 262, 25 | // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers 26 | // should do fine. Even with the new regular expression features, 27 | // We do a lot of work to emulate Perl's regex functionality. 28 | // The tricky changes in this file mostly have the "attacklab:" 29 | // label. Major or self-explanatory changes don't. 30 | // 31 | // Smart diff tools like Araxis Merge will be able to match up 32 | // this file with markdown.pl in a useful way. A little tweaking 33 | // helps: in a copy of markdown.pl, replace "#" with "//" and 34 | // replace "$text" with "text". Be sure to ignore whitespace 35 | // and line endings. 36 | // 37 | 38 | 39 | // 40 | // Usage: 41 | // 42 | // var text = "Markdown *rocks*."; 43 | // 44 | // var converter = new Markdown.Converter(); 45 | // var html = converter.makeHtml(text); 46 | // 47 | // alert(html); 48 | // 49 | // Note: move the sample code to the bottom of this 50 | // file before uncommenting it. 51 | // 52 | 53 | (function () { 54 | 55 | function identity(x) { return x; } 56 | function returnFalse(x) { return false; } 57 | 58 | function HookCollection() { } 59 | 60 | HookCollection.prototype = { 61 | 62 | chain: function (hookname, func) { 63 | var original = this[hookname]; 64 | if (!original) 65 | throw new Error("unknown hook " + hookname); 66 | 67 | if (original === identity) 68 | this[hookname] = func; 69 | else 70 | this[hookname] = function (text) { 71 | var args = Array.prototype.slice.call(arguments, 0); 72 | args[0] = original.apply(null, args); 73 | return func.apply(null, args); 74 | }; 75 | }, 76 | set: function (hookname, func) { 77 | if (!this[hookname]) 78 | throw new Error("unknown hook " + hookname); 79 | this[hookname] = func; 80 | }, 81 | addNoop: function (hookname) { 82 | this[hookname] = identity; 83 | }, 84 | addFalse: function (hookname) { 85 | this[hookname] = returnFalse; 86 | } 87 | }; 88 | 89 | Markdown.HookCollection = HookCollection; 90 | 91 | // g_urls and g_titles allow arbitrary user-entered strings as keys. This 92 | // caused an exception (and hence stopped the rendering) when the user entered 93 | // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this 94 | // (since no builtin property starts with "s_"). See 95 | // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug 96 | // (granted, switching from Array() to Object() alone would have left only __proto__ 97 | // to be a problem) 98 | function SaveHash() { } 99 | SaveHash.prototype = { 100 | set: function (key, value) { 101 | this["s_" + key] = value; 102 | }, 103 | get: function (key) { 104 | return this["s_" + key]; 105 | } 106 | }; 107 | 108 | Markdown.Converter = function () { 109 | var pluginHooks = this.hooks = new HookCollection(); 110 | 111 | // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link 112 | pluginHooks.addNoop("plainLinkText"); 113 | 114 | // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked 115 | pluginHooks.addNoop("preConversion"); 116 | 117 | // called with the text once all normalizations have been completed (tabs to spaces, line endings, etc.), but before any conversions have 118 | pluginHooks.addNoop("postNormalization"); 119 | 120 | // Called with the text before / after creating block elements like code blocks and lists. Note that this is called recursively 121 | // with inner content, e.g. it's called with the full text, and then only with the content of a blockquote. The inner 122 | // call will receive outdented text. 123 | pluginHooks.addNoop("preBlockGamut"); 124 | pluginHooks.addNoop("postBlockGamut"); 125 | 126 | // called with the text of a single block element before / after the span-level conversions (bold, code spans, etc.) have been made 127 | pluginHooks.addNoop("preSpanGamut"); 128 | pluginHooks.addNoop("postSpanGamut"); 129 | 130 | // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml 131 | pluginHooks.addNoop("postConversion"); 132 | 133 | // 134 | // Private state of the converter instance: 135 | // 136 | 137 | // Global hashes, used by various utility routines 138 | var g_urls; 139 | var g_titles; 140 | var g_html_blocks; 141 | 142 | // Used to track when we're inside an ordered or unordered list 143 | // (see _ProcessListItems() for details): 144 | var g_list_level; 145 | 146 | this.makeHtml = function (text) { 147 | 148 | // 149 | // Main function. The order in which other subs are called here is 150 | // essential. Link and image substitutions need to happen before 151 | // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the 152 | // and tags get encoded. 153 | // 154 | 155 | // This will only happen if makeHtml on the same converter instance is called from a plugin hook. 156 | // Don't do that. 157 | if (g_urls) 158 | throw new Error("Recursive call to converter.makeHtml"); 159 | 160 | // Create the private state objects. 161 | g_urls = new SaveHash(); 162 | g_titles = new SaveHash(); 163 | g_html_blocks = []; 164 | g_list_level = 0; 165 | 166 | text = pluginHooks.preConversion(text); 167 | 168 | // attacklab: Replace ~ with ~T 169 | // This lets us use tilde as an escape char to avoid md5 hashes 170 | // The choice of character is arbitray; anything that isn't 171 | // magic in Markdown will work. 172 | text = text.replace(/~/g, "~T"); 173 | 174 | // attacklab: Replace $ with ~D 175 | // RegExp interprets $ as a special character 176 | // when it's in a replacement string 177 | text = text.replace(/\$/g, "~D"); 178 | 179 | // Standardize line endings 180 | text = text.replace(/\r\n/g, "\n"); // DOS to Unix 181 | text = text.replace(/\r/g, "\n"); // Mac to Unix 182 | 183 | // Make sure text begins and ends with a couple of newlines: 184 | text = "\n\n" + text + "\n\n"; 185 | 186 | // Convert all tabs to spaces. 187 | text = _Detab(text); 188 | 189 | // Strip any lines consisting only of spaces and tabs. 190 | // This makes subsequent regexen easier to write, because we can 191 | // match consecutive blank lines with /\n+/ instead of something 192 | // contorted like /[ \t]*\n+/ . 193 | text = text.replace(/^[ \t]+$/mg, ""); 194 | 195 | text = pluginHooks.postNormalization(text); 196 | 197 | // Turn block-level HTML blocks into hash entries 198 | text = _HashHTMLBlocks(text); 199 | 200 | // Strip link definitions, store in hashes. 201 | text = _StripLinkDefinitions(text); 202 | 203 | text = _RunBlockGamut(text); 204 | 205 | text = _UnescapeSpecialChars(text); 206 | 207 | // attacklab: Restore dollar signs 208 | text = text.replace(/~D/g, "$$"); 209 | 210 | // attacklab: Restore tildes 211 | text = text.replace(/~T/g, "~"); 212 | 213 | text = pluginHooks.postConversion(text); 214 | 215 | g_html_blocks = g_titles = g_urls = null; 216 | 217 | return text; 218 | }; 219 | 220 | function _StripLinkDefinitions(text) { 221 | // 222 | // Strips link definitions from text, stores the URLs and titles in 223 | // hash references. 224 | // 225 | 226 | // Link defs are in the form: ^[id]: url "optional title" 227 | 228 | /* 229 | text = text.replace(/ 230 | ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 231 | [ \t]* 232 | \n? // maybe *one* newline 233 | [ \t]* 234 | ? // url = $2 235 | (?=\s|$) // lookahead for whitespace instead of the lookbehind removed below 236 | [ \t]* 237 | \n? // maybe one newline 238 | [ \t]* 239 | ( // (potential) title = $3 240 | (\n*) // any lines skipped = $4 attacklab: lookbehind removed 241 | [ \t]+ 242 | ["(] 243 | (.+?) // title = $5 244 | [")] 245 | [ \t]* 246 | )? // title is optional 247 | (?:\n+|$) 248 | /gm, function(){...}); 249 | */ 250 | 251 | text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, 252 | function (wholeMatch, m1, m2, m3, m4, m5) { 253 | m1 = m1.toLowerCase(); 254 | g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive 255 | if (m4) { 256 | // Oops, found blank lines, so it's not a title. 257 | // Put back the parenthetical statement we stole. 258 | return m3; 259 | } else if (m5) { 260 | g_titles.set(m1, m5.replace(/"/g, """)); 261 | } 262 | 263 | // Completely remove the definition from the text 264 | return ""; 265 | } 266 | ); 267 | 268 | return text; 269 | } 270 | 271 | function _HashHTMLBlocks(text) { 272 | 273 | // Hashify HTML blocks: 274 | // We only want to do this for block-level HTML tags, such as headers, 275 | // lists, and tables. That's because we still want to wrap

s around 276 | // "paragraphs" that are wrapped in non-block-level tags, such as anchors, 277 | // phrase emphasis, and spans. The list of tags we're looking for is 278 | // hard-coded: 279 | var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" 280 | var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" 281 | 282 | // First, look for nested blocks, e.g.: 283 | //

284 | //
285 | // tags for inner block must be indented. 286 | //
287 | //
288 | // 289 | // The outermost tags must start at the left margin for this to match, and 290 | // the inner nested divs must be indented. 291 | // We need to do this before the next, more liberal match, because the next 292 | // match will start at the first `
` and stop at the first `
`. 293 | 294 | // attacklab: This regex can be expensive when it fails. 295 | 296 | /* 297 | text = text.replace(/ 298 | ( // save in $1 299 | ^ // start of line (with /m) 300 | <($block_tags_a) // start tag = $2 301 | \b // word break 302 | // attacklab: hack around khtml/pcre bug... 303 | [^\r]*?\n // any number of lines, minimally matching 304 | // the matching end tag 305 | [ \t]* // trailing spaces/tabs 306 | (?=\n+) // followed by a newline 307 | ) // attacklab: there are sentinel newlines at end of document 308 | /gm,function(){...}}; 309 | */ 310 | text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashElement); 311 | 312 | // 313 | // Now match more liberally, simply from `\n` to `\n` 314 | // 315 | 316 | /* 317 | text = text.replace(/ 318 | ( // save in $1 319 | ^ // start of line (with /m) 320 | <($block_tags_b) // start tag = $2 321 | \b // word break 322 | // attacklab: hack around khtml/pcre bug... 323 | [^\r]*? // any number of lines, minimally matching 324 | .* // the matching end tag 325 | [ \t]* // trailing spaces/tabs 326 | (?=\n+) // followed by a newline 327 | ) // attacklab: there are sentinel newlines at end of document 328 | /gm,function(){...}}; 329 | */ 330 | text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement); 331 | 332 | // Special case just for
. It was easier to make a special case than 333 | // to make the other regex more complicated. 334 | 335 | /* 336 | text = text.replace(/ 337 | \n // Starting after a blank line 338 | [ ]{0,3} 339 | ( // save in $1 340 | (<(hr) // start tag = $2 341 | \b // word break 342 | ([^<>])*? 343 | \/?>) // the matching end tag 344 | [ \t]* 345 | (?=\n{2,}) // followed by a blank line 346 | ) 347 | /g,hashElement); 348 | */ 349 | text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement); 350 | 351 | // Special case for standalone HTML comments: 352 | 353 | /* 354 | text = text.replace(/ 355 | \n\n // Starting after a blank line 356 | [ ]{0,3} // attacklab: g_tab_width - 1 357 | ( // save in $1 358 | -]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackoverflow.com/q/95256 360 | > 361 | [ \t]* 362 | (?=\n{2,}) // followed by a blank line 363 | ) 364 | /g,hashElement); 365 | */ 366 | text = text.replace(/\n\n[ ]{0,3}(-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashElement); 367 | 368 | // PHP and ASP-style processor instructions ( and <%...%>) 369 | 370 | /* 371 | text = text.replace(/ 372 | (?: 373 | \n\n // Starting after a blank line 374 | ) 375 | ( // save in $1 376 | [ ]{0,3} // attacklab: g_tab_width - 1 377 | (?: 378 | <([?%]) // $2 379 | [^\r]*? 380 | \2> 381 | ) 382 | [ \t]* 383 | (?=\n{2,}) // followed by a blank line 384 | ) 385 | /g,hashElement); 386 | */ 387 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement); 388 | 389 | return text; 390 | } 391 | 392 | function hashElement(wholeMatch, m1) { 393 | var blockText = m1; 394 | 395 | // Undo double lines 396 | blockText = blockText.replace(/^\n+/, ""); 397 | 398 | // strip trailing blank lines 399 | blockText = blockText.replace(/\n+$/g, ""); 400 | 401 | // Replace the element text with a marker ("~KxK" where x is its key) 402 | blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n"; 403 | 404 | return blockText; 405 | } 406 | 407 | var blockGamutHookCallback = function (t) { return _RunBlockGamut(t); } 408 | 409 | function _RunBlockGamut(text, doNotUnhash) { 410 | // 411 | // These are all the transformations that form block-level 412 | // tags like paragraphs, headers, and list items. 413 | // 414 | 415 | text = pluginHooks.preBlockGamut(text, blockGamutHookCallback); 416 | 417 | text = _DoHeaders(text); 418 | 419 | // Do Horizontal Rules: 420 | var replacement = "
\n"; 421 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement); 422 | text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement); 423 | text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement); 424 | 425 | text = _DoLists(text); 426 | text = _DoCodeBlocks(text); 427 | text = _DoBlockQuotes(text); 428 | 429 | text = pluginHooks.postBlockGamut(text, blockGamutHookCallback); 430 | 431 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that 432 | // was to escape raw HTML in the original Markdown source. This time, 433 | // we're escaping the markup we've just created, so that we don't wrap 434 | //

tags around block-level tags. 435 | text = _HashHTMLBlocks(text); 436 | text = _FormParagraphs(text, doNotUnhash); 437 | 438 | return text; 439 | } 440 | 441 | function _RunSpanGamut(text) { 442 | // 443 | // These are all the transformations that occur *within* block-level 444 | // tags like paragraphs, headers, and list items. 445 | // 446 | 447 | text = pluginHooks.preSpanGamut(text); 448 | 449 | text = _DoCodeSpans(text); 450 | text = _EscapeSpecialCharsWithinTagAttributes(text); 451 | text = _EncodeBackslashEscapes(text); 452 | 453 | // Process anchor and image tags. Images must come first, 454 | // because ![foo][f] looks like an anchor. 455 | text = _DoImages(text); 456 | text = _DoAnchors(text); 457 | 458 | // Make links out of things like `` 459 | // Must come after _DoAnchors(), because you can use < and > 460 | // delimiters in inline links like [this](). 461 | text = _DoAutoLinks(text); 462 | 463 | text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now 464 | 465 | text = _EncodeAmpsAndAngles(text); 466 | text = _DoItalicsAndBold(text); 467 | 468 | // Do hard breaks: 469 | text = text.replace(/ +\n/g, "
\n"); 470 | 471 | text = pluginHooks.postSpanGamut(text); 472 | 473 | return text; 474 | } 475 | 476 | function _EscapeSpecialCharsWithinTagAttributes(text) { 477 | // 478 | // Within tags -- meaning between < and > -- encode [\ ` * _] so they 479 | // don't conflict with their use in Markdown for code, italics and strong. 480 | // 481 | 482 | // Build a regex to find HTML tags and comments. See Friedl's 483 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. 484 | 485 | // SE: changed the comment part of the regex 486 | 487 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi; 488 | 489 | text = text.replace(regex, function (wholeMatch) { 490 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); 491 | tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987 492 | return tag; 493 | }); 494 | 495 | return text; 496 | } 497 | 498 | function _DoAnchors(text) { 499 | // 500 | // Turn Markdown link shortcuts into XHTML
tags. 501 | // 502 | // 503 | // First, handle reference-style links: [link text] [id] 504 | // 505 | 506 | /* 507 | text = text.replace(/ 508 | ( // wrap whole match in $1 509 | \[ 510 | ( 511 | (?: 512 | \[[^\]]*\] // allow brackets nested one level 513 | | 514 | [^\[] // or anything else 515 | )* 516 | ) 517 | \] 518 | 519 | [ ]? // one optional space 520 | (?:\n[ ]*)? // one optional newline followed by spaces 521 | 522 | \[ 523 | (.*?) // id = $3 524 | \] 525 | ) 526 | ()()()() // pad remaining backreferences 527 | /g, writeAnchorTag); 528 | */ 529 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag); 530 | 531 | // 532 | // Next, inline-style links: [link text](url "optional title") 533 | // 534 | 535 | /* 536 | text = text.replace(/ 537 | ( // wrap whole match in $1 538 | \[ 539 | ( 540 | (?: 541 | \[[^\]]*\] // allow brackets nested one level 542 | | 543 | [^\[\]] // or anything else 544 | )* 545 | ) 546 | \] 547 | \( // literal paren 548 | [ \t]* 549 | () // no id, so leave $3 empty 550 | ? 557 | [ \t]* 558 | ( // $5 559 | (['"]) // quote char = $6 560 | (.*?) // Title = $7 561 | \6 // matching quote 562 | [ \t]* // ignore any spaces/tabs between closing quote and ) 563 | )? // title is optional 564 | \) 565 | ) 566 | /g, writeAnchorTag); 567 | */ 568 | 569 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag); 570 | 571 | // 572 | // Last, handle reference-style shortcuts: [link text] 573 | // These must come last in case you've also got [link test][1] 574 | // or [link test](/foo) 575 | // 576 | 577 | /* 578 | text = text.replace(/ 579 | ( // wrap whole match in $1 580 | \[ 581 | ([^\[\]]+) // link text = $2; can't contain '[' or ']' 582 | \] 583 | ) 584 | ()()()()() // pad rest of backreferences 585 | /g, writeAnchorTag); 586 | */ 587 | text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); 588 | 589 | return text; 590 | } 591 | 592 | function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) { 593 | if (m7 == undefined) m7 = ""; 594 | var whole_match = m1; 595 | var link_text = m2.replace(/:\/\//g, "~P"); // to prevent auto-linking withing the link. will be converted back after the auto-linker runs 596 | var link_id = m3.toLowerCase(); 597 | var url = m4; 598 | var title = m7; 599 | 600 | if (url == "") { 601 | if (link_id == "") { 602 | // lower-case and turn embedded newlines into spaces 603 | link_id = link_text.toLowerCase().replace(/ ?\n/g, " "); 604 | } 605 | url = "#" + link_id; 606 | 607 | if (g_urls.get(link_id) != undefined) { 608 | url = g_urls.get(link_id); 609 | if (g_titles.get(link_id) != undefined) { 610 | title = g_titles.get(link_id); 611 | } 612 | } 613 | else { 614 | if (whole_match.search(/\(\s*\)$/m) > -1) { 615 | // Special case for explicit empty url 616 | url = ""; 617 | } else { 618 | return whole_match; 619 | } 620 | } 621 | } 622 | url = encodeProblemUrlChars(url); 623 | url = escapeCharacters(url, "*_"); 624 | var result = ""; 633 | 634 | return result; 635 | } 636 | 637 | function _DoImages(text) { 638 | // 639 | // Turn Markdown image shortcuts into tags. 640 | // 641 | 642 | // 643 | // First, handle reference-style labeled images: ![alt text][id] 644 | // 645 | 646 | /* 647 | text = text.replace(/ 648 | ( // wrap whole match in $1 649 | !\[ 650 | (.*?) // alt text = $2 651 | \] 652 | 653 | [ ]? // one optional space 654 | (?:\n[ ]*)? // one optional newline followed by spaces 655 | 656 | \[ 657 | (.*?) // id = $3 658 | \] 659 | ) 660 | ()()()() // pad rest of backreferences 661 | /g, writeImageTag); 662 | */ 663 | text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag); 664 | 665 | // 666 | // Next, handle inline images: ![alt text](url "optional title") 667 | // Don't forget: encode * and _ 668 | 669 | /* 670 | text = text.replace(/ 671 | ( // wrap whole match in $1 672 | !\[ 673 | (.*?) // alt text = $2 674 | \] 675 | \s? // One optional whitespace character 676 | \( // literal paren 677 | [ \t]* 678 | () // no id, so leave $3 empty 679 | ? // src url = $4 680 | [ \t]* 681 | ( // $5 682 | (['"]) // quote char = $6 683 | (.*?) // title = $7 684 | \6 // matching quote 685 | [ \t]* 686 | )? // title is optional 687 | \) 688 | ) 689 | /g, writeImageTag); 690 | */ 691 | text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); 692 | 693 | return text; 694 | } 695 | 696 | function attributeEncode(text) { 697 | // unconditionally replace angle brackets here -- what ends up in an attribute (e.g. alt or title) 698 | // never makes sense to have verbatim HTML in it (and the sanitizer would totally break it) 699 | return text.replace(/>/g, ">").replace(/" + _RunSpanGamut(m1) + "\n\n"; } 758 | ); 759 | 760 | text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, 761 | function (matchFound, m1) { return "

" + _RunSpanGamut(m1) + "

\n\n"; } 762 | ); 763 | 764 | // atx-style headers: 765 | // # Header 1 766 | // ## Header 2 767 | // ## Header 2 with closing hashes ## 768 | // ... 769 | // ###### Header 6 770 | // 771 | 772 | /* 773 | text = text.replace(/ 774 | ^(\#{1,6}) // $1 = string of #'s 775 | [ \t]* 776 | (.+?) // $2 = Header text 777 | [ \t]* 778 | \#* // optional closing #'s (not counted) 779 | \n+ 780 | /gm, function() {...}); 781 | */ 782 | 783 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, 784 | function (wholeMatch, m1, m2) { 785 | var h_level = m1.length; 786 | return "" + _RunSpanGamut(m2) + "\n\n"; 787 | } 788 | ); 789 | 790 | return text; 791 | } 792 | 793 | function _DoLists(text, isInsideParagraphlessListItem) { 794 | // 795 | // Form HTML ordered (numbered) and unordered (bulleted) lists. 796 | // 797 | 798 | // attacklab: add sentinel to hack around khtml/safari bug: 799 | // http://bugs.webkit.org/show_bug.cgi?id=11231 800 | text += "~0"; 801 | 802 | // Re-usable pattern to match any entirel ul or ol list: 803 | 804 | /* 805 | var whole_list = / 806 | ( // $1 = whole list 807 | ( // $2 808 | [ ]{0,3} // attacklab: g_tab_width - 1 809 | ([*+-]|\d+[.]) // $3 = first list item marker 810 | [ \t]+ 811 | ) 812 | [^\r]+? 813 | ( // $4 814 | ~0 // sentinel for workaround; should be $ 815 | | 816 | \n{2,} 817 | (?=\S) 818 | (?! // Negative lookahead for another list item marker 819 | [ \t]* 820 | (?:[*+-]|\d+[.])[ \t]+ 821 | ) 822 | ) 823 | ) 824 | /g 825 | */ 826 | var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; 827 | 828 | if (g_list_level) { 829 | text = text.replace(whole_list, function (wholeMatch, m1, m2) { 830 | var list = m1; 831 | var list_type = (m2.search(/[*+-]/g) > -1) ? "ul" : "ol"; 832 | 833 | var result = _ProcessListItems(list, list_type, isInsideParagraphlessListItem); 834 | 835 | // Trim any trailing whitespace, to put the closing `` 836 | // up on the preceding line, to get it past the current stupid 837 | // HTML block parser. This is a hack to work around the terrible 838 | // hack that is the HTML block parser. 839 | result = result.replace(/\s+$/, ""); 840 | result = "<" + list_type + ">" + result + "\n"; 841 | return result; 842 | }); 843 | } else { 844 | whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; 845 | text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) { 846 | var runup = m1; 847 | var list = m2; 848 | 849 | var list_type = (m3.search(/[*+-]/g) > -1) ? "ul" : "ol"; 850 | var result = _ProcessListItems(list, list_type); 851 | result = runup + "<" + list_type + ">\n" + result + "\n"; 852 | return result; 853 | }); 854 | } 855 | 856 | // attacklab: strip sentinel 857 | text = text.replace(/~0/, ""); 858 | 859 | return text; 860 | } 861 | 862 | var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" }; 863 | 864 | function _ProcessListItems(list_str, list_type, isInsideParagraphlessListItem) { 865 | // 866 | // Process the contents of a single ordered or unordered list, splitting it 867 | // into individual list items. 868 | // 869 | // list_type is either "ul" or "ol". 870 | 871 | // The $g_list_level global keeps track of when we're inside a list. 872 | // Each time we enter a list, we increment it; when we leave a list, 873 | // we decrement. If it's zero, we're not in a list anymore. 874 | // 875 | // We do this because when we're not inside a list, we want to treat 876 | // something like this: 877 | // 878 | // I recommend upgrading to version 879 | // 8. Oops, now this line is treated 880 | // as a sub-list. 881 | // 882 | // As a single paragraph, despite the fact that the second line starts 883 | // with a digit-period-space sequence. 884 | // 885 | // Whereas when we're inside a list (or sub-list), that line will be 886 | // treated as the start of a sub-list. What a kludge, huh? This is 887 | // an aspect of Markdown's syntax that's hard to parse perfectly 888 | // without resorting to mind-reading. Perhaps the solution is to 889 | // change the syntax rules such that sub-lists must start with a 890 | // starting cardinal number; e.g. "1." or "a.". 891 | 892 | g_list_level++; 893 | 894 | // trim trailing blank lines: 895 | list_str = list_str.replace(/\n{2,}$/, "\n"); 896 | 897 | // attacklab: add sentinel to emulate \z 898 | list_str += "~0"; 899 | 900 | // In the original attacklab showdown, list_type was not given to this function, and anything 901 | // that matched /[*+-]|\d+[.]/ would just create the next
  • , causing this mismatch: 902 | // 903 | // Markdown rendered by WMD rendered by MarkdownSharp 904 | // ------------------------------------------------------------------ 905 | // 1. first 1. first 1. first 906 | // 2. second 2. second 2. second 907 | // - third 3. third * third 908 | // 909 | // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx, 910 | // with {MARKER} being one of \d+[.] or [*+-], depending on list_type: 911 | 912 | /* 913 | list_str = list_str.replace(/ 914 | (^[ \t]*) // leading whitespace = $1 915 | ({MARKER}) [ \t]+ // list marker = $2 916 | ([^\r]+? // list item text = $3 917 | (\n+) 918 | ) 919 | (?= 920 | (~0 | \2 ({MARKER}) [ \t]+) 921 | ) 922 | /gm, function(){...}); 923 | */ 924 | 925 | var marker = _listItemMarkers[list_type]; 926 | var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm"); 927 | var last_item_had_a_double_newline = false; 928 | list_str = list_str.replace(re, 929 | function (wholeMatch, m1, m2, m3) { 930 | var item = m3; 931 | var leading_space = m1; 932 | var ends_with_double_newline = /\n\n$/.test(item); 933 | var contains_double_newline = ends_with_double_newline || item.search(/\n{2,}/) > -1; 934 | 935 | if (contains_double_newline || last_item_had_a_double_newline) { 936 | item = _RunBlockGamut(_Outdent(item), /* doNotUnhash = */true); 937 | } 938 | else { 939 | // Recursion for sub-lists: 940 | item = _DoLists(_Outdent(item), /* isInsideParagraphlessListItem= */ true); 941 | item = item.replace(/\n$/, ""); // chomp(item) 942 | if (!isInsideParagraphlessListItem) // only the outer-most item should run this, otherwise it's run multiple times for the inner ones 943 | item = _RunSpanGamut(item); 944 | } 945 | last_item_had_a_double_newline = ends_with_double_newline; 946 | return "
  • " + item + "
  • \n"; 947 | } 948 | ); 949 | 950 | // attacklab: strip sentinel 951 | list_str = list_str.replace(/~0/g, ""); 952 | 953 | g_list_level--; 954 | return list_str; 955 | } 956 | 957 | function _DoCodeBlocks(text) { 958 | // 959 | // Process Markdown `
    ` blocks.
     960 |             //  
     961 | 
     962 |             /*
     963 |             text = text.replace(/
     964 |                 (?:\n\n|^)
     965 |                 (                               // $1 = the code block -- one or more lines, starting with a space/tab
     966 |                     (?:
     967 |                         (?:[ ]{4}|\t)           // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
     968 |                         .*\n+
     969 |                     )+
     970 |                 )
     971 |                 (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width
     972 |             /g ,function(){...});
     973 |             */
     974 | 
     975 |             // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
     976 |             text += "~0";
     977 | 
     978 |             text = text.replace(/(?:\n\n|^\n?)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
     979 |                 function (wholeMatch, m1, m2) {
     980 |                     var codeblock = m1;
     981 |                     var nextChar = m2;
     982 | 
     983 |                     codeblock = _EncodeCode(_Outdent(codeblock));
     984 |                     codeblock = _Detab(codeblock);
     985 |                     codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
     986 |                     codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
     987 | 
     988 |                     codeblock = "
    " + codeblock + "\n
    "; 989 | 990 | return "\n\n" + codeblock + "\n\n" + nextChar; 991 | } 992 | ); 993 | 994 | // attacklab: strip sentinel 995 | text = text.replace(/~0/, ""); 996 | 997 | return text; 998 | } 999 | 1000 | function hashBlock(text) { 1001 | text = text.replace(/(^\n+|\n+$)/g, ""); 1002 | return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n"; 1003 | } 1004 | 1005 | function _DoCodeSpans(text) { 1006 | // 1007 | // * Backtick quotes are used for spans. 1008 | // 1009 | // * You can use multiple backticks as the delimiters if you want to 1010 | // include literal backticks in the code span. So, this input: 1011 | // 1012 | // Just type ``foo `bar` baz`` at the prompt. 1013 | // 1014 | // Will translate to: 1015 | // 1016 | //

    Just type foo `bar` baz at the prompt.

    1017 | // 1018 | // There's no arbitrary limit to the number of backticks you 1019 | // can use as delimters. If you need three consecutive backticks 1020 | // in your code, use four for delimiters, etc. 1021 | // 1022 | // * You can use spaces to get literal backticks at the edges: 1023 | // 1024 | // ... type `` `bar` `` ... 1025 | // 1026 | // Turns to: 1027 | // 1028 | // ... type `bar` ... 1029 | // 1030 | 1031 | /* 1032 | text = text.replace(/ 1033 | (^|[^\\]) // Character before opening ` can't be a backslash 1034 | (`+) // $2 = Opening run of ` 1035 | ( // $3 = The code block 1036 | [^\r]*? 1037 | [^`] // attacklab: work around lack of lookbehind 1038 | ) 1039 | \2 // Matching closer 1040 | (?!`) 1041 | /gm, function(){...}); 1042 | */ 1043 | 1044 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, 1045 | function (wholeMatch, m1, m2, m3, m4) { 1046 | var c = m3; 1047 | c = c.replace(/^([ \t]*)/g, ""); // leading whitespace 1048 | c = c.replace(/[ \t]*$/g, ""); // trailing whitespace 1049 | c = _EncodeCode(c); 1050 | c = c.replace(/:\/\//g, "~P"); // to prevent auto-linking. Not necessary in code *blocks*, but in code spans. Will be converted back after the auto-linker runs. 1051 | return m1 + "" + c + ""; 1052 | } 1053 | ); 1054 | 1055 | return text; 1056 | } 1057 | 1058 | function _EncodeCode(text) { 1059 | // 1060 | // Encode/escape certain characters inside Markdown code runs. 1061 | // The point is that in code, these characters are literals, 1062 | // and lose their special Markdown meanings. 1063 | // 1064 | // Encode all ampersands; HTML entities are not 1065 | // entities within a Markdown code span. 1066 | text = text.replace(/&/g, "&"); 1067 | 1068 | // Do the angle bracket song and dance: 1069 | text = text.replace(//g, ">"); 1071 | 1072 | // Now, escape characters that are magic in Markdown: 1073 | text = escapeCharacters(text, "\*_{}[]\\", false); 1074 | 1075 | // jj the line above breaks this: 1076 | //--- 1077 | 1078 | //* Item 1079 | 1080 | // 1. Subitem 1081 | 1082 | // special char: * 1083 | //--- 1084 | 1085 | return text; 1086 | } 1087 | 1088 | function _DoItalicsAndBold(text) { 1089 | 1090 | // must go first: 1091 | text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g, 1092 | "$1$3$4"); 1093 | 1094 | text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g, 1095 | "$1$3$4"); 1096 | 1097 | return text; 1098 | } 1099 | 1100 | function _DoBlockQuotes(text) { 1101 | 1102 | /* 1103 | text = text.replace(/ 1104 | ( // Wrap whole match in $1 1105 | ( 1106 | ^[ \t]*>[ \t]? // '>' at the start of a line 1107 | .+\n // rest of the first line 1108 | (.+\n)* // subsequent consecutive lines 1109 | \n* // blanks 1110 | )+ 1111 | ) 1112 | /gm, function(){...}); 1113 | */ 1114 | 1115 | text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, 1116 | function (wholeMatch, m1) { 1117 | var bq = m1; 1118 | 1119 | // attacklab: hack around Konqueror 3.5.4 bug: 1120 | // "----------bug".replace(/^-/g,"") == "bug" 1121 | 1122 | bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting 1123 | 1124 | // attacklab: clean up hack 1125 | bq = bq.replace(/~0/g, ""); 1126 | 1127 | bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines 1128 | bq = _RunBlockGamut(bq); // recurse 1129 | 1130 | bq = bq.replace(/(^|\n)/g, "$1 "); 1131 | // These leading spaces screw with
     content, so we need to fix that:
    1132 |                     bq = bq.replace(
    1133 |                             /(\s*
    [^\r]+?<\/pre>)/gm,
    1134 |                         function (wholeMatch, m1) {
    1135 |                             var pre = m1;
    1136 |                             // attacklab: hack around Konqueror 3.5.4 bug:
    1137 |                             pre = pre.replace(/^  /mg, "~0");
    1138 |                             pre = pre.replace(/~0/g, "");
    1139 |                             return pre;
    1140 |                         });
    1141 | 
    1142 |                     return hashBlock("
    \n" + bq + "\n
    "); 1143 | } 1144 | ); 1145 | return text; 1146 | } 1147 | 1148 | function _FormParagraphs(text, doNotUnhash) { 1149 | // 1150 | // Params: 1151 | // $text - string to process with html

    tags 1152 | // 1153 | 1154 | // Strip leading and trailing lines: 1155 | text = text.replace(/^\n+/g, ""); 1156 | text = text.replace(/\n+$/g, ""); 1157 | 1158 | var grafs = text.split(/\n{2,}/g); 1159 | var grafsOut = []; 1160 | 1161 | var markerRe = /~K(\d+)K/; 1162 | 1163 | // 1164 | // Wrap

    tags. 1165 | // 1166 | var end = grafs.length; 1167 | for (var i = 0; i < end; i++) { 1168 | var str = grafs[i]; 1169 | 1170 | // if this is an HTML marker, copy it 1171 | if (markerRe.test(str)) { 1172 | grafsOut.push(str); 1173 | } 1174 | else if (/\S/.test(str)) { 1175 | str = _RunSpanGamut(str); 1176 | str = str.replace(/^([ \t]*)/g, "

    "); 1177 | str += "

    " 1178 | grafsOut.push(str); 1179 | } 1180 | 1181 | } 1182 | // 1183 | // Unhashify HTML blocks 1184 | // 1185 | if (!doNotUnhash) { 1186 | end = grafsOut.length; 1187 | for (var i = 0; i < end; i++) { 1188 | var foundAny = true; 1189 | while (foundAny) { // we may need several runs, since the data may be nested 1190 | foundAny = false; 1191 | grafsOut[i] = grafsOut[i].replace(/~K(\d+)K/g, function (wholeMatch, id) { 1192 | foundAny = true; 1193 | return g_html_blocks[id]; 1194 | }); 1195 | } 1196 | } 1197 | } 1198 | return grafsOut.join("\n\n"); 1199 | } 1200 | 1201 | function _EncodeAmpsAndAngles(text) { 1202 | // Smart processing for ampersands and angle brackets that need to be encoded. 1203 | 1204 | // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 1205 | // http://bumppo.net/projects/amputator/ 1206 | text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&"); 1207 | 1208 | // Encode naked <'s 1209 | text = text.replace(/<(?![a-z\/?!]|~D)/gi, "<"); 1210 | 1211 | return text; 1212 | } 1213 | 1214 | function _EncodeBackslashEscapes(text) { 1215 | // 1216 | // Parameter: String. 1217 | // Returns: The string, with after processing the following backslash 1218 | // escape sequences. 1219 | // 1220 | 1221 | // attacklab: The polite way to do this is with the new 1222 | // escapeCharacters() function: 1223 | // 1224 | // text = escapeCharacters(text,"\\",true); 1225 | // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); 1226 | // 1227 | // ...but we're sidestepping its use of the (slow) RegExp constructor 1228 | // as an optimization for Firefox. This function gets called a LOT. 1229 | 1230 | text = text.replace(/\\(\\)/g, escapeCharacters_callback); 1231 | text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback); 1232 | return text; 1233 | } 1234 | 1235 | var charInsideUrl = "[-A-Z0-9+&@#/%?=~_|[\\]()!:,.;]", 1236 | charEndingUrl = "[-A-Z0-9+&@#/%=~_|[\\])]", 1237 | autoLinkRegex = new RegExp("(=\"|<)?\\b(https?|ftp)(://" + charInsideUrl + "*" + charEndingUrl + ")(?=$|\\W)", "gi"), 1238 | endCharRegex = new RegExp(charEndingUrl, "i"); 1239 | 1240 | function handleTrailingParens(wholeMatch, lookbehind, protocol, link) { 1241 | if (lookbehind) 1242 | return wholeMatch; 1243 | if (link.charAt(link.length - 1) !== ")") 1244 | return "<" + protocol + link + ">"; 1245 | var parens = link.match(/[()]/g); 1246 | var level = 0; 1247 | for (var i = 0; i < parens.length; i++) { 1248 | if (parens[i] === "(") { 1249 | if (level <= 0) 1250 | level = 1; 1251 | else 1252 | level++; 1253 | } 1254 | else { 1255 | level--; 1256 | } 1257 | } 1258 | var tail = ""; 1259 | if (level < 0) { 1260 | var re = new RegExp("\\){1," + (-level) + "}$"); 1261 | link = link.replace(re, function (trailingParens) { 1262 | tail = trailingParens; 1263 | return ""; 1264 | }); 1265 | } 1266 | if (tail) { 1267 | var lastChar = link.charAt(link.length - 1); 1268 | if (!endCharRegex.test(lastChar)) { 1269 | tail = lastChar + tail; 1270 | link = link.substr(0, link.length - 1); 1271 | } 1272 | } 1273 | return "<" + protocol + link + ">" + tail; 1274 | } 1275 | 1276 | function _DoAutoLinks(text) { 1277 | 1278 | // note that at this point, all other URL in the text are already hyperlinked as
    1279 | // *except* for the case 1280 | 1281 | // automatically add < and > around unadorned raw hyperlinks 1282 | // must be preceded by a non-word character (and not by =" or <) and followed by non-word/EOF character 1283 | // simulating the lookbehind in a consuming way is okay here, since a URL can neither and with a " nor 1284 | // with a <, so there is no risk of overlapping matches. 1285 | text = text.replace(autoLinkRegex, handleTrailingParens); 1286 | 1287 | // autolink anything like 1288 | 1289 | var replacer = function (wholematch, m1) { return "" + pluginHooks.plainLinkText(m1) + ""; } 1290 | text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer); 1291 | 1292 | // Email addresses: 1293 | /* 1294 | text = text.replace(/ 1295 | < 1296 | (?:mailto:)? 1297 | ( 1298 | [-.\w]+ 1299 | \@ 1300 | [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ 1301 | ) 1302 | > 1303 | /gi, _DoAutoLinks_callback()); 1304 | */ 1305 | 1306 | /* disabling email autolinking, since we don't do that on the server, either 1307 | text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, 1308 | function(wholeMatch,m1) { 1309 | return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); 1310 | } 1311 | ); 1312 | */ 1313 | return text; 1314 | } 1315 | 1316 | function _UnescapeSpecialChars(text) { 1317 | // 1318 | // Swap back in all the special characters we've hidden. 1319 | // 1320 | text = text.replace(/~E(\d+)E/g, 1321 | function (wholeMatch, m1) { 1322 | var charCodeToReplace = parseInt(m1); 1323 | return String.fromCharCode(charCodeToReplace); 1324 | } 1325 | ); 1326 | return text; 1327 | } 1328 | 1329 | function _Outdent(text) { 1330 | // 1331 | // Remove one level of line-leading tabs or spaces 1332 | // 1333 | 1334 | // attacklab: hack around Konqueror 3.5.4 bug: 1335 | // "----------bug".replace(/^-/g,"") == "bug" 1336 | 1337 | text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width 1338 | 1339 | // attacklab: clean up hack 1340 | text = text.replace(/~0/g, "") 1341 | 1342 | return text; 1343 | } 1344 | 1345 | function _Detab(text) { 1346 | if (!/\t/.test(text)) 1347 | return text; 1348 | 1349 | var spaces = [" ", " ", " ", " "], 1350 | skew = 0, 1351 | v; 1352 | 1353 | return text.replace(/[\n\t]/g, function (match, offset) { 1354 | if (match === "\n") { 1355 | skew = offset + 1; 1356 | return match; 1357 | } 1358 | v = (offset - skew) % 4; 1359 | skew = offset + 1; 1360 | return spaces[v]; 1361 | }); 1362 | } 1363 | 1364 | // 1365 | // attacklab: Utility functions 1366 | // 1367 | 1368 | var _problemUrlChars = /(?:["'*()[\]:]|~D)/g; 1369 | 1370 | // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems 1371 | function encodeProblemUrlChars(url) { 1372 | if (!url) 1373 | return ""; 1374 | 1375 | var len = url.length; 1376 | 1377 | return url.replace(_problemUrlChars, function (match, offset) { 1378 | if (match == "~D") // escape for dollar 1379 | return "%24"; 1380 | if (match == ":") { 1381 | if (offset == len - 1 || /[0-9\/]/.test(url.charAt(offset + 1))) 1382 | return ":" 1383 | } 1384 | return "%" + match.charCodeAt(0).toString(16); 1385 | }); 1386 | } 1387 | 1388 | 1389 | function escapeCharacters(text, charsToEscape, afterBackslash) { 1390 | // First we have to escape the escape characters so that 1391 | // we can build a character class out of them 1392 | var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])"; 1393 | 1394 | if (afterBackslash) { 1395 | regexString = "\\\\" + regexString; 1396 | } 1397 | 1398 | var regex = new RegExp(regexString, "g"); 1399 | text = text.replace(regex, escapeCharacters_callback); 1400 | 1401 | return text; 1402 | } 1403 | 1404 | 1405 | function escapeCharacters_callback(wholeMatch, m1) { 1406 | var charCodeToEscape = m1.charCodeAt(0); 1407 | return "~E" + charCodeToEscape + "E"; 1408 | } 1409 | 1410 | }; // end of the Markdown.Converter constructor 1411 | 1412 | })(); 1413 | -------------------------------------------------------------------------------- /chatOnline/SaeChannel.js: -------------------------------------------------------------------------------- 1 | // JSON2 by Douglas Crockford (minified). 2 | var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c1?this._listeners[a]=d.slice(0,e).concat(d.slice(e+1)):delete this._listeners[a];return}return},d.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d=3e3&&a<=4999},c.countRTO=function(a){var b;return a>100?b=3*a:b=a+200,b},c.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},c.flatUrl=function(a){return a.indexOf("?")===-1&&a.indexOf("#")===-1},c.amendUrl=function(b){var d=a.location;if(!b)throw new Error("Wrong url for SockJS");if(!c.flatUrl(b))throw new Error("Only basic urls are supported in SockJS");b.indexOf("//")===0&&(b=d.protocol+b),b.indexOf("/")===0&&(b=d.protocol+"//"+d.host+b),b=b.replace(/[/]+$/,"");var e=b.split("/");if(e[0]==="http:"&&/:80$/.test(e[2])||e[0]==="https:"&&/:443$/.test(e[2]))e[2]=e[2].replace(/:(80|443)$/,"");return b=e.join("/"),b},c.arrIndexOf=function(a,b){for(var c=0;c=0},c.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j={"\0":"\\u0000","\x01":"\\u0001","\x02":"\\u0002","\x03":"\\u0003","\x04":"\\u0004","\x05":"\\u0005","\x06":"\\u0006","\x07":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n","\x0b":"\\u000b","\f":"\\f","\r":"\\r","\x0e":"\\u000e","\x0f":"\\u000f","\x10":"\\u0010","\x11":"\\u0011","\x12":"\\u0012","\x13":"\\u0013","\x14":"\\u0014","\x15":"\\u0015","\x16":"\\u0016","\x17":"\\u0017","\x18":"\\u0018","\x19":"\\u0019","\x1a":"\\u001a","\x1b":"\\u001b","\x1c":"\\u001c","\x1d":"\\u001d","\x1e":"\\u001e","\x1f":"\\u001f",'"':'\\"',"\\":"\\\\","\x7f":"\\u007f","\x80":"\\u0080","\x81":"\\u0081","\x82":"\\u0082","\x83":"\\u0083","\x84":"\\u0084","\x85":"\\u0085","\x86":"\\u0086","\x87":"\\u0087","\x88":"\\u0088","\x89":"\\u0089","\x8a":"\\u008a","\x8b":"\\u008b","\x8c":"\\u008c","\x8d":"\\u008d","\x8e":"\\u008e","\x8f":"\\u008f","\x90":"\\u0090","\x91":"\\u0091","\x92":"\\u0092","\x93":"\\u0093","\x94":"\\u0094","\x95":"\\u0095","\x96":"\\u0096","\x97":"\\u0097","\x98":"\\u0098","\x99":"\\u0099","\x9a":"\\u009a","\x9b":"\\u009b","\x9c":"\\u009c","\x9d":"\\u009d","\x9e":"\\u009e","\x9f":"\\u009f","\xad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},k=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,l,m=JSON&&JSON.stringify||function(a){return i.lastIndex=0,i.test(a)&&(a=a.replace(i,function(a){return j[a]})),'"'+a+'"'},n=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};c.quote=function(a){var b=m(a);return k.lastIndex=0,k.test(b)?(l||(l=n(k)),b.replace(k,function(a){return l[a]})):b};var o=["websocket","xdr-streaming","xhr-streaming","iframe-eventsource","iframe-htmlfile","xdr-polling","xhr-polling","iframe-xhr-polling","jsonp-polling"];c.probeProtocols=function(){var a={};for(var b=0;b0&&h(a)};return c.websocket!==!1&&h(["websocket"]),d["xhr-streaming"]&&!c.null_origin?e.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-streaming"):h(["iframe-eventsource","iframe-htmlfile"]),d["xhr-polling"]&&!c.null_origin?e.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-polling"):h(["iframe-xhr-polling","jsonp-polling"]),e};var p="_sockjs_global";c.createHook=function(){var a="a"+c.random_string(8);if(!(p in b)){var d={};b[p]=function(a){return a in d||(d[a]={id:a,del:function(){delete d[a]}}),d[a]}}return b[p](a)},c.attachMessage=function(a){c.attachEvent("message",a)},c.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},c.detachMessage=function(a){c.detachEvent("message",a)},c.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var q={},r=!1,s=function(){for(var a in q)q[a](),delete q[a]},t=function(){if(r)return;r=!0,s()};c.attachEvent("unload",t),c.unload_add=function(a){var b=c.random_string(8);return q[b]=a,r&&c.delay(s),b},c.unload_del=function(a){a in q&&delete q[a]},c.createIframe=function(b,d){var e=a.createElement("iframe"),f,g,h=function(){clearTimeout(f);try{e.onload=null}catch(a){}e.onerror=null},i=function(){e&&(h(),setTimeout(function(){e&&e.parentNode.removeChild(e),e=null},0),c.unload_del(g))},j=function(a){e&&(i(),d(a))},k=function(a,b){try{e&&e.contentWindow&&e.contentWindow.postMessage(a,b)}catch(c){}};return e.src=b,e.style.display="none",e.style.position="absolute",e.onerror=function(){j("onerror")},e.onload=function(){clearTimeout(f),f=setTimeout(function(){j("onload timeout")},2e3)},a.body.appendChild(e),f=setTimeout(function(){j("timeout")},15e3),g=c.unload_add(i),{post:k,cleanup:i,loaded:h}},c.createHtmlfile=function(a,d){var e=new ActiveXObject("htmlfile"),f,g,i,j=function(){clearTimeout(f)},k=function(){e&&(j(),c.unload_del(g),i.parentNode.removeChild(i),i=e=null,CollectGarbage())},l=function(a){e&&(k(),d(a))},m=function(a,b){try{i&&i.contentWindow&&i.contentWindow.postMessage(a,b)}catch(c){}};e.open(),e.write(' 72 | 73 | -------------------------------------------------------------------------------- /options.js: -------------------------------------------------------------------------------- 1 | window.onload = function() { 2 | var form = document.forms[0]; 3 | chrome.storage.sync.get("answerSearch", function(d) { 4 | if(d.hasOwnProperty('answerSearch')) 5 | d.answerSearch == form.answerSearch[0].value ? form.answerSearch[0].checked = true : form.answerSearch[1].checked = true; 6 | }) 7 | chrome.storage.sync.get("reputationChart", function(d) { 8 | if(d.hasOwnProperty("reputationChart")) 9 | d.reputationChart == form.reputationChart[0].value ? form.reputationChart[0].checked = true : form.reputationChart[1].checked = true; 10 | }) 11 | chrome.storage.sync.get("cmtEnhanced", function(d) { 12 | if(d.hasOwnProperty("cmtEnhanced")) 13 | d.cmtEnhanced == form.cmtEnhanced[0].value ? form.cmtEnhanced[0].checked = true : form.cmtEnhanced[1].checked = true; 14 | }) 15 | chrome.storage.sync.get("chatOnline", function(d) { 16 | if(d.hasOwnProperty("chatOnline")) 17 | d.chatOnline == form.chatOnline[0].value ? form.chatOnline[0].checked = true : form.chatOnline[1].checked = true; 18 | }) 19 | chrome.storage.sync.get("msgbg", function(d) { 20 | if(d.hasOwnProperty('msgbg')) form.msgbg.value = d.msgbg; 21 | }) 22 | chrome.storage.sync.get("sendkey", function(d) { 23 | if(d.hasOwnProperty('sendkey')) form.sendkey.value = d.sendkey; 24 | }) 25 | chrome.storage.sync.get("aero", function(d) { 26 | if(d.hasOwnProperty("aero")) 27 | d.aero == form.aero[0].value ? form.aero[0].checked = true : form.aero[1].checked = true; 28 | }) 29 | chrome.storage.sync.get("opacity", function(d) { 30 | if(d.hasOwnProperty("opacity")) { 31 | form.opacity.value = d.opacity * 100; 32 | form.opacity.nextElementSibling.innerText = d.opacity; 33 | } 34 | }) 35 | chrome.storage.sync.get("customCSS", function(d) { 36 | if(d.hasOwnProperty("customCSS")) { 37 | form['custom-css'].value = d.customCSS; 38 | } 39 | }) 40 | chrome.storage.sync.get("customJS", function(d) { 41 | if(d.hasOwnProperty("customJS")) { 42 | form['custom-js'].value = d.customJS; 43 | } 44 | }) 45 | form.onsubmit = function() {return false;} 46 | var inputs = document.querySelectorAll('.op'); 47 | for(var i=0,l=inputs.length; i 2 | 3 | 4 | 5 | SF notifier 6 | 7 | 8 | 9 |
      10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 这款插件把之前我写的一些插件融合在一块而创造出来的。名字来源于“豆瓣精灵”这个插件。目前插件的主要功能有如下几个: 2 | 3 | #### 0.0.8版 4 | - 增加用户自定义 CSS/JS 选项 5 | - 去除隐藏用户通知数字功能(用户可自行在 CSS 中自定义) 6 | - 修复上个版本的一些 BUG 7 | 8 | #### 0.0.7版 9 | - 给评论加上了 Markdown 在线预览功能并增加颜文字输入的支持 10 | 11 | #### 0.0.6版 12 | - 增加单用户答案根据评分排序功能 13 | 14 | #### 0.0.5版 15 | - 由于官方新版已加入所以去除图片粘贴上传功能 16 | 17 | #### 0.0.4版 18 | - 增加了首页挂件数字提醒隐藏的功能 19 | - 增加了个人主页声望3D直方图显示功能 20 | 21 | #### 0.0.3版 22 | - 适配新版 23 | - 由于官方对API进行了限制暂时去除了站内消息浏览器提醒功能 24 | 25 | #### 0.0.1版 26 | - 【站内消息浏览器提醒扩展】 27 | 修复了之前 @airyland 版本点击消息无法去除提醒的问题。 28 | - 【用户已回答答案搜索】 29 | 后台选项中可单独开启/关闭该功能,是 [SF Answer Search][2] 的融合版。 30 | - 【在线聊天室】 31 | 后台选项中可单独开启/关闭该功能,是 [SF ChatOnline][3] 的融合版。 32 | - 【回答区图片粘贴上传】 33 | 后台选项中可单独开启/关闭该功能。 34 | 35 | 36 | [1]: https://github.com/airyland/sf-notifier 37 | [2]: http://blog.segmentfault.com/openwrt/1190000000440113 38 | [3]: http://blog.segmentfault.com/openwrt/1190000000458838 39 | -------------------------------------------------------------------------------- /reputation-chart-3d/iso.js: -------------------------------------------------------------------------------- 1 | function sfISO() { 2 | var SIZE = 15, MARGIN = 2, MAX_HEIGHT = 77; 3 | var COLORS = { 4 | gray: {r:243, g:243, b:243}, 5 | green: {r:0, g:154, b:97}, 6 | red: {r:175, g:57, b:51}, 7 | bg: {r:255, g:255, b:255} 8 | } 9 | this.rep = document.querySelector(".rep-rects"); 10 | this.stats = getStats(this.rep); 11 | function colorTransfer(c, o) { 12 | o = Number(o).toFixed(2)/1 || 1; 13 | return parseInt(['r', 'g', 'b'].map(function(t){ 14 | return parseInt((1-o)*COLORS.bg[t] + o*COLORS[c][t]).toString(16) 15 | }).join(''), 16); 16 | } 17 | function getStats(rep) { 18 | var columns = Math.floor((rep.clientWidth-SIZE)/(SIZE+MARGIN)); 19 | return [].slice.call(rep.querySelectorAll("li")).map(function(r,i){ 20 | var color, value, date; 21 | if(r.classList.contains('bg-gray')) { 22 | color = colorTransfer('gray'); 23 | value = 0; 24 | date = (r.getAttribute('title')||r.getAttribute('data-original-title')).match(/没有获得声望
      (\d{4}-\d{2}-\d{2})/)[1]; 25 | } else { 26 | color = colorTransfer([].slice.call(r.classList).filter(function(c){return c.match(/bg\-([green|red|gray]+)/)})[0].substr(3), r.style.opacity); 27 | var info = (r.getAttribute('title')||r.getAttribute('data-original-title')).match(/[+-](\d+?) 声望
      (\d{4}-\d{2}-\d{2})/); 28 | value = info[1]/1; 29 | date = info[2]; 30 | } 31 | return { 32 | x: i%columns, 33 | y: Math.floor(i/columns), 34 | color: color, 35 | value: value, 36 | date: date 37 | } 38 | }) 39 | } 40 | 41 | var MAX_REP = this.stats.reduce(function(a,b) {return a.value>b.value?a:b}); 42 | sfISO.prototype.initUI = function() { 43 | var html = '
      {{title}}
      过去72天
      {{last_72_days_reputation}}
      历史最高
      {{highest_day_reputation}}
      显示普通图表▾
      过去7天
      {{last_7_days_reputation}}
      今日成就
      {{today_reputation}}
      '; 44 | var title = '声望3D直方图'; 45 | var lastTotalReputation = this.stats.reduce(function(a, b){return a+b.value}, 0); 46 | var lastTotalDay = this.stats[0].date+this.stats[this.stats.length-1].date; 47 | var lastSevenDay = this.stats.slice(-7); 48 | var lastSevenDayReputation = lastSevenDay.reduce(function(a, b){return a+b.value}, 0); 49 | var lastSevenDayDate = lastSevenDay[0].date+lastSevenDay[lastSevenDay.length-1].date; 50 | var data = { 51 | title: title, 52 | last_72_days_reputation: lastTotalReputation, 53 | last_72_days_date: lastTotalDay, 54 | last_7_days_reputation: lastSevenDayReputation, 55 | last_7_days_date: lastSevenDayDate, 56 | highest_day_reputation: MAX_REP.value, 57 | highest_day_date: MAX_REP.date, 58 | today_reputation: this.stats[this.stats.length-1].value, 59 | today_date: this.stats[this.stats.length-1].date 60 | } 61 | html = html.replace(/{{(.+?)}}/g, function(){ 62 | return data.hasOwnProperty(arguments[1]) ? data[arguments[1]] : arguments[1]; 63 | }) 64 | this.rep.classList.toggle('chart-display'); 65 | this.rep.outerHTML = html+this.rep.outerHTML; 66 | document.querySelector(".normal-chart-toggle").addEventListener("click", function(e){ 67 | e.preventDefault(); 68 | document.querySelector(".rep-rects").classList.toggle("chart-display"); 69 | var show = "显示普通图表 ▾", hidden = "隐藏普通图表 ▴"; 70 | this.innerHTML = this.innerHTML==show?hidden:show; 71 | return false; 72 | }) 73 | return document.querySelector("#reputation-chart-3d"); 74 | } 75 | sfISO.prototype.render = function() { 76 | var canvas = document.querySelector("reputation-chart-3d") || this.initUI(); 77 | var pixelView = new obelisk.PixelView(canvas, new obelisk.Point(70, 80)); 78 | this.stats.forEach(function(item) { 79 | var color = color = new obelisk.CubeColor().getByHorizontalColor(item.color), 80 | height = parseInt(MAX_HEIGHT * item.value / MAX_REP.value)+3; 81 | var p3d = new obelisk.Point3D(12*item.x, 12*item.y, 0); 82 | var dimension = new obelisk.CubeDimension(10, 10, height); 83 | var cube = new obelisk.Cube(dimension, color, false); 84 | return pixelView.renderObject(cube, p3d); 85 | }); 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /reputation-chart-3d/main.js: -------------------------------------------------------------------------------- 1 | chrome.storage.sync.get('reputationChart', function(d) { 2 | if(+d.reputationChart) return false; 3 | if(location.pathname.substr(0, 3) === "/u/") { 4 | var chart = new sfISO(); 5 | chart.render(); 6 | } 7 | }) -------------------------------------------------------------------------------- /reputation-chart-3d/obelisk.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * obelisk.js 1.0.2 3 | * https://github.com/nosir/obelisk.js 4 | * MIT licensed 5 | * 6 | * Copyright (C) 2012 Max Huang https://github.com/nosir/ 7 | */ 8 | !function(a){"use strict";var b={};b.version="1.0.2",b.author="max huang",a.obelisk=b}(window),function(a){"use strict";var b,c;b=function(){this.initialize()},c=b.prototype,c.inner=null,c.border=null,c.borderHighlight=null,c.left=null,c.right=null,c.horizontal=null,c.leftSlope=null,c.rightSlope=null,c.initialize=function(){return this},c.toString=function(){return"[AbstractColor]"},a.AbstractColor=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b,c,d,e){this.initialize(a,b,c,d,e)},c=b.prototype=new a.AbstractColor,c.BRIGHTNESS_GAIN=-20,c.initialize=function(b,c,d,e,f){return this.border=a.ColorGeom.get32(void 0===b?8882055:b),this.borderHighlight=a.ColorGeom.get32(void 0===c?16777215:c),this.left=a.ColorGeom.get32(void 0===d?13225936:d),this.right=a.ColorGeom.get32(void 0===e?14935011:e),this.horizontal=a.ColorGeom.get32(void 0===f?15658992:f),this},c.getByHorizontalColor=function(c){return new b(a.ColorGeom.applyBrightness(c,4*this.BRIGHTNESS_GAIN),a.ColorGeom.applyBrightness(c,0,!0),a.ColorGeom.applyBrightness(c,2*this.BRIGHTNESS_GAIN),a.ColorGeom.applyBrightness(c,this.BRIGHTNESS_GAIN),c)},c.toString=function(){return"[CubeColor]"},a.CubeColor=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b,c,d){this.initialize(a,b,c,d)},c=b.prototype=new a.AbstractColor,c.BRIGHTNESS_GAIN=-20,c.initialize=function(b,c,d,e){return this.border=a.ColorGeom.get32(void 0===b?9737880:b),this.borderHighlight=a.ColorGeom.get32(void 0===c?16777215:c),this.left=a.ColorGeom.get32(void 0===d?15132905:d),this.right=a.ColorGeom.get32(void 0===e?15658992:e),this},c.getByRightColor=function(c){return new b(a.ColorGeom.applyBrightness(c,4*this.BRIGHTNESS_GAIN),a.ColorGeom.applyBrightness(c,0,!0),a.ColorGeom.applyBrightness(c,this.BRIGHTNESS_GAIN),c)},c.toString=function(){return"[PyramidColor]"},a.PyramidColor=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b){this.initialize(a,b)},c=b.prototype=new a.AbstractColor,c.BRIGHTNESS_GAIN=-20,c.initialize=function(b,c){return this.border=a.ColorGeom.get32(void 0===b?8882055:b),this.inner=a.ColorGeom.get32(void 0===c?15658734:c),this},c.getByInnerColor=function(b){return new a.SideColor(a.ColorGeom.applyBrightness(b,4*this.BRIGHTNESS_GAIN),b)},c.toString=function(){return"[SideColor]"},a.SideColor=b}(obelisk),function(a){"use strict";var b,c;b=function(){this.initialize()},c=b.prototype,c.xAxis=null,c.yAxis=null,c.zAxis=null,c.tall=!1,c.initialize=function(){return this},c.toString=function(){return"[AbstractDimension]"},a.AbstractDimension=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b){this.initialize(a,b)},c=b.prototype=new a.AbstractDimension,c.initialize=function(a,b){if(this.xAxis=a||30,this.yAxis=b||30,1===this.xAxis%2||1===this.yAxis%2)throw new Error("x,yAxis must be even number");if(this.xAxis<=4||this.yAxis<=4)throw new Error("dimension is too small");return this},c.toString=function(){return"[BrickDimension]"},a.BrickDimension=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b,c){this.initialize(a,b,c)},c=b.prototype=new a.AbstractDimension,c.initialize=function(a,b,c){if(this.xAxis=a||30,this.yAxis=b||30,this.zAxis=c||30,1===this.xAxis%2||1===this.yAxis%2)throw new Error("x,yAxis must be even number");if(this.xAxis<=4||this.yAxis<=4||this.zAxis<=2)throw new Error("dimension is too small");return this},c.toString=function(){return"[CubeDimension]"},a.CubeDimension=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b){this.initialize(a,b)},c=b.prototype=new a.AbstractDimension,c.initialize=function(a,b){if(this.xAxis=a||30,this.yAxis=a||30,this.tall=b||!1,1===this.xAxis%2)throw new Error("axis must be even number");if(this.xAxis<=4)throw new Error("dimension is too small");return this},c.toString=function(){return"[PyramidDimension]"},a.PyramidDimension=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b){this.initialize(a,b)},c=b.prototype=new a.AbstractDimension,c.initialize=function(a,b){if(this.xAxis=a||30,this.zAxis=b||30,1===this.xAxis%2)throw new Error("xAxis must be even number");if(this.xAxis<=4||this.zAxis<=2)throw new Error("dimension is too small");return this},c.toString=function(){return"[SideXDimension]"},a.SideXDimension=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b){this.initialize(a,b)},c=b.prototype=new a.AbstractDimension,c.initialize=function(a,b){if(this.yAxis=a||30,this.zAxis=b||30,1===this.yAxis%2)throw new Error("yAxis must be even number");if(this.yAxis<=4||this.zAxis<=2)throw new Error("dimension is too small");return this},c.toString=function(){return"[SideYDimension]"},a.SideYDimension=b}(obelisk),function(a){"use strict";var b,c;b=function(a,b,c){this.initialize(a,b,c)},c=b.prototype,c.imageData=null,c.canvas=null,c.context=null,c.initialize=function(b,c,d){if(void 0===b||void 0===c)throw new Error("BitmapData width or height is missing");return this.canvas=d?a.CanvasManager.getDefaultCanvas():a.CanvasManager.getNewCanvas(),this.canvas.setAttribute("width",b),this.canvas.setAttribute("height",c),this.context=this.canvas.getContext("2d"),this.context.clearRect(0,0,this.canvas.width,this.canvas.height),this.imageData=this.context.createImageData(b,c),this},c.setPixel=function(a,b,c){var d=4*(b*this.imageData.width+a);this.setPixelByIndex(d,c)},c.setPixelByIndex=function(a,b){var c=this.imageData.data;c[a]=255&b>>>16,c[a+1]=255&b>>>8,c[a+2]=255&b>>>0,c[a+3]=255&b>>>24},c.checkPixelAvailable=function(a,b){var c=4*(b*this.imageData.width+a);return 0===this.imageData.data[c+3]},c.floodFill=function(a,b,c){if(0!==(255&c>>>24)){var d,e,f,g,h,i=a,j=b,k=[],l=[],m=[],n=this.imageData.width,o=this.imageData.height;if(!(0>i||0>j||i>=n||j>=o)){if(!this.checkPixelAvailable(i,j))throw new Error("Start point for flood fill is already filled");for(d=i;d>=0;d-=1){for(e=j;e>=0;e-=1)if(this.checkPixelAvailable(d,e))k.push(4*(e*n+d)),l.push(e);else if(e!==j||!this.checkPixelAvailable(d+1,e-1))break;for(e=j;o>e;e+=1)if(this.checkPixelAvailable(d,e))k.push(4*(e*n+d)),l.push(e);else if(e!==j||!this.checkPixelAvailable(d+1,e+1))break;for(d===i&&(m=l.concat()),f=!1,g=0;gd;d+=1){for(e=j;e>=0;e-=1)if(this.checkPixelAvailable(d,e))k.push(4*(e*n+d)),l.push(e);else if(e!==j||!this.checkPixelAvailable(d-1,e-1))break;for(e=j;o>e;e+=1)if(this.checkPixelAvailable(d,e))k.push(4*(e*n+d)),l.push(e);else if(e!==j||!this.checkPixelAvailable(d-1,e+1))break;for(d===i&&(m=l.concat()),f=!1,g=0;ga?a+4278190080:a},c.applyBrightness=function(a,b,c){var d,e,f,g,h,i,j;return d=255&a>>>24,e=255&a>>>16,f=255&a>>>8,g=255&a,h=(313524*e>>20)+(615514*f>>20)+(119538*g>>20),j=-(155189*e>>20)-(303038*f>>20)+(458227*g>>20),i=(644874*e>>20)-(540016*f>>20)-(104857*g>>20),c?h=60+Math.pow(h,1.2):h+=b,e=h+(1195376*i>>20),f=h-(408944*j>>20)-(608174*i>>20),g=h+(2128609*j>>20),e=Math.max(0,Math.min(e,255)),f=Math.max(0,Math.min(f,255)),g=Math.max(0,Math.min(g,255)),d<<24|e<<16|f<<8|g},c.toString=function(){return"[ColorGeom]"},a.ColorGeom=b}(obelisk),function(a){"use strict";var b,c;b=function(){throw new Error("ColorGeom is a static Class, cannot be instanced.")},c=b,c.GRASS_GREEN=13434624,c.YELLOW=16776960,c.WINE_RED=16711833,c.PINK=16743615,c.PURPLE=13369599,c.BLUE=52479,c.GRAY=15658734,c.BLACK=6710886,c.FINE_COLORS=[c.GRASS_GREEN,c.YELLOW,c.WINE_RED,c.PINK,c.PURPLE,c.BLUE,c.GRAY,c.BLACK],c.getRandomComfortableColor=function(){return c.FINE_COLORS[Math.floor(Math.random()*c.FINE_COLORS.length)]},c.toString=function(){return"[ColorPattern]"},a.ColorPattern=b}(obelisk); --------------------------------------------------------------------------------