// ==UserScript== // @name Blogger Hatena Syntax // @namespace http://d.hatena.ne.jp/edvakf/ // @description Hatena syntax and code highlight for blogger // @include http://www.blogger.com/post-create.g* // @include http://www.blogger.com/post-edit.g* // ==/UserScript== // // Copyright (C) 2010 edvakf // // The JavaScript code in this page is free software: you can // redistribute it and/or modify it under the terms of the GNU // General Public License (GNU GPL) as published by the Free Software // Foundation, either version 3 of the License, or (at your option) // any later version. The code is distributed WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. // // As additional permission under GNU GPL version 3 section 7, you // may distribute non-source (e.g., minimized or compacted) forms of // that code without the copy of the GNU GPL normally required by // section 4, provided you include this license notice and a URL // through which recipients can access the Corresponding Source. // // This script makes use of: // text-hatena.js // http://tech.nitoyon.com/javascript/application/texthatena/download.html // SHJS // http://shjs.sourceforge.net/ // // Syntax-highlightable languages: // C (c), C++ (cpp), CSS (css), diff (diff), HTML (html), Java (java), // JavaScript (javascript), Perl (perl), PHP (php), Python (python), // Ruby (ruby), Scala (scala), Shell Script (sh), SQL (sql), XML (xml) // main part setTimeout(function() { if (window.top !== window.self) return; var box = document.getElementById("body"); var ta = document.getElementById("textarea"); var editor = document.createElement("textarea"); editor.setAttribute('style', [ 'display:block;', 'float:left;', 'width:45%;', 'padding:0 0 0 5px;', 'minimum-height:300px;' ].join('')); var preview = document.createElement("div"); preview.setAttribute('style', [ 'float:right;', 'width:50%;', 'border:solid black 1px;', 'padding:5px;', 'minimum-height:300px;' ].join('')); box.appendChild(editor); box.appendChild(preview); var sectionanchor = '\u25a0'; var hatena = new Hatena({sectionanchor: sectionanchor}); var m; if (m = ta.value.match(/\n/)) { setTimeout(function() { // to let variables below be defined editor.value = m[1].replace( /\{\{(\d+) hyphens\}\}/g, function($0,$1) {return String.times('-', +$1);} ); seePreview(); resize(editor); }, 10); } var timer; editor.addEventListener('input', function() { resize(editor); timer = clearTimeout(timer); timer = setTimeout(seePreview, 500); }, false); function seePreview() { hatena.parse(editor.value); preview.innerHTML = hatena.html(); Array.prototype.forEach.call( preview.getElementsByTagName("pre"), function(pre) { if ((m = pre.className.match(/sh_(\S*)/)) && sh_languages[m[1]]) { sh_highlightElement(pre, sh_languages[m[1]]); } } ); replaceTitles(); setTextArea(); fetchTitles(); } function setTextArea() { var clone = preview.cloneNode(true); var h3 = clone.firstChild.firstElementChild; if (h3 && /h3/i.test(h3.nodeName)) { document.getElementById('f-title').value = h3.textContent.replace(sectionanchor, ''); h3.parentNode.removeChild(h3); } ta.value = clone.innerHTML + "\n"; } function resize(el) { var es = el.style; var sh = el.scrollHeight; var paddingTopAndBottom = 0 + 0; if (this.opera) { // http://orera.g.hatena.ne.jp/misttrap/20090825/p1 es.minHeight = sh + 'px'; var diff = sh + paddingTopAndBottom - el.clientHeight if (diff > 0) es.minHeight = sh + diff + 'px'; } else { // http://d.hatena.ne.jp/javascripter/20090311/1236779096 es.height = '0px'; es.height = sh - paddingTopAndBottom + 'px'; } } var script = document.createElement('script'); script.textContent = [ // run in page's context. works for Greasemonkey & Chrome ,"function fireMyEvent(o) {" ,"if (o.error) return;" ,"var url = o.query.diagnostics.url;" ,"url = url.content || url[url.length - 1].content;" ,"var title = o.query.results;" ,"if (window.opera) {" ,"var ev = document.createEvent('Event');" ,"ev.initEvent('TitleReady', true, false);" ,"ev.url = url;" ,"ev.title = title;" ,"} else {" ,"var ev = document.createEvent('MessageEvent');" ,"ev.initMessageEvent('TitleReady', true, false," // type, canBubble, cancelable ,"JSON.stringify({url: url, title: title})," // data ,"location.protocol + '//' + location.host," // origin ,"''," // lastEventId ,"window" // source ,");" ,"}" ,"document.dispatchEvent(ev);" ,"}" ].join('\n'); document.body.appendChild(script); var URL2TITLE = {}; document.addEventListener('TitleReady', function(ev) { var data = ev.data ? JSON.parse(ev.data) : ev; URL2TITLE[data.url] = data.title || null; replaceTitles(); setTextArea(); }, false); function replaceTitles() { Array.prototype.forEach.call( preview.getElementsByTagName('a'), function(a) { if (a.textContent === '{{title}}') { var title = URL2TITLE[a.href]; if (title === void 0) { // title must be fetched } else if (title === null) { a.textContent = a.href; } else if (title === '') { // JSONP not loaded yet } else { a.textContent = title; var grandpa = a.parentNode.parentNode; if (/blockquote/i.test(grandpa.tagName) && grandpa.getAttribute('title') === '{{title}}') grandpa.setAttribute('title', title); } } } ); } function fetchTitles() { Array.prototype.forEach.call( preview.getElementsByTagName('a'), function(a) { var url = a.href; if (a.textContent === '{{title}}' && URL2TITLE[url] === void 0) { URL2TITLE[url] = ''; var api = "http://query.yahooapis.com/v1/public/yql" + "?format=json&callback=fireMyEvent&q=select%20*%20from%20html%20where%20url%3d'" + encodeURIComponent(url) + "'%20and%20xpath%3d'%2f%2ftitle%2ftext()'"; var script = document.createElement('script'); script.src = api; document.body.appendChild(script); } } ); } }, 10); /* * http://tech.nitoyon.com/javascript/application/texthatena/text-hatena0-2.js * modified by edvakf */ // from prototype.js Object.extend = function(destination, source) { for (property in source) { destination[property] = source[property]; } return destination; } String.times = function(str, time){ var s = ""; for(var i = 0; i < time; i++)s += str; return s; } String._escapeHTML = function(s){ s = s.replace(/\&/g, "&"); s = s.replace(//g, ">"); s = s.replace(/"/g, """); s = s.replace(/\'/g, "'"); s = s.replace(/\\/g, "\"); return s; } String._unescapeHTML = function(s){ s = s.replace(/&/g, "&"); s = s.replace(/</g, "<"); s = s.replace(/>/g, ">"); s = s.replace(/"/g, "\""); return s; } // Hatena::Hatena_HTMLFilter Hatena_HTMLFilter = function(args){ this.self = { context : args["context"], html : '' }; this.init(); } Hatena_HTMLFilter.prototype = { init :function(){ // HTML::Parser を利用すべきなんだけど JavaScript ではなんとも... }, parse : function(html){ var c = this.self.context; this.self.html = c.self.texthandler(html, c); }, html : function(){ return this.self.html; } } // Hatena Hatena = function(args){ if(args == null) args = {}; this.self = { html : '', baseuri : args["baseuri"], permalink : args["permalink"] || "", ilevel : args["ilevel"] || 0, invalidnode : args["invalidnode"] || [], sectionanchor : args["sectionanchor"] || 'o-', texthandler : args["texthandler"] || function(text, c){ // footnote var p = c.self.permalink; var html = ""; var foot = text.split("(("); for(var i = 0; i < foot.length; i++){ if(i == 0){ html += foot[i]; continue; } var s = foot[i].split("))", 2); if(s.length != 2){ html += "((" + foot[i]; continue; } var pre = foot[i - i]; var note = s[0]; var post = foot[i].substr(s[0].length + 2); if(pre.match(/\)$/) && post.match(/^\(/)){ html += "((" + post; } else { var notes = c.footnotes(note); var num = notes.length; note = note.replace(/<.*?>/g, ""); note = note.replace(/&/g, "&"); html += '*' + num + '' + post; } } // auto link (added by edvakf) html = html.replace(/\[(https?:\/\/[^\]\s]+?)(:title(=[^\]\n]*)?)?\]/g, function($0,$1,$2,$3) { return '' + ( $3 ? String._escapeHTML($3.slice(1)) : // title given by user $2 ? '{{title}}' : // title will be fetched (via YQL) $1 // use URL instead of title ) + ''; }); return html; } }; } Hatena.prototype = { parse : function(text){ this.self.context = new Hatena_Context({ text : text || "", baseuri : this.self.baseuri, permalink : this.self.permalink, invalidnode : this.self.invalidnode, sectionanchor : this.self.sectionanchor, texthandler : this.self.texthandler }); var c = this.self.context; var node = new Hatena_BodyNode(); node._new({ context : c, ilevel : this.self.ilevel }); node.parse(); var parser = new Hatena_HTMLFilter({ context : c }); parser.parse(c.html()); this.self.html = parser.html(); if (this.self.context.footnotes().length != 0) { var node = new Hatena_FootnoteNode(); node._new({ context : this.self.context, ilevel : this.self.ilevel }); node.parse(); this.self.html += "\n"; this.self.html += node.self.html; } }, html : function(){ return this.self.html; } } // Hatena::Context Hatena_Context = function(args){ this.self = { text : args["text"], baseuri : args["baseuri"], permalink : args["permalink"], invalidnode : args["invalidnode"], sectionanchor : args["sectionanchor"], texthandler : args["texthandler"], _htmllines : [], footnotes : Array(), sectioncount : 0, syntaxrefs : [], noparagraph : 0 }; this.init(); } Hatena_Context.prototype = { init : function() { this.self.text = this.self.text.replace(/\r/g, ""); this.self.lines = this.self.text.split('\n'); this.self.index = -1; }, hasnext : function() { return (this.self.lines != null && this.self.lines.length - 1 > this.self.index); }, nextline : function() { return this.self.lines[this.self.index + 1]; }, shiftline : function() { return this.self.lines[++this.self.index]; }, currentline : function() { return this.self.lines[this.self.index]; }, html : function() { return this.self._htmllines.join ("\n"); }, htmllines : function(line) { if(line != null) this.self._htmllines.push(line); return this.self._htmllines; }, lasthtmlline : function() {return this.self._htmllines[this.self._htmllines.length - 1]; }, footnotes : function(line) { if(line != null) this.self.footnotes.push(line); return this.self.footnotes; }, syntaxrefs : function(line) { if(line != null) this.self.syntaxrefs.push(line); return this.self.syntaxrefs; }, syntaxpattern : function(pattern) { if(pattern != null) this.self.syntaxpattern = pattern; return this.self.syntaxpattern; }, noparagraph : function(noparagraph) { if(noparagraph != null) this.self.noparagraph = noparagraph; return this.self.noparagraph; }, incrementsection : function() { return this.self.sectioncount++; } } // Hatena::Node Hatena_Node = function(){} Hatena_Node.prototype = { html : "", pattern : "", _new : function(args){ if(args == null) args = Array(); this.self = { context : args["context"], ilevel : args["ilevel"], html : '' }; this.init(); }, init : function(){ this.self.pattern = ''; }, parse : function(){ alert('die'); }, context : function(v){ this.self.context = v; } }; // Hatena::BodyNode Hatena_BodyNode = function(){}; Hatena_BodyNode.prototype = Object.extend(new Hatena_Node(), { parse : function(){ var c = this.self.context; while (this.self.context.hasnext()) { var node = new Hatena_SectionNode(); node._new({ context : c, ilevel : this.self.ilevel }); node.parse(); } } }) // Hatena::BrNode Hatena_BrNode = function(){}; Hatena_BrNode.prototype = Object.extend(new Hatena_Node(), { parse : function(){ var c = this.self.context; var l = c.shiftline(); if(l.length != 0) return; var t = String.times("\t", this.self.ilevel); if (c.lasthtmlline() == t + "
" || c.lasthtmlline() == t) { c.htmllines(t + "
"); } else { c.htmllines(t); } } }) // Hatena::CDataNode Hatena_CDataNode = function(){}; Hatena_CDataNode.prototype = Object.extend(new Hatena_Node(), { parse : function(){ var c = this.self.context; var t = String.times("\t", this.self.ilevel); var l = c.shiftline(); var text = new Hatena_Text(); text._new({context : c}); text.parse(l); l = text.html(); c.htmllines(t + l); } }) // Hatena::DlNode Hatena_DlNode = function(){}; Hatena_DlNode.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.pattern = /^\:((?:<[^>]+>|\[\].+?\[\]|\[[^\]]+\]|\[\]|[^\:<\[]+)+)\:(.+)$/; }, parse : function(){ var c = this.self.context; var l = c.nextline(); if(!l.match(this.self.pattern)) return; this.self.llevel = RegExp.$1.length; var t = String.times("\t", this.self.ilevel); c.htmllines(t + "
"); while (l = c.nextline()) { if(!l.match(this.self.pattern)) break; c.shiftline(); c.htmllines(t + "\t
" + RegExp.$1 + "
"); c.htmllines(t + "\t
" + RegExp.$2 + "
"); } c.htmllines(t + "
"); } }) // Hatena::FootnoteNode Hatena_FootnoteNode = function(){}; Hatena_FootnoteNode.prototype = Object.extend(new Hatena_Node(), { html : "", parse : function(){ var c = this.self["context"]; if(c.self.footnotes == null || c.self.footnotes.length == 0) return; var t = String.times("\t", this.self["ilevel"]); var p = c.self.permalink; this.self["html"] = ''; this.self.html += t + '
\n'; var num = 0; var text = new Hatena_Text(); text._new({context : c}); for(var i = 0; i < c.self.footnotes.length; i++) { var note = c.self.footnotes[i]; num++; text.parse(note); var l = t + '\t

*' + num + ': ' + text.html() + '

'; this.self["html"] += l + "\n"; } this.self["html"] += t + '
\n'; } }) // Hatena::H3Node Hatena_H3Node = function(){}; Hatena_H3Node.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.pattern = /^\*(?:(\d{9,10}|[a-zA-Z]\w*)\*)?((?:\[[^\:\[\]]+\])+)?(.*)$/; }, parse : function(){ var c = this.self.context; var l = c.shiftline(); if(l == null) return; if(!l.match(this.self.pattern)) return; var name = RegExp.$1; var cat = RegExp.$2; var title = RegExp.$3; var b = c.self.baseuri; var p = c.self.permalink; var t = String.times("\t", this.self.ilevel); var sa = c.self.sectionanchor; /* TODO: カテゴリは未対応 if (cat) { if(cat.match(/\[([^\:\[\]]+)\]/)){ // 繰り返しできないなぁ... var w = RegExp.$1; var ew = escape(RegExp.$1); cat = cat.replace(/\[([^\:\[\]]+)\]/, '[' + w + ']'); } }*/ var extra = ''; var ret = this._formatname(name); var name = (ret[0] != undefined ? ret[0] : ""); extra = (ret[1] != undefined ? ret[1] : ""); c.htmllines(t + '

' + sa + ' ' + cat + title + '

' + extra); }, _formatname : function(name){ /* TODO: 時間も未対応。表示時の時間が表示されてしまう... if (name && name.match(/^\d{9,10}$/)) { var m = sprintf('%02d', (localtime($name))[1]); var h = sprintf('%02d', (localtime($name))[2]); return ( $name, qq| $h:$m|, ); } elsif ($name) {*/ if(name != ""){ return [name]; } else { this.self.context.incrementsection(); name = 'p' + this.self.context.self.sectioncount; return [name]; } } }) // Hatena::H4Node Hatena_H4Node = function(){}; Hatena_H4Node.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.pattern = /^\*\*((?:[^\*]).*)$/; }, parse : function(){ var c = this.self.context; var l = c.shiftline(); if(l == null) return; if(!l.match(this.self.pattern)) return; var t = String.times("\t", this.self.ilevel); c.htmllines(t + "

" + RegExp.$1 + "

"); } }) // Hatena::H5Node Hatena_H5Node = function(){}; Hatena_H5Node.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.pattern = /^\*\*\*((?:[^\*]).*)$/; }, parse : function(){ var c = this.self.context; var l = c.shiftline(); if(l == null) return; if(!l.match(this.self.pattern)) return; var t = String.times("\t", this.self.ilevel); c.htmllines(t + "
" + RegExp.$1 + "
"); } }) // Hatena::ListNode Hatena_ListNode = function(){}; Hatena_ListNode.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.pattern = /^([\-\+]+)([^>\-\+].*)$/; }, parse : function(){ var c = this.self.context; var l = c.nextline(); if(!l.match(this.self.pattern)) return; this.self.llevel = RegExp.$1.length; var t = String.times("\t", this.self.ilevel + this.self.llevel - 1); this.self.type = RegExp.$1.substr(0, 1) == '-' ? 'ul' : 'ol'; c.htmllines(t + "<" + this.self.type + ">"); while (l = c.nextline()) { if(!l.match(this.self.pattern)) break; if (RegExp.$1.length > this.self.llevel) { //c.htmllines(t + "\t
  • "); bug?? var node = new Hatena_ListNode(); node._new({ context : this.self.context, ilevel : this.self.ilevel }); node.parse(); //c.htmllines(t + "\t
  • "); bug?? } else if(RegExp.$1.length < this.self.llevel) { break; } else { l = c.shiftline(); c.htmllines(t + "\t
  • " + RegExp.$2 + "
  • "); } } c.htmllines(t + ""); } }) // Hatena::PNode Hatena_PNode = function(){}; Hatena_PNode.prototype = Object.extend(new Hatena_Node(), { parse :function(){ var c = this.self.context; var t = String.times("\t", this.self.ilevel); var l = c.shiftline(); var text = new Hatena_Text(); text._new({context : c}); text.parse(l); l = text.html(); c.htmllines(t + "

    " + l + "

    "); } }); // Hatena::PreNode Hatena_PreNode = function(){}; Hatena_PreNode.prototype = Object.extend(new Hatena_Node(), { init :function(){ this.self.pattern = /^>\|$/; this.self.endpattern = /(.*)\|<$/; this.self.startstring = "
    ";
    		this.self.endstring = "
    "; }, parse : function(){ // modified by edvakf var c = this.self.context; var m; if(!(m = c.nextline().match(this.self.pattern))) return; c.shiftline(); var t = String.times("\t", this.self.ilevel); c.htmllines(t + (m[1] ? // add class for syntax highlight this.self.startstring.replace('>',' class="sh_' + m[1] + '">') : this.self.startstring)); var x = ''; while (c.hasnext()) { var l = c.nextline(); if (l.match(this.self.endpattern)) { var x = RegExp.$1; c.shiftline(); break; } c.htmllines(this.escape_pre(c.shiftline())); } c.htmllines(x + this.self.endstring); }, escape_pre : function(text){ return text; } }) // Hatena::SuperpreNode Hatena_SuperpreNode = function(){}; Hatena_SuperpreNode.prototype = Object.extend(new Hatena_PreNode(), { init : function(){ this.self.pattern = /^>\|(\S*)\|$/; // modified by edvakf this.self.endpattern = /^\|\|<$/; this.self.startstring = "
    ";
    		this.self.endstring = "
    "; }, escape_pre : function(s){ return String._escapeHTML(s); } }) // Hatena::SuperpreNode Hatena_TableNode = function(){}; Hatena_TableNode.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.pattern = /^\|([^\|]*\|(?:[^\|]*\|)+)$/; }, parse : function(s){ var c = this.self.context; var l = c.nextline(); if(!l.match(this.self.pattern)) return; var t = String.times("\t", this.self.ilevel); c.htmllines(t + ""); while (l = c.nextline()) { if(!l.match(this.self.pattern)) break; l = c.shiftline(); c.htmllines(t + "\t"); var td = l.split("|"); td.pop(); td.shift(); for (var i = 0; i < td.length; i++) { var item = td[i]; if (item.match(/^\*(.*)/)) { c.htmllines(t + "\t\t"); } else { c.htmllines(t + "\t\t"); } } c.htmllines(t + "\t"); } c.htmllines(t + "
    " + RegExp.$1 + "" + item + "
    "); } }) // Hatena::Section Hatena_SectionNode = function(){}; Hatena_SectionNode.prototype = Object.extend(new Hatena_Node(), { init : function(){ this.self.childnode = ["h5", "h4", "h3", "blockquote", "dl", "list", "pre", "superpre", "table", "tagline", "tag"]; this.self.startstring = '
    '; this.self.endstring = '
    '; this.self.child_node_refs = Array(); }, parse : function(){ var c = this.self.context; var t = String.times("\t", this.self.ilevel); this._set_child_node_refs(); c.htmllines(t + this.self.startstring); while (c.hasnext()) { var l = c.nextline(); var node = this._findnode(l); if(node == null) return; // TODO: ref == instanceof ??? //if (ref(node) eq 'Hatena_H3Node') { // if(this.self.started++) break; //} node.parse(); } c.htmllines(t + this.self.endstring); }, _set_child_node_refs : function(){ var c = this.self.context; var nodeoption = { context : c, ilevel : this.self.ilevel + 1 }; var invalid = Array(); if(c.self.invalidnode) invalid[c.self.invalidnode] = Array(); for(var i = 0; i < this.self.childnode.length; i++) { var node = this.self.childnode[i]; if(invalid[node]) continue; var mod = 'Hatena_' + node.charAt(0).toUpperCase() + node.substr(1).toLowerCase() + 'Node'; var n = eval("new "+ mod +"()"); n._new(nodeoption); this.self.child_node_refs.push(n); } }, _findnode : function(l){ for(var i = 0; i < this.self.child_node_refs.length; i++) { var node = this.self.child_node_refs[i]; var pat = node.self.pattern; if(pat == null) continue; if (l.match(pat)) { return node; } } var nodeoption = { context : this.self.context, ilevel : this.self.ilevel + 1 }; if (l.length == 0) { var node = new Hatena_BrNode(nodeoption); node._new(nodeoption); return node; } else if (this.self.context.noparagraph()) { var node = new Hatena_CDataNode(); node._new(nodeoption); return node; } else { var node = new Hatena_PNode; node._new(nodeoption); return node; } } }) // Hatena::BlockquoteNode (modified by edvakf) Hatena_BlockquoteNode = function(){}; Hatena_BlockquoteNode.prototype = Object.extend(new Hatena_SectionNode(), { init : function(){ this.self.pattern = /^>(?:(https?:\/\/.*?)(:title(=.+)?)?)?>$/; // modified by edvakf this.self.endpattern = /^<<$/; this.self.childnode = ["h4", "h5", "blockquote", "dl", "list", "pre", "superpre", "table"];//, "tagline", "tag"]; this.self.startstring = "
    "; this.self.endstring = "
    "; this.self.child_node_refs = Array(); }, parse : function(){ var c = this.self.context; var m; if(!(m = c.nextline().match(this.self.pattern))) return; if (m[1]) { // m[1] is the url (added by edvakf) var title = String._escapeHTML(m[3] ? m[3].substr(1) : // if title given, then use it m[2] ? '{{title}}' : // else if title not given, fetch from YQL m[1]); // else, use URL this.self.startstring = this.self.startstring.replace('>', ' cite="' + m[1] + '" title="' + title + '">'); var cite = '' + title + ''; } else { var cite = ''; } c.shiftline(); var t = String.times("\t", this.self.ilevel); this._set_child_node_refs(); c.htmllines(t + this.self.startstring); while (c.hasnext()) { var l = c.nextline(); if (l.match(this.self.endpattern)) { c.shiftline(); break; } var node = this._findnode(l); if(node == null) break; node.parse(); } c.htmllines(t + cite + this.self.endstring); } }) // Hatena::TagNode Hatena_TagNode = function(){}; Hatena_TagNode.prototype = Object.extend(new Hatena_SectionNode(), { init : function(){ this.self.pattern = /^>(<.*)$/; this.self.endpattern = /^(.*>)<$/; this.self.childnode = ["h4", "h5", "blockquote", "dl", "list", "pre", "superpre", "table"]; this.self.child_node_refs = Array(); }, parse : function(){ var c = this.self.context; var t = String.times("\t", this.self.ilevel); if(!c.nextline().match(this.self.pattern)) return; c.shiftline(); c.noparagraph(1); this._set_child_node_refs(); var x =this._parse_text(RegExp.$1); c.htmllines(t + x); while (c.hasnext()) { var l = c.nextline(); if (l.match(this.self.endpattern)) { c.shiftline(); x = this._parse_text(RegExp.$1); c.htmllines(t + x); break; } var node = this._findnode(l); if(node == null) break; node.parse(); } c.noparagraph(0); }, _parse_text : function(l){ var text = new Hatena_Text(); text._new({context : this.self.context}); text.parse(l); return text.html(); } }) // Hatena::TaglineNode Hatena_TaglineNode = function(){}; Hatena_TaglineNode.prototype = Object.extend(new Hatena_SectionNode(), { init : function(){ this.self.pattern = /^>(<.*>)<$/; this.self.child_node_refs = Array(); }, parse : function(){ var c = this.self.context; var t = String.times("\t", this.self.ilevel); if(!c.nextline().match(this.self.pattern)) return; c.shiftline(); c.htmllines(t + RegExp.$1); } }) // Hatena::Text Hatena_Text = function(){} Hatena_Text.prototype = { _new : function(args){ this.self = { context : args["context"], html : '' }; }, parse : function(text){ this.self.html = ''; if(text == null) return; this.self.html = text; }, html : function(){return this.self.html;} } /*var h = new Hatena(); h.parse("hoge((a))aaa))aaaa\n>hoge= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') { url = url.substr(1, url.length - 2); } if (sh_isEmailAddress(url)) { url = 'mailto:' + url; } tags[numTags - 2].node.href = url; } /* Konqueror has a bug where the regular expression /$/g will not match at the end of a line more than once: var regex = /$/g; var match; var line = '1234567890'; regex.lastIndex = 10; match = regex.exec(line); var line2 = 'abcde'; regex.lastIndex = 5; match = regex.exec(line2); // fails */ function sh_konquerorExec(s) { var result = ['']; result.index = s.length; result.input = s; return result; } /** Highlights all elements containing source code in a text string. The return value is an array of objects, each representing an HTML start or end tag. Each object has a property named pos, which is an integer representing the text offset of the tag. Every start tag also has a property named node, which is the DOM element started by the tag. End tags do not have this property. @param inputString a text string @param language a language definition object @return an array of tag objects */ function sh_highlightString(inputString, language) { if (/Konqueror/.test(navigator.userAgent)) { if (! language.konquered) { for (var s = 0; s < language.length; s++) { for (var p = 0; p < language[s].length; p++) { var r = language[s][p][0]; if (r.source === '$') { r.exec = sh_konquerorExec; } } } language.konquered = true; } } var a = document.createElement('a'); var span = document.createElement('span'); // the result var tags = []; var numTags = 0; // each element is a pattern object from language var patternStack = []; // the current position within inputString var pos = 0; // the name of the current style, or null if there is no current style var currentStyle = null; var output = function(s, style) { var length = s.length; // this is more than just an optimization - we don't want to output empty elements if (length === 0) { return; } if (! style) { var stackLength = patternStack.length; if (stackLength !== 0) { var pattern = patternStack[stackLength - 1]; // check whether this is a state or an environment if (! pattern[3]) { // it's not a state - it's an environment; use the style for this environment style = pattern[1]; } } } if (currentStyle !== style) { if (currentStyle) { tags[numTags++] = {pos: pos}; if (currentStyle === 'sh_url') { sh_setHref(tags, numTags, inputString); } } if (style) { var clone; if (style === 'sh_url') { clone = a.cloneNode(false); } else { clone = span.cloneNode(false); } clone.className = style; tags[numTags++] = {node: clone, pos: pos}; } } pos += length; currentStyle = style; }; var endOfLinePattern = /\r\n|\r|\n/g; endOfLinePattern.lastIndex = 0; var inputStringLength = inputString.length; while (pos < inputStringLength) { var start = pos; var end; var startOfNextLine; var endOfLineMatch = endOfLinePattern.exec(inputString); if (endOfLineMatch === null) { end = inputStringLength; startOfNextLine = inputStringLength; } else { end = endOfLineMatch.index; startOfNextLine = endOfLinePattern.lastIndex; } var line = inputString.substring(start, end); var matchCache = []; for (;;) { var posWithinLine = pos - start; var stateIndex; var stackLength = patternStack.length; if (stackLength === 0) { stateIndex = 0; } else { // get the next state stateIndex = patternStack[stackLength - 1][2]; } var state = language[stateIndex]; var numPatterns = state.length; var mc = matchCache[stateIndex]; if (! mc) { mc = matchCache[stateIndex] = []; } var bestMatch = null; var bestPatternIndex = -1; for (var i = 0; i < numPatterns; i++) { var match; if (i < mc.length && (mc[i] === null || posWithinLine <= mc[i].index)) { match = mc[i]; } else { var regex = state[i][0]; regex.lastIndex = posWithinLine; match = regex.exec(line); mc[i] = match; } if (match !== null && (bestMatch === null || match.index < bestMatch.index)) { bestMatch = match; bestPatternIndex = i; if (match.index === posWithinLine) { break; } } } if (bestMatch === null) { output(line.substring(posWithinLine), null); break; } else { // got a match if (bestMatch.index > posWithinLine) { output(line.substring(posWithinLine, bestMatch.index), null); } var pattern = state[bestPatternIndex]; var newStyle = pattern[1]; var matchedString; if (newStyle instanceof Array) { for (var subexpression = 0; subexpression < newStyle.length; subexpression++) { matchedString = bestMatch[subexpression + 1]; output(matchedString, newStyle[subexpression]); } } else { matchedString = bestMatch[0]; output(matchedString, newStyle); } switch (pattern[2]) { case -1: // do nothing break; case -2: // exit patternStack.pop(); break; case -3: // exitall patternStack.length = 0; break; default: // this was the start of a delimited pattern or a state/environment patternStack.push(pattern); break; } } } // end of the line if (currentStyle) { tags[numTags++] = {pos: pos}; if (currentStyle === 'sh_url') { sh_setHref(tags, numTags, inputString); } currentStyle = null; } pos = startOfNextLine; } return tags; } //////////////////////////////////////////////////////////////////////////////// // DOM-dependent functions function sh_getClasses(element) { var result = []; var htmlClass = element.className; if (htmlClass && htmlClass.length > 0) { var htmlClasses = htmlClass.split(' '); for (var i = 0; i < htmlClasses.length; i++) { if (htmlClasses[i].length > 0) { result.push(htmlClasses[i]); } } } return result; } function sh_addClass(element, name) { var htmlClasses = sh_getClasses(element); for (var i = 0; i < htmlClasses.length; i++) { if (name.toLowerCase() === htmlClasses[i].toLowerCase()) { return; } } htmlClasses.push(name); element.className = htmlClasses.join(' '); } /** Extracts the tags from an HTML DOM NodeList. @param nodeList a DOM NodeList @param result an object with text, tags and pos properties */ function sh_extractTagsFromNodeList(nodeList, result) { var length = nodeList.length; for (var i = 0; i < length; i++) { var node = nodeList.item(i); switch (node.nodeType) { case 1: if (node.nodeName.toLowerCase() === 'br') { var terminator; if (/MSIE/.test(navigator.userAgent)) { terminator = '\r'; } else { terminator = '\n'; } result.text.push(terminator); result.pos++; } else { result.tags.push({node: node.cloneNode(false), pos: result.pos}); sh_extractTagsFromNodeList(node.childNodes, result); result.tags.push({pos: result.pos}); } break; case 3: case 4: result.text.push(node.data); result.pos += node.length; break; } } } /** Extracts the tags from the text of an HTML element. The extracted tags will be returned as an array of tag objects. See sh_highlightString for the format of the tag objects. @param element a DOM element @param tags an empty array; the extracted tag objects will be returned in it @return the text of the element @see sh_highlightString */ function sh_extractTags(element, tags) { var result = {}; result.text = []; result.tags = tags; result.pos = 0; sh_extractTagsFromNodeList(element.childNodes, result); return result.text.join(''); } /** Merges the original tags from an element with the tags produced by highlighting. @param originalTags an array containing the original tags @param highlightTags an array containing the highlighting tags - these must not overlap @result an array containing the merged tags */ function sh_mergeTags(originalTags, highlightTags) { var numOriginalTags = originalTags.length; if (numOriginalTags === 0) { return highlightTags; } var numHighlightTags = highlightTags.length; if (numHighlightTags === 0) { return originalTags; } var result = []; var originalIndex = 0; var highlightIndex = 0; while (originalIndex < numOriginalTags && highlightIndex < numHighlightTags) { var originalTag = originalTags[originalIndex]; var highlightTag = highlightTags[highlightIndex]; if (originalTag.pos <= highlightTag.pos) { result.push(originalTag); originalIndex++; } else { result.push(highlightTag); if (highlightTags[highlightIndex + 1].pos <= originalTag.pos) { highlightIndex++; result.push(highlightTags[highlightIndex]); highlightIndex++; } else { // new end tag result.push({pos: originalTag.pos}); // new start tag highlightTags[highlightIndex] = {node: highlightTag.node.cloneNode(false), pos: originalTag.pos}; } } } while (originalIndex < numOriginalTags) { result.push(originalTags[originalIndex]); originalIndex++; } while (highlightIndex < numHighlightTags) { result.push(highlightTags[highlightIndex]); highlightIndex++; } return result; } /** Inserts tags into text. @param tags an array of tag objects @param text a string representing the text @return a DOM DocumentFragment representing the resulting HTML */ function sh_insertTags(tags, text) { var doc = document; var result = document.createDocumentFragment(); var tagIndex = 0; var numTags = tags.length; var textPos = 0; var textLength = text.length; var currentNode = result; // output one tag or text node every iteration while (textPos < textLength || tagIndex < numTags) { var tag; var tagPos; if (tagIndex < numTags) { tag = tags[tagIndex]; tagPos = tag.pos; } else { tagPos = textLength; } if (tagPos <= textPos) { // output the tag if (tag.node) { // start tag var newNode = tag.node; currentNode.appendChild(newNode); currentNode = newNode; } else { // end tag currentNode = currentNode.parentNode; } tagIndex++; } else { // output text currentNode.appendChild(doc.createTextNode(text.substring(textPos, tagPos))); textPos = tagPos; } } return result; } /** Highlights an element containing source code. Upon completion of this function, the element will have been placed in the "sh_sourceCode" class. @param element a DOM
     element containing the source code to be highlighted
    @param  language  a language definition object
    */
    function sh_highlightElement(element, language) {
      sh_addClass(element, 'sh_sourceCode');
      var originalTags = [];
      var inputString = sh_extractTags(element, originalTags);
      var highlightTags = sh_highlightString(inputString, language);
      var tags = sh_mergeTags(originalTags, highlightTags);
      var documentFragment = sh_insertTags(tags, inputString);
      while (element.hasChildNodes()) {
        element.removeChild(element.firstChild);
      }
      element.appendChild(documentFragment);
    }
    /*
    function sh_getXMLHttpRequest() {
      if (window.ActiveXObject) {
        return new ActiveXObject('Msxml2.XMLHTTP');
      }
      else if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
      }
      throw 'No XMLHttpRequest implementation available';
    }
    
    function sh_load(language, element, prefix, suffix) {
      if (language in sh_requests) {
        sh_requests[language].push(element);
        return;
      }
      sh_requests[language] = [element];
      var request = sh_getXMLHttpRequest();
      var url = prefix + 'sh_' + language + suffix;
      request.open('GET', url, true);
      request.onreadystatechange = function () {
        if (request.readyState === 4) {
          try {
            if (! request.status || request.status === 200) {
              eval(request.responseText);
              var elements = sh_requests[language];
              for (var i = 0; i < elements.length; i++) {
                sh_highlightElement(elements[i], sh_languages[language]);
              }
            }
            else {
              throw 'HTTP error: status ' + request.status;
            }
          }
          finally {
            request = null;
          }
        }
      };
      request.send(null);
    }
    */
    /**
    Highlights all elements containing source code on the current page. Elements
    containing source code must be "pre" elements with a "class" attribute of
    "sh_LANGUAGE", where LANGUAGE is a valid language identifier; e.g., "sh_java"
    identifies the element as containing "java" language source code.
    */
    /*
    function sh_highlightDocument(prefix, suffix) {
      var nodeList = document.getElementsByTagName('pre');
      for (var i = 0; i < nodeList.length; i++) {
        var element = nodeList.item(i);
        var htmlClasses = sh_getClasses(element);
        for (var j = 0; j < htmlClasses.length; j++) {
          var htmlClass = htmlClasses[j].toLowerCase();
          if (htmlClass === 'sh_sourcecode') {
            continue;
          }
          if (htmlClass.substr(0, 3) === 'sh_') {
            var language = htmlClass.substring(3);
            if (language in sh_languages) {
              sh_highlightElement(element, sh_languages[language]);
            }
            else if (typeof(prefix) === 'string' && typeof(suffix) === 'string') {
              sh_load(language, element, prefix, suffix);
            }
            else {
              throw 'Found 
     element with class="' + htmlClass + '", but no such language exists';
            }
            break;
          }
        }
      }
    }
    */
    
    
    if (! this.sh_languages) {
      this.sh_languages = {};
    }
    sh_languages['c'] = [
      [
        [
          /\/\/\//g,
          'sh_comment',
          1
        ],
        [
          /\/\//g,
          'sh_comment',
          7
        ],
        [
          /\/\*\*/g,
          'sh_comment',
          8
        ],
        [
          /\/\*/g,
          'sh_comment',
          9
        ],
        [
          /(\bstruct)([ \t]+)([A-Za-z0-9_]+)/g,
          ['sh_keyword', 'sh_normal', 'sh_classname'],
          -1
        ],
        [
          /^[ \t]*#(?:[ \t]*include)/g,
          'sh_preproc',
          10,
          1
        ],
        [
          /^[ \t]*#(?:[ \t]*[A-Za-z0-9_]*)/g,
          'sh_preproc',
          -1
        ],
        [
          /\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,
          'sh_number',
          -1
        ],
        [
          /"/g,
          'sh_string',
          13
        ],
        [
          /'/g,
          'sh_string',
          14
        ],
        [
          /\b(?:__asm|__cdecl|__declspec|__export|__far16|__fastcall|__fortran|__import|__pascal|__rtti|__stdcall|_asm|_cdecl|__except|_export|_far16|_fastcall|__finally|_fortran|_import|_pascal|_stdcall|__thread|__try|asm|auto|break|case|catch|cdecl|const|continue|default|do|else|enum|extern|for|goto|if|pascal|register|return|sizeof|static|struct|switch|typedef|union|volatile|while)\b/g,
          'sh_keyword',
          -1
        ],
        [
          /\b(?:bool|char|double|float|int|long|short|signed|unsigned|void|wchar_t)\b/g,
          'sh_type',
          -1
        ],
        [
          /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g,
          'sh_symbol',
          -1
        ],
        [
          /\{|\}/g,
          'sh_cbracket',
          -1
        ],
        [
          /(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g,
          'sh_function',
          -1
        ],
        [
          /([A-Za-z](?:[^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]|[_])*)((?:<.*>)?)(\s+(?=[*&]*[A-Za-z][^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]*\s*[`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\[\]]+))/g,
          ['sh_usertype', 'sh_usertype', 'sh_normal'],
          -1
        ]
      ],
      [
        [
          /$/g,
          null,
          -2
        ],
        [
          /(?:?)|(?:?)/g,
          'sh_url',
          -1
        ],
        [
          /<\?xml/g,
          'sh_preproc',
          2,
          1
        ],
        [
          //g,
          'sh_keyword',
          -1
        ],
        [
          /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g,
          'sh_keyword',
          6,
          1
        ],
        [
          /&(?:[A-Za-z0-9]+);/g,
          'sh_preproc',
          -1
        ],
        [
          /<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g,
          'sh_keyword',
          -1
        ],
        [
          /<(?:\/)?[A-Za-z][A-Za-z0-9]*/g,
          'sh_keyword',
          6,
          1
        ],
        [
          /@[A-Za-z]+/g,
          'sh_type',
          -1
        ],
        [
          /(?:TODO|FIXME|BUG)(?:[:]?)/g,
          'sh_todo',
          -1
        ]
      ],
      [
        [
          /\?>/g,
          'sh_preproc',
          -2
        ],
        [
          /([^=" \t>]+)([ \t]*)(=?)/g,
          ['sh_type', 'sh_normal', 'sh_symbol'],
          -1
        ],
        [
          /"/g,
          'sh_string',
          3
        ]
      ],
      [
        [
          /\\(?:\\|")/g,
          null,
          -1
        ],
        [
          /"/g,
          'sh_string',
          -2
        ]
      ],
      [
        [
          />/g,
          'sh_preproc',
          -2
        ],
        [
          /([^=" \t>]+)([ \t]*)(=?)/g,
          ['sh_type', 'sh_normal', 'sh_symbol'],
          -1
        ],
        [
          /"/g,
          'sh_string',
          3
        ]
      ],
      [
        [
          /-->/g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          //g,
          'sh_comment',
          -2
        ],
        [
          /