├── .gitignore ├── Makefile ├── README.md ├── bookmarklet.js ├── demo ├── bookmarkme.html ├── demo.css ├── docco.css ├── flexible-nav.min.css ├── flexible-nav.min.js └── index.html ├── dist └── flexible-nav-1.0.zip ├── docco ├── .gitignore ├── Cakefile ├── README ├── bin │ └── docco ├── index.html ├── lib │ └── docco.js ├── package.json ├── resources │ ├── docco.css │ └── docco.jst ├── src │ └── docco.coffee └── vendor │ └── showdown.js ├── flexible-nav.js ├── flexible-nav.less └── lib ├── bookmarklet.min.js ├── flexible-nav.min.css └── flexible-nav.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.un~ 2 | *.swp 3 | docs/ 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LESSC=lessc 2 | LESSOPT=-x 3 | 4 | JSMINC=yuicompressor 5 | JSMINOPT= 6 | 7 | JSDOCC=./docco/bin/docco 8 | JSDOCOPT= 9 | 10 | VERSION=1.0.1 11 | ZNAME=flexible-nav-${VERSION} 12 | 13 | 14 | all: dist/${ZNAME}.zip lib/flexible-nav.min.css demo/index.html lib/flexible-nav.min.js lib/bookmarklet.min.js docs/flexible-nav.html demo/flexible-nav.min.js demo/flexible-nav.min.css 15 | 16 | lib/flexible-nav.min.css: flexible-nav.less 17 | lib/flexible-nav.min.js: flexible-nav.js 18 | lib/bookmarklet.min.js: bookmarklet.js 19 | 20 | demo/index.html: docs/flexible-nav.html 21 | cp $< $@ 22 | 23 | docs/flexible-nav.html: flexible-nav.js 24 | 25 | demo/flexible-nav.min.js: lib/flexible-nav.min.js 26 | cp $< $@ 27 | 28 | demo/flexible-nav.min.css: lib/flexible-nav.min.css 29 | cp $< $@ 30 | 31 | docs/%.html: %.js 32 | ${JSDOCC} ${JSDOCOPT} $< 33 | 34 | lib/%.min.css: %.less 35 | ${LESSC} ${LESSOPT} $< -o $@ 36 | 37 | %.min.css: %.less 38 | ${LESSC} ${LESSOPT} $< -o $@ 39 | 40 | lib/%.min.js: %.js 41 | ${JSMINC} ${JSMINOPT} $< -o $@ 42 | 43 | dist/${ZNAME}.zip: lib/flexible-nav.min.js lib/flexible-nav.min.css 44 | rm -rf ${ZNAME}/ ${ZNAME}.zip 45 | mkdir ${ZNAME}/ 46 | cp lib/flexible-nav.min.js ${ZNAME}/ 47 | cp lib/flexible-nav.min.css ${ZNAME}/ 48 | zip -r dist/${ZNAME}.zip ${ZNAME} 49 | rm -rf ${ZNAME} 50 | 51 | clean: 52 | rm -rf dist/${ZNAME}.zip docs/* lib/* demo/index.html demo/flexible-nav.min.js demo/flexible-nav.min.css 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | See http://demo.greweb.fr/flexible-nav for documentation. 2 | 3 | License 4 | ======= 5 | 6 | Copyright 2011 Gaetan Renaudeau 7 | 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | -------------------------------------------------------------------------------- /bookmarklet.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Flexible nav bookmarklet 3 | * by @greweb 4 | */ 5 | 6 | (function(){ 7 | var base = window.flexibleNavBase; 8 | var LINK_LIMIT = 20; 9 | 10 | fullFunc({ 11 | css : [ base + "flexible-nav.min.css"], 12 | js : [ base + "flexible-nav.min.js" ], 13 | ready : function() { 14 | $('nav.flexible-nav').remove(); 15 | var headers = ['h1']; 16 | for(var i=2; $( headers.join(',') ).size()") 29 | .attr({href:f,rel:"stylesheet",type:'text/css'}).appendTo("head")})}a.jqpath=a. 30 | jqpath||"http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"; 31 | (function(b){var c=document.createElement("script");c.type="text/javascript";c.src=b; 32 | c.onload=function(){e(a.css);d(a.js)};document.body.appendChild(c)})(a.jqpath)}; 33 | 34 | -------------------------------------------------------------------------------- /demo/bookmarkme.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flexible Nav Bookmarklet 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | a.bookmark { 2 | margin: 10px; 3 | padding: 5px 10px; 4 | border-radius: 10px; 5 | background: #333; 6 | color: #eee; 7 | text-decoration: none; 8 | font-size: 1.2em; 9 | } 10 | a.bookmark:hover { 11 | background: #555; 12 | color: #fff; 13 | } 14 | a.download { 15 | margin: 10px 0; 16 | padding: 5px 10px; 17 | font-size: 1.2em; 18 | font-weight: bold; 19 | color: #379; 20 | text-decoration: none; 21 | border: 2px solid #379; 22 | border-radius: 10px; 23 | display: inline-block; 24 | } 25 | -------------------------------------------------------------------------------- /demo/docco.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Layout and Typography ----------------------------*/ 2 | body { 3 | font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 4 | font-size: 15px; 5 | line-height: 22px; 6 | color: #252519; 7 | margin: 0; padding: 0; 8 | } 9 | a { 10 | color: #261a3b; 11 | } 12 | a:visited { 13 | color: #261a3b; 14 | } 15 | p { 16 | margin: 0 0 15px 0; 17 | } 18 | h1, h2, h3, h4, h5, h6 { 19 | margin: 0px 0 15px 0; 20 | } 21 | h1 { 22 | margin-top: 40px; 23 | } 24 | #container { 25 | position: relative; 26 | } 27 | #background { 28 | position: fixed; 29 | top: 0; left: 525px; right: 0; bottom: 0; 30 | background: #f5f5ff; 31 | border-left: 1px solid #e5e5ee; 32 | z-index: -1; 33 | } 34 | #jump_to, #jump_page { 35 | background: white; 36 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 37 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 38 | font: 10px Arial; 39 | text-transform: uppercase; 40 | cursor: pointer; 41 | text-align: right; 42 | } 43 | #jump_to, #jump_wrapper { 44 | position: fixed; 45 | right: 0; top: 0; 46 | padding: 5px 10px; 47 | } 48 | #jump_wrapper { 49 | padding: 0; 50 | display: none; 51 | } 52 | #jump_to:hover #jump_wrapper { 53 | display: block; 54 | } 55 | #jump_page { 56 | padding: 5px 0 3px; 57 | margin: 0 0 25px 25px; 58 | } 59 | #jump_page .source { 60 | display: block; 61 | padding: 5px 10px; 62 | text-decoration: none; 63 | border-top: 1px solid #eee; 64 | } 65 | #jump_page .source:hover { 66 | background: #f5f5ff; 67 | } 68 | #jump_page .source:first-child { 69 | } 70 | table td { 71 | border: 0; 72 | outline: 0; 73 | } 74 | td.docs, th.docs { 75 | max-width: 450px; 76 | min-width: 450px; 77 | min-height: 5px; 78 | padding: 10px 25px 1px 50px; 79 | overflow-x: hidden; 80 | vertical-align: top; 81 | text-align: left; 82 | } 83 | .docs pre { 84 | margin: 15px 0 15px; 85 | padding-left: 15px; 86 | } 87 | .docs p tt, .docs p code { 88 | background: #f8f8ff; 89 | border: 1px solid #dedede; 90 | font-size: 12px; 91 | padding: 0 0.2em; 92 | } 93 | .pilwrap { 94 | position: relative; 95 | } 96 | .pilcrow { 97 | font: 12px Arial; 98 | text-decoration: none; 99 | color: #454545; 100 | position: absolute; 101 | top: 3px; left: -20px; 102 | padding: 1px 2px; 103 | opacity: 0; 104 | -webkit-transition: opacity 0.2s linear; 105 | } 106 | td.docs:hover .pilcrow { 107 | opacity: 1; 108 | } 109 | td.code, th.code { 110 | padding: 14px 15px 16px 25px; 111 | width: 100%; 112 | vertical-align: top; 113 | background: #f5f5ff; 114 | border-left: 1px solid #e5e5ee; 115 | } 116 | pre, tt, code { 117 | font-size: 12px; line-height: 18px; 118 | font-family: Monaco, Consolas, "Lucida Console", monospace; 119 | margin: 0; padding: 0; 120 | } 121 | 122 | 123 | /*---------------------- Syntax Highlighting -----------------------------*/ 124 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 125 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 126 | body .hll { background-color: #ffffcc } 127 | body .c { color: #408080; font-style: italic } /* Comment */ 128 | body .err { border: 1px solid #FF0000 } /* Error */ 129 | body .k { color: #954121 } /* Keyword */ 130 | body .o { color: #666666 } /* Operator */ 131 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 132 | body .cp { color: #BC7A00 } /* Comment.Preproc */ 133 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 134 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 135 | body .gd { color: #A00000 } /* Generic.Deleted */ 136 | body .ge { font-style: italic } /* Generic.Emph */ 137 | body .gr { color: #FF0000 } /* Generic.Error */ 138 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 139 | body .gi { color: #00A000 } /* Generic.Inserted */ 140 | body .go { color: #808080 } /* Generic.Output */ 141 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 142 | body .gs { font-weight: bold } /* Generic.Strong */ 143 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 144 | body .gt { color: #0040D0 } /* Generic.Traceback */ 145 | body .kc { color: #954121 } /* Keyword.Constant */ 146 | body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 147 | body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 148 | body .kp { color: #954121 } /* Keyword.Pseudo */ 149 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 150 | body .kt { color: #B00040 } /* Keyword.Type */ 151 | body .m { color: #666666 } /* Literal.Number */ 152 | body .s { color: #219161 } /* Literal.String */ 153 | body .na { color: #7D9029 } /* Name.Attribute */ 154 | body .nb { color: #954121 } /* Name.Builtin */ 155 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 156 | body .no { color: #880000 } /* Name.Constant */ 157 | body .nd { color: #AA22FF } /* Name.Decorator */ 158 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 159 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 160 | body .nf { color: #0000FF } /* Name.Function */ 161 | body .nl { color: #A0A000 } /* Name.Label */ 162 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 163 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 164 | body .nv { color: #19469D } /* Name.Variable */ 165 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 166 | body .w { color: #bbbbbb } /* Text.Whitespace */ 167 | body .mf { color: #666666 } /* Literal.Number.Float */ 168 | body .mh { color: #666666 } /* Literal.Number.Hex */ 169 | body .mi { color: #666666 } /* Literal.Number.Integer */ 170 | body .mo { color: #666666 } /* Literal.Number.Oct */ 171 | body .sb { color: #219161 } /* Literal.String.Backtick */ 172 | body .sc { color: #219161 } /* Literal.String.Char */ 173 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 174 | body .s2 { color: #219161 } /* Literal.String.Double */ 175 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 176 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 177 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 178 | body .sx { color: #954121 } /* Literal.String.Other */ 179 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 180 | body .s1 { color: #219161 } /* Literal.String.Single */ 181 | body .ss { color: #19469D } /* Literal.String.Symbol */ 182 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 183 | body .vc { color: #19469D } /* Name.Variable.Class */ 184 | body .vg { color: #19469D } /* Name.Variable.Global */ 185 | body .vi { color: #19469D } /* Name.Variable.Instance */ 186 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ 187 | 188 | -------------------------------------------------------------------------------- /demo/flexible-nav.min.css: -------------------------------------------------------------------------------- 1 | nav.flexible-nav{text-align:right;position:fixed;right:0px;height:100%;width:20px;top:0;right:0;border-left:4px dashed rgba(150, 150, 150, 0.2);background:white;z-index:9999;}nav.flexible-nav ul{padding:0;margin:0;list-style:none;}nav.flexible-nav ul li{margin:0;padding:0;} 2 | nav.flexible-nav ul li a{border-radius:5px;-moz-border-radius:5px;-webkit-border-radius:5px;-o-border-radius:5px;z-index:10001;text-decoration:none;position:fixed;right:10px;background:#666;color:#eee;opacity:0.7;padding:2px 6px;transition-duration:0.2s;-moz-transition-duration:0.2s;-webkit-transition-duration:0.2s;-o-transition-duration:0.2s;transition-property:padding background color;-moz-transition-property:padding background color;-webkit-transition-property:padding background color;-o-transition-property:padding background color;}nav.flexible-nav ul li a:hover{z-index:11111;opacity:1;padding-right:12px;} 3 | nav.flexible-nav ul li a.current{background:#000;} 4 | nav.flexible-nav ul li a.tnn-h1{border-right:15px solid #A00;} 5 | nav.flexible-nav ul li a.tnn-h2{border-right:15px solid #406;} 6 | nav.flexible-nav ul li a.tnn-h3{border-right:15px solid #690;} 7 | -------------------------------------------------------------------------------- /demo/flexible-nav.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Flexible Nav - 2011 - by @greweb 3 | * http://www.apache.org/licenses/LICENSE-2.0 4 | */ 5 | (function(e){var b=0;var c=function(){return ++b};var a=function(f){var h=e('');var g=e.map(f,function(i){return e('
  • '+i.text+"
  • ")[0]});h.find("ul").append(g);return h};var d=function(g){var f=e(g).attr("href");if(f.substring(0,1)=="#"){var h=e(f);return h.size()?h:null}return null};window.FlexibleNavMaker=function(f){var g=this;g.nodes=e(f||"h1,h2,h3");g.make=function(){var h=g.nodes.map(function(){var i=e(this);var k=i.attr("id");if(!k){while(!k){k="n"+(c());if(e("#"+k).size()>0){k=null}}i.attr("id",k)}var j=i.attr("data-navtext")||i.text();return{href:"#"+k,text:j}});return a(h)}};window.FlexibleNav=function(g){var f=this;f.nav=e(g);f.updateClasses=function(){f.nav.find("a").each(function(){var h=e(this);var i=d(h);if(i){h.addClass("tnn-"+i[0].nodeName.toLowerCase())}})};f.update=function(){var k=e(document).height();var l=e(window).height();var h=f.nav.find("a").map(function(){var m=e(this);var n=d(m);if(n==null||!n.is(":visible")){return null}return{top:n.offset().top,node:m}});e.each(h,function(m,n){n.node.css("top",(100*n.top/k)+"%")});var j=e(document).scrollTop();var i=null;e.each(h,function(m,n){n.node.removeClass("current");if(i==null||(n.top<=j+10&&n.top>i.top)){i=n}});i&&i.node.addClass("current")};e(window).scroll(function(){f.update()});f.updateClasses();f.update()}}(jQuery)); -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flexible Nav jQuery library 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 27 | 38 | 39 | 40 |
    41 |
    42 |
    43 | 44 | 46 |
    47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 148 | 154 | 155 | 156 | 157 | 158 | 164 | 168 | 169 | 170 | 171 | 172 | 180 | 184 | 185 | 186 | 187 | 188 | 198 | 209 | 210 | 211 | 212 | 213 | 219 | 229 | 230 | 231 | 232 | 233 | 246 | 252 | 253 | 254 | 255 | 256 | 265 | 270 | 271 | 272 | 273 | 274 | 280 | 290 | 291 | 292 | 293 | 294 | 300 | 308 | 309 | 310 | 311 | 312 | 324 | 329 | 330 | 331 | 332 | 333 | 342 | 353 | 354 | 355 | 356 | 357 | 363 | 369 | 370 | 371 | 372 | 373 | 379 | 390 | 391 | 392 | 393 | 394 | 400 | 405 | 406 | 407 | 408 | 409 | 415 | 426 | 427 | 428 | 429 | 430 | 438 | 449 | 450 | 451 | 452 |
    56 |
    57 | 58 |
    59 |

    Flexible Nav jQuery library.

    60 | 61 |

    Flexible Nav is a small jQuery library 62 | which add a smart navigation bar on the right of the page. 63 | It improves a web page navigation and helps 64 | to visualize different sections 65 | of a document, an article,.. any web page.

    66 | 67 |

    Nav links are distributed proportionally to the page sections. 68 | See how your scrollbar "weds" these links :)

    69 | 70 |
    71 | 72 |

    You can easily use it on any website with the Bookmarklet.

    73 | 74 |

    Save this bookmark by drag and dropping it in your bar...

    75 | 76 |

    FlexibleNav bookmarklet

    77 | 78 |

    ...and click on it in any website!

    79 | 80 |
    81 | 82 |

    This page is a 3-in-one document!

    83 | 84 |
      85 |
    • First, it explains goals and possible usages of the library.
    • 86 |
    • Second, it presents a library usage 87 | (see this flexible box right panel?).
    • 88 |
    • Third, it shows the annotated JS code (thanks to docco)
    • 89 |
    90 | 91 |

    Download

    92 | 93 |

    You can also use it in your websites.

    94 | 95 |

    Download or fork me on github

    96 | 97 |

    Usages

    98 | 99 |

    Generation and Flexibility

    100 | 101 |

    The library can generate for you the nav sidebar content 102 | providing elements to display in navigation.

    103 | 104 |

    By default, it uses h1 to h3 elements to retrieve and locate 105 | the different sections of a document but you can override these 106 | elements selector.

    107 | 108 |

    You have usually two kind of selected elements to use:

    109 | 110 |
      111 |
    • Select a title. The link will take the title as text.
    • 112 |
    • Select any container and add a data-navtext attribute 113 | to define the title you want.
    • 114 |
    115 | 116 |

    Example: <div id="demo" data-navtext="Demo of the game">...

    117 | 118 |
      119 |
    • Simple usage used in this page

      120 | 121 |
      var nav = new FlexibleNavMaker().make().prependTo('body');
      122 | new FlexibleNav(nav);
      123 | 
    • 124 |
    • Selecting element you want

      125 | 126 |
      new FlexibleNavMaker(".navtitle");
      127 | 
    • 128 |
    129 | 130 |

    Using this code bellow, all nodes having this navtitle class 131 | will appear in the flexible nav panel.

    132 | 133 |

    Flexibility only

    134 | 135 |

    You can also create your own nav links to have more controls of used links. 136 | Conventionnally, you need to use 137 | a <nav /> element with a flexible-nav class and <a href="#myidtarget" /> kind of elements for links.

    138 | 139 |
      140 |
    • Add flexibility to an existing nav

      141 | 142 |
      new FlexibleNav($('#myNav'));
      143 | 
    • 144 |
    145 | 146 |

    (replace myNav by you nav id)

    147 |
    149 |
    /*!
    150 |  * Flexible Nav - 2011 - by @greweb
    151 |  * http://www.apache.org/licenses/LICENSE-2.0
    152 |  */
    153 |
    159 |
    160 | 161 |
    162 |

    The code

    163 |
    165 |
    (function($){
    166 |   "use strict";
    167 |
    173 |
    174 | 175 |
    176 |

    Utilities functions

    177 | 178 |

    Generate an unique number identifier

    179 |
    181 |
      var _uuid = 0;
    182 |   var uuid = function(){ return ++_uuid; };
    183 |
    189 |
    190 | 191 |
    192 |

    Template for creating a nav element with links.

    193 | 194 |
      195 |
    • links : an Array of { href, text }
    • 196 |
    197 |
    199 |
      var tmplNav = function(links) {
    200 |     var nav = $('<nav class="flexible-nav"><ul></ul></nav>');
    201 |     var lis = $.map(links, function(link){
    202 |       return $('<li><a href="'+link.href+'">'+link.text+'</a></li>')[0];
    203 |     });
    204 |     nav.find('ul').append(lis);
    205 |     return nav;
    206 |   }
    207 |   
    208 |
    214 |
    215 | 216 |
    217 |

    Retrieve a node pointed by an a element aEl hash href.

    218 |
    220 |
      var targetForLink = function(aEl) {
    221 |     var href = $(aEl).attr('href');
    222 |     if(href.substring(0,1) == '#') {
    223 |       var target = $(href);
    224 |       return target.size() ? target : null;
    225 |     }
    226 |     return null;
    227 |   }
    228 |
    234 |
    235 | 236 |
    237 |

    FlexibleNavMaker

    238 | 239 |

    Dynamically create a nav.

    240 | 241 |
      242 |
    • selector (optional) : selector for all nodes to add in nav. 243 | using h1, h2, h3 if not setted.
    • 244 |
    245 |
    247 |
      window.FlexibleNavMaker = function(selector) {
    248 |     var self = this;
    249 |     self.nodes = $(selector || 'h1,h2,h3');
    250 |     
    251 |
    257 |
    258 | 259 |
    260 |

    .make()

    261 | 262 |

    An instance of FlexibleNavMaker contains only a make method 263 | which create the nav element with nodes matching selector.

    264 |
    266 |
        self.make = function() {
    267 |       var links = self.nodes.map(function(){
    268 |         var node = $(this);
    269 |
    275 |
    276 | 277 |
    278 |

    Find the id or create a unique one

    279 |
    281 |
            var id = node.attr('id');
    282 |         if(!id) {
    283 |           while(!id) {
    284 |             id = 'n'+( uuid() );
    285 |             if($('#'+id).size()>0) id = null;
    286 |           }
    287 |           node.attr('id', id);
    288 |         }
    289 |
    295 |
    296 | 297 |
    298 |

    The text link is the data-navtext attribute or the node text.

    299 |
    301 |
            var text = node.attr('data-navtext') || node.text();
    302 | 	return { href: '#'+id, text: text };
    303 |       });
    304 |       return tmplNav(links);
    305 |     }
    306 |   }
    307 |
    313 |
    314 | 315 |
    316 |

    FlexibleNav

    317 | 318 |

    Make a nav element "flexible".

    319 | 320 |
      321 |
    • nav : a nav DOM element
    • 322 |
    323 |
    325 |
      window.FlexibleNav = function(nav) {
    326 |     var self = this;
    327 |     self.nav = $(nav);
    328 |
    334 |
    335 | 336 |
    337 |

    Init links adding some classes.

    338 | 339 |

    Set the target node name class. 340 | Example: class tnn-h1

    341 |
    343 |
        self.updateClasses = function() {
    344 |       self.nav.find('a').each(function(){
    345 |         var node = $(this);
    346 |         var target = targetForLink(node);
    347 |         if(target) {
    348 |            node.addClass('tnn-'+target[0].nodeName.toLowerCase());
    349 |         }
    350 |       });
    351 |     }
    352 |
    358 |
    359 | 360 |
    361 |

    .update()

    362 |
    364 |
        self.update = function() {
    365 |       var documentHeight = $(document).height();
    366 |       var windowHeight = $(window).height();
    367 |       
    368 |
    374 |
    375 | 376 |
    377 |

    Transform links into array of visible target top position and link node.

    378 |
    380 |
          var links = self.nav.find('a').map(function(){
    381 |         var node = $(this);
    382 |         var target = targetForLink(node);
    383 |         if(target==null || !target.is(':visible')) return null;
    384 |         return {
    385 |           top: target.offset().top,
    386 |           node: node
    387 |         };
    388 |       });
    389 |
    395 |
    396 | 397 |
    398 |

    Update nav link positions.

    399 |
    401 |
          $.each(links, function(i, link) {
    402 |          link.node.css('top', (100*link.top/documentHeight)+'%');
    403 |       });
    404 |
    410 |
    411 | 412 |
    413 |

    Update current section.

    414 |
    416 |
          var scrollTop = $(document).scrollTop();
    417 |       var closest = null;
    418 |       $.each(links, function(i, link){
    419 |         link.node.removeClass('current');
    420 |         if(closest==null || (link.top <= scrollTop+10 && link.top > closest.top))
    421 |           closest = link;
    422 |       });
    423 |       closest && closest.node.addClass('current');
    424 |     }
    425 |
    431 |
    432 | 433 |
    434 |

    Init

    435 | 436 |

    Bind scroll event and init nav.

    437 |
    439 |
        $(window).scroll(function(){
    440 |     	self.update();
    441 |     });
    442 |     self.updateClasses();
    443 |     self.update();
    444 |   }
    445 | }(jQuery));
    446 | 
    447 | 
    448 |
    453 |
    454 | 455 |
    456 | 457 |
    458 | 459 | 470 | 471 | 472 | -------------------------------------------------------------------------------- /dist/flexible-nav-1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gre/deprecated-flexible-nav/7b30b9e0ab45cfed8cb302560cf0d5c642ff5623/dist/flexible-nav-1.0.zip -------------------------------------------------------------------------------- /docco/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | output 3 | docs -------------------------------------------------------------------------------- /docco/Cakefile: -------------------------------------------------------------------------------- 1 | {spawn, exec} = require 'child_process' 2 | 3 | option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`' 4 | 5 | task 'build', 'continually build the docco library with --watch', -> 6 | coffee = spawn 'coffee', ['-cw', '-o', 'lib', 'src'] 7 | coffee.stdout.on 'data', (data) -> console.log data.toString().trim() 8 | 9 | task 'install', 'install the `docco` command into /usr/local (or --prefix)', (options) -> 10 | base = options.prefix or '/usr/local' 11 | lib = base + '/lib/docco' 12 | exec([ 13 | 'mkdir -p ' + lib 14 | 'cp -rf bin README resources vendor lib ' + lib 15 | 'ln -sf ' + lib + '/bin/docco ' + base + '/bin/docco' 16 | ].join(' && '), (err, stdout, stderr) -> 17 | if err then console.error stderr 18 | ) 19 | 20 | task 'doc', 'rebuild the Docco documentation', -> 21 | exec([ 22 | 'bin/docco src/docco.coffee' 23 | 'sed "s/docco.css/resources\\/docco.css/" < docs/docco.html > index.html' 24 | 'rm -r docs' 25 | ].join(' && '), (err) -> 26 | throw err if err 27 | ) 28 | -------------------------------------------------------------------------------- /docco/README: -------------------------------------------------------------------------------- 1 | ____ 2 | /\ _`\ 3 | \ \ \/\ \ ___ ___ ___ ___ 4 | \ \ \ \ \ / __`\ /'___\ /'___\ / __`\ 5 | \ \ \_\ \ /\ \ \ \ /\ \__/ /\ \__/ /\ \ \ \ 6 | \ \____/ \ \____/ \ \____\ \ \____\ \ \____/ 7 | \/___/ \/___/ \/____/ \/____/ \/___/ 8 | 9 | 10 | Docco is a quick-and-dirty, hundred-line-long, literate-programming-style 11 | documentation generator. For more information, see: 12 | 13 | http://jashkenas.github.com/docco/ -------------------------------------------------------------------------------- /docco/bin/docco: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | var fs = require('fs'); 5 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib'); 6 | 7 | process.ARGV = process.argv = process.argv.slice(2, process.argv.length); 8 | require(lib + '/docco.js'); 9 | -------------------------------------------------------------------------------- /docco/index.html: -------------------------------------------------------------------------------- 1 | docco.coffee

    docco.coffee

    Docco is a quick-and-dirty, hundred-line-long, literate-programming-style 2 | documentation generator. It produces HTML that displays your comments 3 | alongside your code. Comments are passed through 4 | Markdown, and code is 5 | passed through Pygments syntax highlighting. 6 | This page is the result of running Docco against its own source file.

    7 | 8 |

    If you install Docco, you can run it from the command-line:

    9 | 10 |
    docco src/*.coffee
     11 | 
    12 | 13 |

    ...will generate linked HTML documentation for the named source files, saving 14 | it into a docs folder.

    15 | 16 |

    The source for Docco is available on GitHub, 17 | and released under the MIT license.

    18 | 19 |

    To install Docco, first make sure you have Node.js, 20 | Pygments (install the latest dev version of Pygments 21 | from its Mercurial repo), and 22 | CoffeeScript. Then, with NPM:

    23 | 24 |
    sudo npm install docco
     25 | 
    26 | 27 |

    If Node.js doesn't run on your platform, or you'd prefer a more convenient 28 | package, get Rocco, the Ruby port that's 29 | available as a gem. If you're writing shell scripts, try 30 | Shocco, a port for the POSIX shell. 31 | Both are by Ryan Tomayko. If Python's more 32 | your speed, take a look at Nick Fitzgerald's 33 | Pycco.

    Main Documentation Generation Functions

    Generate the documentation for a source file by reading it in, splitting it 34 | up into comment/code sections, highlighting them for the appropriate language, 35 | and merging them into an HTML template.

    generate_documentation = (source, callback) ->
     36 |   fs.readFile source, "utf-8", (error, code) ->
     37 |     throw error if error
     38 |     sections = parse source, code
     39 |     highlight source, sections, ->
     40 |       generate_html source, sections
     41 |       callback()

    Given a string of source code, parse out each comment and the code that 42 | follows it, and create an individual section for it. 43 | Sections take the form:

    44 | 45 |
    {
     46 |   docs_text: ...
     47 |   docs_html: ...
     48 |   code_text: ...
     49 |   code_html: ...
     50 | }
     51 | 
    parse = (source, code) ->
     52 |   lines    = code.split '\n'
     53 |   sections = []
     54 |   language = get_language source
     55 |   has_code = docs_text = code_text = ''
     56 | 
     57 |   save = (docs, code) ->
     58 |     sections.push docs_text: docs, code_text: code
     59 | 
     60 |   for line in lines
     61 |     if line.match language.comment_matcher
     62 |       if not (line.match language.comment_filter)
     63 |         if has_code
     64 |           save docs_text, code_text
     65 |           has_code = docs_text = code_text = ''
     66 |         docs_text += line.replace(language.comment_matcher, '') + '\n'
     67 |     else
     68 |       has_code = yes
     69 |       code_text += line + '\n'
     70 |   save docs_text, code_text
     71 |   sections

    Highlights a single chunk of CoffeeScript code, using Pygments over stdio, 72 | and runs the text of its corresponding comment through Markdown, using the 73 | Github-flavored-Markdown modification of Showdown.js.

    74 | 75 |

    We process the entire file in a single call to Pygments by inserting little 76 | marker comments between each section and then splitting the result string 77 | wherever our markers occur.

    highlight = (source, sections, callback) ->
     78 |   language = get_language source
     79 |   pygments = spawn 'pygmentize', ['-l', language.name, '-f', 'html', '-O', 'encoding=utf-8']
     80 |   output   = ''
     81 |   pygments.stderr.addListener 'data',  (error)  ->
     82 |     console.error error if error
     83 |   pygments.stdout.addListener 'data', (result) ->
     84 |     output += result if result
     85 |   pygments.addListener 'exit', ->
     86 |     output = output.replace(highlight_start, '').replace(highlight_end, '')
     87 |     fragments = output.split language.divider_html
     88 |     for section, i in sections
     89 |       section.code_html = highlight_start + fragments[i] + highlight_end
     90 |       section.docs_html = showdown.makeHtml section.docs_text
     91 |     callback()
     92 |   pygments.stdin.write((section.code_text for section in sections).join(language.divider_text))
     93 |   pygments.stdin.end()

    Once all of the code is finished highlighting, we can generate the HTML file 94 | and write out the documentation. Pass the completed sections into the template 95 | found in resources/docco.jst

    generate_html = (source, sections) ->
     96 |   title = path.basename source
     97 |   dest  = destination source
     98 |   html  = docco_template {
     99 |     title: title, sections: sections, sources: sources, path: path, destination: destination
    100 |   }
    101 |   console.log "docco: #{source} -> #{dest}"
    102 |   fs.writeFile dest, html

    Helpers & Setup

    Require our external dependencies, including Showdown.js 103 | (the JavaScript implementation of Markdown).

    fs       = require 'fs'
    104 | path     = require 'path'
    105 | showdown = require('./../vendor/showdown').Showdown
    106 | {spawn, exec} = require 'child_process'

    A list of the languages that Docco supports, mapping the file extension to 107 | the name of the Pygments lexer and the symbol that indicates a comment. To 108 | add another language to Docco's repertoire, add it here.

    languages =
    109 |   '.coffee':
    110 |     name: 'coffee-script', symbol: '#'
    111 |   '.js':
    112 |     name: 'javascript', symbol: '//'
    113 |   '.rb':
    114 |     name: 'ruby', symbol: '#'
    115 |   '.py':
    116 |     name: 'python', symbol: '#'

    Build out the appropriate matchers and delimiters for each language.

    for ext, l of languages

    Does the line begin with a comment?

      l.comment_matcher = new RegExp('^\\s*' + l.symbol + '\\s?')

    Ignore hashbangs)

      l.comment_filter = new RegExp('^#![/]')

    The dividing token we feed into Pygments, to delimit the boundaries between 117 | sections.

      l.divider_text = '\n' + l.symbol + 'DIVIDER\n'

    The mirror of divider_text that we expect Pygments to return. We can split 118 | on this to recover the original sections. 119 | Note: the class is "c" for Python and "c1" for the other languages

      l.divider_html = new RegExp('\\n*<span class="c1?">' + l.symbol + 'DIVIDER<\\/span>\\n*')

    Get the current language we're documenting, based on the extension.

    get_language = (source) -> languages[path.extname(source)]

    Compute the destination HTML path for an input source file path. If the source 120 | is lib/example.coffee, the HTML will be at docs/example.html

    destination = (filepath) ->
    121 |   'docs/' + path.basename(filepath, path.extname(filepath)) + '.html'

    Ensure that the destination directory exists.

    ensure_directory = (callback) ->
    122 |   exec 'mkdir -p docs', -> callback()

    Micro-templating, originally by John Resig, borrowed by way of 123 | Underscore.js.

    template = (str) ->
    124 |   new Function 'obj',
    125 |     'var p=[],print=function(){p.push.apply(p,arguments);};' +
    126 |     'with(obj){p.push(\'' +
    127 |     str.replace(/[\r\t\n]/g, " ")
    128 |        .replace(/'(?=[^<]*%>)/g,"\t")
    129 |        .split("'").join("\\'")
    130 |        .split("\t").join("'")
    131 |        .replace(/<%=(.+?)%>/g, "',$1,'")
    132 |        .split('<%').join("');")
    133 |        .split('%>').join("p.push('") +
    134 |        "');}return p.join('');"

    Create the template that we will use to generate the Docco HTML page.

    docco_template  = template fs.readFileSync(__dirname + '/../resources/docco.jst').toString()

    The CSS styles we'd like to apply to the documentation.

    docco_styles    = fs.readFileSync(__dirname + '/../resources/resources/docco.css').toString()

    The start of each Pygments highlight block.

    highlight_start = '<div class="highlight"><pre>'

    The end of each Pygments highlight block.

    highlight_end   = '</pre></div>'

    Run the script. 135 | For each source file passed in as an argument, generate the documentation.

    sources = process.ARGV.sort()
    136 | if sources.length
    137 |   ensure_directory ->
    138 |     fs.writeFile 'docs/resources/docco.css', docco_styles
    139 |     files = sources.slice(0)
    140 |     next_file = -> generate_documentation files.shift(), next_file if files.length
    141 |     next_file()
    142 | 
    143 | 
    144 | -------------------------------------------------------------------------------- /docco/lib/docco.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var destination, docco_styles, docco_template, ensure_directory, exec, ext, fs, generate_documentation, generate_html, get_language, highlight, highlight_end, highlight_start, l, languages, parse, path, showdown, sources, spawn, template, _ref; 3 | generate_documentation = function(source, callback) { 4 | return fs.readFile(source, "utf-8", function(error, code) { 5 | var sections; 6 | if (error) { 7 | throw error; 8 | } 9 | sections = parse(source, code); 10 | return highlight(source, sections, function() { 11 | generate_html(source, sections); 12 | return callback(); 13 | }); 14 | }); 15 | }; 16 | parse = function(source, code) { 17 | var code_text, docs_text, has_code, language, line, lines, save, sections, _i, _len; 18 | lines = code.split('\n'); 19 | sections = []; 20 | language = get_language(source); 21 | has_code = docs_text = code_text = ''; 22 | save = function(docs, code) { 23 | return sections.push({ 24 | docs_text: docs, 25 | code_text: code 26 | }); 27 | }; 28 | for (_i = 0, _len = lines.length; _i < _len; _i++) { 29 | line = lines[_i]; 30 | if (line.match(language.comment_matcher) && !line.match(language.comment_filter)) { 31 | if (has_code) { 32 | save(docs_text, code_text); 33 | has_code = docs_text = code_text = ''; 34 | } 35 | docs_text += line.replace(language.comment_matcher, '') + '\n'; 36 | } else { 37 | has_code = true; 38 | code_text += line + '\n'; 39 | } 40 | } 41 | save(docs_text, code_text); 42 | return sections; 43 | }; 44 | highlight = function(source, sections, callback) { 45 | var language, output, pygments, section; 46 | language = get_language(source); 47 | pygments = spawn('pygmentize', ['-l', language.name, '-f', 'html']); 48 | output = ''; 49 | pygments.stderr.addListener('data', function(error) { 50 | if (error) { 51 | return console.error(error); 52 | } 53 | }); 54 | pygments.stdout.addListener('data', function(result) { 55 | if (result) { 56 | return output += result; 57 | } 58 | }); 59 | pygments.addListener('exit', function() { 60 | var fragments, i, section, _len; 61 | output = output.replace(highlight_start, '').replace(highlight_end, ''); 62 | fragments = output.split(language.divider_html); 63 | for (i = 0, _len = sections.length; i < _len; i++) { 64 | section = sections[i]; 65 | section.code_html = highlight_start + fragments[i] + highlight_end; 66 | section.docs_html = showdown.makeHtml(section.docs_text); 67 | } 68 | return callback(); 69 | }); 70 | pygments.stdin.write(((function() { 71 | var _i, _len, _results; 72 | _results = []; 73 | for (_i = 0, _len = sections.length; _i < _len; _i++) { 74 | section = sections[_i]; 75 | _results.push(section.code_text); 76 | } 77 | return _results; 78 | })()).join(language.divider_text)); 79 | return pygments.stdin.end(); 80 | }; 81 | generate_html = function(source, sections) { 82 | var dest, html, title; 83 | title = path.basename(source); 84 | dest = destination(source); 85 | html = docco_template({ 86 | title: title, 87 | sections: sections, 88 | sources: sources, 89 | path: path, 90 | destination: destination 91 | }); 92 | console.log("docco: " + source + " -> " + dest); 93 | return fs.writeFile(dest, html); 94 | }; 95 | fs = require('fs'); 96 | path = require('path'); 97 | showdown = require('./../vendor/showdown').Showdown; 98 | _ref = require('child_process'), spawn = _ref.spawn, exec = _ref.exec; 99 | languages = { 100 | '.coffee': { 101 | name: 'coffee-script', 102 | symbol: '#' 103 | }, 104 | '.js': { 105 | name: 'javascript', 106 | symbol: '//' 107 | }, 108 | '.rb': { 109 | name: 'ruby', 110 | symbol: '#' 111 | }, 112 | '.py': { 113 | name: 'python', 114 | symbol: '#' 115 | } 116 | }; 117 | for (ext in languages) { 118 | l = languages[ext]; 119 | l.comment_matcher = new RegExp('^\\s*' + l.symbol + '\\s?'); 120 | l.comment_filter = new RegExp('(^#![/]|^\\s*#\\{)'); 121 | l.divider_text = '\n' + l.symbol + 'DIVIDER\n'; 122 | l.divider_html = new RegExp('\\n*' + l.symbol + 'DIVIDER<\\/span>\\n*'); 123 | } 124 | get_language = function(source) { 125 | return languages[path.extname(source)]; 126 | }; 127 | destination = function(filepath) { 128 | return 'docs/' + path.basename(filepath, path.extname(filepath)) + '.html'; 129 | }; 130 | ensure_directory = function(dir, callback) { 131 | return exec("mkdir -p " + dir, function() { 132 | return callback(); 133 | }); 134 | }; 135 | template = function(str) { 136 | return new Function('obj', 'var p=[],print=function(){p.push.apply(p,arguments);};' + 'with(obj){p.push(\'' + str.replace(/[\r\t\n]/g, "\\n").replace(/'(?=[^<]*%>)/g, "\t").split("'").join("\\'").split("\t").join("'").replace(/<%=(.+?)%>/g, "',$1,'").split('<%').join("');").split('%>').join("p.push('") + "');}return p.join('');"); 137 | }; 138 | docco_template = template(fs.readFileSync(__dirname + '/../resources/docco.jst').toString()); 139 | docco_styles = fs.readFileSync(__dirname + '/../resources/docco.css').toString(); 140 | highlight_start = '
    ';
    141 |   highlight_end = '
    '; 142 | sources = process.ARGV.sort(); 143 | if (sources.length) { 144 | ensure_directory('docs', function() { 145 | var files, next_file; 146 | fs.writeFile('docs/docco.css', docco_styles); 147 | files = sources.slice(0); 148 | next_file = function() { 149 | if (files.length) { 150 | return generate_documentation(files.shift(), next_file); 151 | } 152 | }; 153 | return next_file(); 154 | }); 155 | } 156 | }).call(this); 157 | -------------------------------------------------------------------------------- /docco/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docco", 3 | "description": "The Quick and Dirty Literate Programming Documentation Generator", 4 | "keywords": ["documentation", "docs", "generator", "coffeescript"], 5 | "author": "Jeremy Ashkenas", 6 | "version": "0.3.0", 7 | "licenses": [{ 8 | "type": "MIT", 9 | "url": "http://opensource.org/licenses/mit-license.php" 10 | }], 11 | "engines": { 12 | "node": ">=0.2.0" 13 | }, 14 | "directories": { 15 | "lib" : "./lib" 16 | }, 17 | "main" : "./lib/docco", 18 | "bin": { 19 | "docco": "./bin/docco" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docco/resources/docco.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Layout and Typography ----------------------------*/ 2 | body { 3 | font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 4 | font-size: 15px; 5 | line-height: 22px; 6 | color: #252519; 7 | margin: 0; padding: 0; 8 | } 9 | a { 10 | color: #261a3b; 11 | } 12 | a:visited { 13 | color: #261a3b; 14 | } 15 | p { 16 | margin: 0 0 15px 0; 17 | } 18 | h1, h2, h3, h4, h5, h6 { 19 | margin: 0px 0 15px 0; 20 | } 21 | h1 { 22 | margin-top: 40px; 23 | } 24 | #container { 25 | position: relative; 26 | } 27 | #background { 28 | position: fixed; 29 | top: 0; left: 525px; right: 0; bottom: 0; 30 | background: #f5f5ff; 31 | border-left: 1px solid #e5e5ee; 32 | z-index: -1; 33 | } 34 | #jump_to, #jump_page { 35 | background: white; 36 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 37 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 38 | font: 10px Arial; 39 | text-transform: uppercase; 40 | cursor: pointer; 41 | text-align: right; 42 | } 43 | #jump_to, #jump_wrapper { 44 | position: fixed; 45 | right: 0; top: 0; 46 | padding: 5px 10px; 47 | } 48 | #jump_wrapper { 49 | padding: 0; 50 | display: none; 51 | } 52 | #jump_to:hover #jump_wrapper { 53 | display: block; 54 | } 55 | #jump_page { 56 | padding: 5px 0 3px; 57 | margin: 0 0 25px 25px; 58 | } 59 | #jump_page .source { 60 | display: block; 61 | padding: 5px 10px; 62 | text-decoration: none; 63 | border-top: 1px solid #eee; 64 | } 65 | #jump_page .source:hover { 66 | background: #f5f5ff; 67 | } 68 | #jump_page .source:first-child { 69 | } 70 | table td { 71 | border: 0; 72 | outline: 0; 73 | } 74 | td.docs, th.docs { 75 | max-width: 450px; 76 | min-width: 450px; 77 | min-height: 5px; 78 | padding: 10px 25px 1px 50px; 79 | overflow-x: hidden; 80 | vertical-align: top; 81 | text-align: left; 82 | } 83 | .docs pre { 84 | margin: 15px 0 15px; 85 | padding-left: 15px; 86 | } 87 | .docs p tt, .docs p code { 88 | background: #f8f8ff; 89 | border: 1px solid #dedede; 90 | font-size: 12px; 91 | padding: 0 0.2em; 92 | } 93 | .pilwrap { 94 | position: relative; 95 | } 96 | .pilcrow { 97 | font: 12px Arial; 98 | text-decoration: none; 99 | color: #454545; 100 | position: absolute; 101 | top: 3px; left: -20px; 102 | padding: 1px 2px; 103 | opacity: 0; 104 | -webkit-transition: opacity 0.2s linear; 105 | } 106 | td.docs:hover .pilcrow { 107 | opacity: 1; 108 | } 109 | td.code, th.code { 110 | padding: 14px 15px 16px 25px; 111 | width: 100%; 112 | vertical-align: top; 113 | background: #f5f5ff; 114 | border-left: 1px solid #e5e5ee; 115 | } 116 | pre, tt, code { 117 | font-size: 12px; line-height: 18px; 118 | font-family: Monaco, Consolas, "Lucida Console", monospace; 119 | margin: 0; padding: 0; 120 | } 121 | 122 | 123 | /*---------------------- Syntax Highlighting -----------------------------*/ 124 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 125 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 126 | body .hll { background-color: #ffffcc } 127 | body .c { color: #408080; font-style: italic } /* Comment */ 128 | body .err { border: 1px solid #FF0000 } /* Error */ 129 | body .k { color: #954121 } /* Keyword */ 130 | body .o { color: #666666 } /* Operator */ 131 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 132 | body .cp { color: #BC7A00 } /* Comment.Preproc */ 133 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 134 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 135 | body .gd { color: #A00000 } /* Generic.Deleted */ 136 | body .ge { font-style: italic } /* Generic.Emph */ 137 | body .gr { color: #FF0000 } /* Generic.Error */ 138 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 139 | body .gi { color: #00A000 } /* Generic.Inserted */ 140 | body .go { color: #808080 } /* Generic.Output */ 141 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 142 | body .gs { font-weight: bold } /* Generic.Strong */ 143 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 144 | body .gt { color: #0040D0 } /* Generic.Traceback */ 145 | body .kc { color: #954121 } /* Keyword.Constant */ 146 | body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 147 | body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 148 | body .kp { color: #954121 } /* Keyword.Pseudo */ 149 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 150 | body .kt { color: #B00040 } /* Keyword.Type */ 151 | body .m { color: #666666 } /* Literal.Number */ 152 | body .s { color: #219161 } /* Literal.String */ 153 | body .na { color: #7D9029 } /* Name.Attribute */ 154 | body .nb { color: #954121 } /* Name.Builtin */ 155 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 156 | body .no { color: #880000 } /* Name.Constant */ 157 | body .nd { color: #AA22FF } /* Name.Decorator */ 158 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 159 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 160 | body .nf { color: #0000FF } /* Name.Function */ 161 | body .nl { color: #A0A000 } /* Name.Label */ 162 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 163 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 164 | body .nv { color: #19469D } /* Name.Variable */ 165 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 166 | body .w { color: #bbbbbb } /* Text.Whitespace */ 167 | body .mf { color: #666666 } /* Literal.Number.Float */ 168 | body .mh { color: #666666 } /* Literal.Number.Hex */ 169 | body .mi { color: #666666 } /* Literal.Number.Integer */ 170 | body .mo { color: #666666 } /* Literal.Number.Oct */ 171 | body .sb { color: #219161 } /* Literal.String.Backtick */ 172 | body .sc { color: #219161 } /* Literal.String.Char */ 173 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 174 | body .s2 { color: #219161 } /* Literal.String.Double */ 175 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 176 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 177 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 178 | body .sx { color: #954121 } /* Literal.String.Other */ 179 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 180 | body .s1 { color: #219161 } /* Literal.String.Single */ 181 | body .ss { color: #19469D } /* Literal.String.Symbol */ 182 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 183 | body .vc { color: #19469D } /* Name.Variable.Class */ 184 | body .vg { color: #19469D } /* Name.Variable.Global */ 185 | body .vi { color: #19469D } /* Name.Variable.Instance */ 186 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docco/resources/docco.jst: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Flexible Nav jQuery library 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 31 | 32 | 33 |
    34 |
    35 |
    36 | 37 | 39 |
    40 | 41 | 42 | <% if (sources.length > 1) { %> 43 |
    44 | Jump To … 45 |
    46 |
    47 | <% for (var i=0, l=sources.length; i 48 | <% var source = sources[i]; %> 49 | 50 | <%= path.basename(source) %> 51 | 52 | <% } %> 53 |
    54 |
    55 |
    56 | <% } %> 57 | 58 | 59 | <% for (var i=0, l=sections.length; i 60 | <% var section = sections[i]; %> 61 | 62 | 68 | 71 | 72 | <% } %> 73 | 74 |
    63 |
    64 | 65 |
    66 | <%= section.docs_html %> 67 |
    69 | <%= section.code_html %> 70 |
    75 |
    76 | 77 |
    78 | 79 |
    80 | 81 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /docco/src/docco.coffee: -------------------------------------------------------------------------------- 1 | # **Docco** is a quick-and-dirty, hundred-line-long, literate-programming-style 2 | # documentation generator. It produces HTML that displays your comments 3 | # alongside your code. Comments are passed through 4 | # [Markdown](http://daringfireball.net/projects/markdown/syntax), and code is 5 | # passed through [Pygments](http://pygments.org/) syntax highlighting. 6 | # This page is the result of running Docco against its own source file. 7 | # 8 | # If you install Docco, you can run it from the command-line: 9 | # 10 | # docco src/*.coffee 11 | # 12 | # ...will generate linked HTML documentation for the named source files, saving 13 | # it into a `docs` folder. 14 | # 15 | # The [source for Docco](http://github.com/jashkenas/docco) is available on GitHub, 16 | # and released under the MIT license. 17 | # 18 | # To install Docco, first make sure you have [Node.js](http://nodejs.org/), 19 | # [Pygments](http://pygments.org/) (install the latest dev version of Pygments 20 | # from [its Mercurial repo](http://dev.pocoo.org/hg/pygments-main)), and 21 | # [CoffeeScript](http://coffeescript.org/). Then, with NPM: 22 | # 23 | # sudo npm install docco 24 | # 25 | #### Partners in Crime: 26 | # 27 | # * If **Node.js** doesn't run on your platform, or you'd prefer a more convenient 28 | # package, get [Ryan Tomayko](http://github.com/rtomayko)'s 29 | # [Rocco](http://rtomayko.github.com/rocco/), the Ruby port that's available as a gem. 30 | # 31 | # * If you're writing shell scripts, try 32 | # [Shocco](http://rtomayko.github.com/shocco/), a port for the **POSIX shell**, 33 | # also by Mr. Tomayko. 34 | # 35 | # * If Python's more your speed, take a look at 36 | # [Nick Fitzgerald](http://github.com/fitzgen)'s [Pycco](http://fitzgen.github.com/pycco/). 37 | # 38 | # * For **Clojure** fans, [Fogus](http://blog.fogus.me/)'s 39 | # [Marginalia](http://fogus.me/fun/marginalia/) is a bit of a departure from 40 | # "quick-and-dirty", but it'll get the job done. 41 | # 42 | # * **Lua** enthusiasts can get their fix with 43 | # [Robert Gieseke](https://github.com/rgieseke)'s [Locco](http://rgieseke.github.com/locco/). 44 | # 45 | # * And if you happen to be a **.NET** 46 | # aficionado, check out [Don Wilson](https://github.com/dontangg)'s 47 | # [Nocco](http://dontangg.github.com/nocco/). 48 | 49 | #### Main Documentation Generation Functions 50 | 51 | # Generate the documentation for a source file by reading it in, splitting it 52 | # up into comment/code sections, highlighting them for the appropriate language, 53 | # and merging them into an HTML template. 54 | generate_documentation = (source, callback) -> 55 | fs.readFile source, "utf-8", (error, code) -> 56 | throw error if error 57 | sections = parse source, code 58 | highlight source, sections, -> 59 | generate_html source, sections 60 | callback() 61 | 62 | # Given a string of source code, parse out each comment and the code that 63 | # follows it, and create an individual **section** for it. 64 | # Sections take the form: 65 | # 66 | # { 67 | # docs_text: ... 68 | # docs_html: ... 69 | # code_text: ... 70 | # code_html: ... 71 | # } 72 | # 73 | parse = (source, code) -> 74 | lines = code.split '\n' 75 | sections = [] 76 | language = get_language source 77 | has_code = docs_text = code_text = '' 78 | 79 | save = (docs, code) -> 80 | sections.push docs_text: docs, code_text: code 81 | 82 | for line in lines 83 | if line.match(language.comment_matcher) and not line.match(language.comment_filter) 84 | if has_code 85 | save docs_text, code_text 86 | has_code = docs_text = code_text = '' 87 | docs_text += line.replace(language.comment_matcher, '') + '\n' 88 | else 89 | has_code = yes 90 | code_text += line + '\n' 91 | save docs_text, code_text 92 | sections 93 | 94 | # Highlights a single chunk of CoffeeScript code, using **Pygments** over stdio, 95 | # and runs the text of its corresponding comment through **Markdown**, using the 96 | # **Github-flavored-Markdown** modification of [Showdown.js](http://attacklab.net/showdown/). 97 | # 98 | # We process the entire file in a single call to Pygments by inserting little 99 | # marker comments between each section and then splitting the result string 100 | # wherever our markers occur. 101 | highlight = (source, sections, callback) -> 102 | language = get_language source 103 | pygments = spawn 'pygmentize', ['-l', language.name, '-f', 'html'] 104 | output = '' 105 | pygments.stderr.addListener 'data', (error) -> 106 | console.error error if error 107 | pygments.stdout.addListener 'data', (result) -> 108 | output += result if result 109 | pygments.addListener 'exit', -> 110 | output = output.replace(highlight_start, '').replace(highlight_end, '') 111 | fragments = output.split language.divider_html 112 | for section, i in sections 113 | section.code_html = highlight_start + fragments[i] + highlight_end 114 | section.docs_html = showdown.makeHtml section.docs_text 115 | callback() 116 | pygments.stdin.write((section.code_text for section in sections).join(language.divider_text)) 117 | pygments.stdin.end() 118 | 119 | # Once all of the code is finished highlighting, we can generate the HTML file 120 | # and write out the documentation. Pass the completed sections into the template 121 | # found in `resources/docco.jst` 122 | generate_html = (source, sections) -> 123 | title = path.basename source 124 | dest = destination source 125 | html = docco_template { 126 | title: title, sections: sections, sources: sources, path: path, destination: destination 127 | } 128 | console.log "docco: #{source} -> #{dest}" 129 | fs.writeFile dest, html 130 | 131 | #### Helpers & Setup 132 | 133 | # Require our external dependencies, including **Showdown.js** 134 | # (the JavaScript implementation of Markdown). 135 | fs = require 'fs' 136 | path = require 'path' 137 | showdown = require('./../vendor/showdown').Showdown 138 | {spawn, exec} = require 'child_process' 139 | 140 | # A list of the languages that Docco supports, mapping the file extension to 141 | # the name of the Pygments lexer and the symbol that indicates a comment. To 142 | # add another language to Docco's repertoire, add it here. 143 | languages = 144 | '.coffee': 145 | name: 'coffee-script', symbol: '#' 146 | '.js': 147 | name: 'javascript', symbol: '//' 148 | '.rb': 149 | name: 'ruby', symbol: '#' 150 | '.py': 151 | name: 'python', symbol: '#' 152 | 153 | # Build out the appropriate matchers and delimiters for each language. 154 | for ext, l of languages 155 | 156 | # Does the line begin with a comment? 157 | l.comment_matcher = new RegExp('^\\s*' + l.symbol + '\\s?') 158 | 159 | # Ignore [hashbangs](http://en.wikipedia.org/wiki/Shebang_(Unix)) 160 | # and interpolations... 161 | l.comment_filter = new RegExp('(^#![/]|^\\s*#\\{)') 162 | 163 | # The dividing token we feed into Pygments, to delimit the boundaries between 164 | # sections. 165 | l.divider_text = '\n' + l.symbol + 'DIVIDER\n' 166 | 167 | # The mirror of `divider_text` that we expect Pygments to return. We can split 168 | # on this to recover the original sections. 169 | # Note: the class is "c" for Python and "c1" for the other languages 170 | l.divider_html = new RegExp('\\n*' + l.symbol + 'DIVIDER<\\/span>\\n*') 171 | 172 | # Get the current language we're documenting, based on the extension. 173 | get_language = (source) -> languages[path.extname(source)] 174 | 175 | # Compute the destination HTML path for an input source file path. If the source 176 | # is `lib/example.coffee`, the HTML will be at `docs/example.html` 177 | destination = (filepath) -> 178 | 'docs/' + path.basename(filepath, path.extname(filepath)) + '.html' 179 | 180 | # Ensure that the destination directory exists. 181 | ensure_directory = (dir, callback) -> 182 | exec "mkdir -p #{dir}", -> callback() 183 | 184 | # Micro-templating, originally by John Resig, borrowed by way of 185 | # [Underscore.js](http://documentcloud.github.com/underscore/). 186 | template = (str) -> 187 | new Function 'obj', 188 | 'var p=[],print=function(){p.push.apply(p,arguments);};' + 189 | 'with(obj){p.push(\'' + 190 | str.replace(/[\r\t\n]/g, "\\n") 191 | .replace(/'(?=[^<]*%>)/g,"\t") 192 | .split("'").join("\\'") 193 | .split("\t").join("'") 194 | .replace(/<%=(.+?)%>/g, "',$1,'") 195 | .split('<%').join("');") 196 | .split('%>').join("p.push('") + 197 | "');}return p.join('');" 198 | 199 | # Create the template that we will use to generate the Docco HTML page. 200 | docco_template = template fs.readFileSync(__dirname + '/../resources/docco.jst').toString() 201 | 202 | # The CSS styles we'd like to apply to the documentation. 203 | docco_styles = fs.readFileSync(__dirname + '/../resources/docco.css').toString() 204 | 205 | # The start of each Pygments highlight block. 206 | highlight_start = '
    '
    207 | 
    208 | # The end of each Pygments highlight block.
    209 | highlight_end   = '
    ' 210 | 211 | # Run the script. 212 | # For each source file passed in as an argument, generate the documentation. 213 | sources = process.ARGV.sort() 214 | if sources.length 215 | ensure_directory 'docs', -> 216 | fs.writeFile 'docs/docco.css', docco_styles 217 | files = sources.slice(0) 218 | next_file = -> generate_documentation files.shift(), next_file if files.length 219 | next_file() 220 | 221 | -------------------------------------------------------------------------------- /docco/vendor/showdown.js: -------------------------------------------------------------------------------- 1 | // 2 | // showdown.js -- A javascript port of Markdown. 3 | // 4 | // Copyright (c) 2007 John Fraser. 5 | // 6 | // Original Markdown Copyright (c) 2004-2005 John Gruber 7 | // 8 | // 9 | // Redistributable under a BSD-style open source license. 10 | // See license.txt for more information. 11 | // 12 | // The full source distribution is at: 13 | // 14 | // A A L 15 | // T C A 16 | // T K B 17 | // 18 | // 19 | // 20 | 21 | // 22 | // Wherever possible, Showdown is a straight, line-by-line port 23 | // of the Perl version of Markdown. 24 | // 25 | // This is not a normal parser design; it's basically just a 26 | // series of string substitutions. It's hard to read and 27 | // maintain this way, but keeping Showdown close to the original 28 | // design makes it easier to port new features. 29 | // 30 | // More importantly, Showdown behaves like markdown.pl in most 31 | // edge cases. So web applications can do client-side preview 32 | // in Javascript, and then build identical HTML on the server. 33 | // 34 | // This port needs the new RegExp functionality of ECMA 262, 35 | // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers 36 | // should do fine. Even with the new regular expression features, 37 | // We do a lot of work to emulate Perl's regex functionality. 38 | // The tricky changes in this file mostly have the "attacklab:" 39 | // label. Major or self-explanatory changes don't. 40 | // 41 | // Smart diff tools like Araxis Merge will be able to match up 42 | // this file with markdown.pl in a useful way. A little tweaking 43 | // helps: in a copy of markdown.pl, replace "#" with "//" and 44 | // replace "$text" with "text". Be sure to ignore whitespace 45 | // and line endings. 46 | // 47 | 48 | 49 | // 50 | // Showdown usage: 51 | // 52 | // var text = "Markdown *rocks*."; 53 | // 54 | // var converter = new Showdown.converter(); 55 | // var html = converter.makeHtml(text); 56 | // 57 | // alert(html); 58 | // 59 | // Note: move the sample code to the bottom of this 60 | // file before uncommenting it. 61 | // 62 | 63 | 64 | // 65 | // Showdown namespace 66 | // 67 | var Showdown = {}; 68 | 69 | // 70 | // converter 71 | // 72 | // Wraps all "globals" so that the only thing 73 | // exposed is makeHtml(). 74 | // 75 | Showdown.converter = function() { 76 | 77 | // 78 | // Globals: 79 | // 80 | 81 | // Global hashes, used by various utility routines 82 | var g_urls; 83 | var g_titles; 84 | var g_html_blocks; 85 | 86 | // Used to track when we're inside an ordered or unordered list 87 | // (see _ProcessListItems() for details): 88 | var g_list_level = 0; 89 | 90 | 91 | this.makeHtml = function(text) { 92 | // 93 | // Main function. The order in which other subs are called here is 94 | // essential. Link and image substitutions need to happen before 95 | // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the 96 | // and tags get encoded. 97 | // 98 | 99 | // Clear the global hashes. If we don't clear these, you get conflicts 100 | // from other articles when generating a page which contains more than 101 | // one article (e.g. an index page that shows the N most recent 102 | // articles): 103 | g_urls = new Array(); 104 | g_titles = new Array(); 105 | g_html_blocks = new Array(); 106 | 107 | // attacklab: Replace ~ with ~T 108 | // This lets us use tilde as an escape char to avoid md5 hashes 109 | // The choice of character is arbitray; anything that isn't 110 | // magic in Markdown will work. 111 | text = text.replace(/~/g,"~T"); 112 | 113 | // attacklab: Replace $ with ~D 114 | // RegExp interprets $ as a special character 115 | // when it's in a replacement string 116 | text = text.replace(/\$/g,"~D"); 117 | 118 | // Standardize line endings 119 | text = text.replace(/\r\n/g,"\n"); // DOS to Unix 120 | text = text.replace(/\r/g,"\n"); // Mac to Unix 121 | 122 | // Make sure text begins and ends with a couple of newlines: 123 | text = "\n\n" + text + "\n\n"; 124 | 125 | // Convert all tabs to spaces. 126 | text = _Detab(text); 127 | 128 | // Strip any lines consisting only of spaces and tabs. 129 | // This makes subsequent regexen easier to write, because we can 130 | // match consecutive blank lines with /\n+/ instead of something 131 | // contorted like /[ \t]*\n+/ . 132 | text = text.replace(/^[ \t]+$/mg,""); 133 | 134 | // Turn block-level HTML blocks into hash entries 135 | text = _HashHTMLBlocks(text); 136 | 137 | // Strip link definitions, store in hashes. 138 | text = _StripLinkDefinitions(text); 139 | 140 | text = _RunBlockGamut(text); 141 | 142 | text = _UnescapeSpecialChars(text); 143 | 144 | // attacklab: Restore dollar signs 145 | text = text.replace(/~D/g,"$$"); 146 | 147 | // attacklab: Restore tildes 148 | text = text.replace(/~T/g,"~"); 149 | 150 | return text; 151 | } 152 | 153 | 154 | var _StripLinkDefinitions = function(text) { 155 | // 156 | // Strips link definitions from text, stores the URLs and titles in 157 | // hash references. 158 | // 159 | 160 | // Link defs are in the form: ^[id]: url "optional title" 161 | 162 | /* 163 | var text = text.replace(/ 164 | ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 165 | [ \t]* 166 | \n? // maybe *one* newline 167 | [ \t]* 168 | ? // url = $2 169 | [ \t]* 170 | \n? // maybe one newline 171 | [ \t]* 172 | (?: 173 | (\n*) // any lines skipped = $3 attacklab: lookbehind removed 174 | ["(] 175 | (.+?) // title = $4 176 | [")] 177 | [ \t]* 178 | )? // title is optional 179 | (?:\n+|$) 180 | /gm, 181 | function(){...}); 182 | */ 183 | var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm, 184 | function (wholeMatch,m1,m2,m3,m4) { 185 | m1 = m1.toLowerCase(); 186 | g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive 187 | if (m3) { 188 | // Oops, found blank lines, so it's not a title. 189 | // Put back the parenthetical statement we stole. 190 | return m3+m4; 191 | } else if (m4) { 192 | g_titles[m1] = m4.replace(/"/g,"""); 193 | } 194 | 195 | // Completely remove the definition from the text 196 | return ""; 197 | } 198 | ); 199 | 200 | return text; 201 | } 202 | 203 | 204 | var _HashHTMLBlocks = function(text) { 205 | // attacklab: Double up blank lines to reduce lookaround 206 | text = text.replace(/\n/g,"\n\n"); 207 | 208 | // Hashify HTML blocks: 209 | // We only want to do this for block-level HTML tags, such as headers, 210 | // lists, and tables. That's because we still want to wrap

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

    219 | //
    220 | // tags for inner block must be indented. 221 | //
    222 | //
    223 | // 224 | // The outermost tags must start at the left margin for this to match, and 225 | // the inner nested divs must be indented. 226 | // We need to do this before the next, more liberal match, because the next 227 | // match will start at the first `
    ` and stop at the first `
    `. 228 | 229 | // attacklab: This regex can be expensive when it fails. 230 | /* 231 | var text = text.replace(/ 232 | ( // save in $1 233 | ^ // start of line (with /m) 234 | <($block_tags_a) // start tag = $2 235 | \b // word break 236 | // attacklab: hack around khtml/pcre bug... 237 | [^\r]*?\n // any number of lines, minimally matching 238 | // the matching end tag 239 | [ \t]* // trailing spaces/tabs 240 | (?=\n+) // followed by a newline 241 | ) // attacklab: there are sentinel newlines at end of document 242 | /gm,function(){...}}; 243 | */ 244 | 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); 245 | 246 | // 247 | // Now match more liberally, simply from `\n` to `\n` 248 | // 249 | 250 | /* 251 | var text = text.replace(/ 252 | ( // save in $1 253 | ^ // start of line (with /m) 254 | <($block_tags_b) // start tag = $2 255 | \b // word break 256 | // attacklab: hack around khtml/pcre bug... 257 | [^\r]*? // any number of lines, minimally matching 258 | .* // the matching end tag 259 | [ \t]* // trailing spaces/tabs 260 | (?=\n+) // followed by a newline 261 | ) // attacklab: there are sentinel newlines at end of document 262 | /gm,function(){...}}; 263 | */ 264 | 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); 265 | 266 | // Special case just for
    . It was easier to make a special case than 267 | // to make the other regex more complicated. 268 | 269 | /* 270 | text = text.replace(/ 271 | ( // save in $1 272 | \n\n // Starting after a blank line 273 | [ ]{0,3} 274 | (<(hr) // start tag = $2 275 | \b // word break 276 | ([^<>])*? // 277 | \/?>) // the matching end tag 278 | [ \t]* 279 | (?=\n{2,}) // followed by a blank line 280 | ) 281 | /g,hashElement); 282 | */ 283 | text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); 284 | 285 | // Special case for standalone HTML comments: 286 | 287 | /* 288 | text = text.replace(/ 289 | ( // save in $1 290 | \n\n // Starting after a blank line 291 | [ ]{0,3} // attacklab: g_tab_width - 1 292 | 295 | [ \t]* 296 | (?=\n{2,}) // followed by a blank line 297 | ) 298 | /g,hashElement); 299 | */ 300 | text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); 301 | 302 | // PHP and ASP-style processor instructions ( and <%...%>) 303 | 304 | /* 305 | text = text.replace(/ 306 | (?: 307 | \n\n // Starting after a blank line 308 | ) 309 | ( // save in $1 310 | [ ]{0,3} // attacklab: g_tab_width - 1 311 | (?: 312 | <([?%]) // $2 313 | [^\r]*? 314 | \2> 315 | ) 316 | [ \t]* 317 | (?=\n{2,}) // followed by a blank line 318 | ) 319 | /g,hashElement); 320 | */ 321 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); 322 | 323 | // attacklab: Undo double lines (see comment at top of this function) 324 | text = text.replace(/\n\n/g,"\n"); 325 | return text; 326 | } 327 | 328 | var hashElement = function(wholeMatch,m1) { 329 | var blockText = m1; 330 | 331 | // Undo double lines 332 | blockText = blockText.replace(/\n\n/g,"\n"); 333 | blockText = blockText.replace(/^\n/,""); 334 | 335 | // strip trailing blank lines 336 | blockText = blockText.replace(/\n+$/g,""); 337 | 338 | // Replace the element text with a marker ("~KxK" where x is its key) 339 | blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n"; 340 | 341 | return blockText; 342 | }; 343 | 344 | var _RunBlockGamut = function(text) { 345 | // 346 | // These are all the transformations that form block-level 347 | // tags like paragraphs, headers, and list items. 348 | // 349 | text = _DoHeaders(text); 350 | 351 | // Do Horizontal Rules: 352 | var key = hashBlock("
    "); 353 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); 354 | text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); 355 | text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); 356 | 357 | text = _DoLists(text); 358 | text = _DoCodeBlocks(text); 359 | text = _DoBlockQuotes(text); 360 | 361 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that 362 | // was to escape raw HTML in the original Markdown source. This time, 363 | // we're escaping the markup we've just created, so that we don't wrap 364 | //

    tags around block-level tags. 365 | text = _HashHTMLBlocks(text); 366 | text = _FormParagraphs(text); 367 | 368 | return text; 369 | } 370 | 371 | 372 | var _RunSpanGamut = function(text) { 373 | // 374 | // These are all the transformations that occur *within* block-level 375 | // tags like paragraphs, headers, and list items. 376 | // 377 | 378 | text = _DoCodeSpans(text); 379 | text = _EscapeSpecialCharsWithinTagAttributes(text); 380 | text = _EncodeBackslashEscapes(text); 381 | 382 | // Process anchor and image tags. Images must come first, 383 | // because ![foo][f] looks like an anchor. 384 | text = _DoImages(text); 385 | text = _DoAnchors(text); 386 | 387 | // Make links out of things like `` 388 | // Must come after _DoAnchors(), because you can use < and > 389 | // delimiters in inline links like [this](). 390 | text = _DoAutoLinks(text); 391 | text = _EncodeAmpsAndAngles(text); 392 | text = _DoItalicsAndBold(text); 393 | 394 | // Do hard breaks: 395 | text = text.replace(/ +\n/g,"
    \n"); 396 | 397 | return text; 398 | } 399 | 400 | var _EscapeSpecialCharsWithinTagAttributes = function(text) { 401 | // 402 | // Within tags -- meaning between < and > -- encode [\ ` * _] so they 403 | // don't conflict with their use in Markdown for code, italics and strong. 404 | // 405 | 406 | // Build a regex to find HTML tags and comments. See Friedl's 407 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. 408 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; 409 | 410 | text = text.replace(regex, function(wholeMatch) { 411 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); 412 | tag = escapeCharacters(tag,"\\`*_"); 413 | return tag; 414 | }); 415 | 416 | return text; 417 | } 418 | 419 | var _DoAnchors = function(text) { 420 | // 421 | // Turn Markdown link shortcuts into XHTML
    tags. 422 | // 423 | // 424 | // First, handle reference-style links: [link text] [id] 425 | // 426 | 427 | /* 428 | text = text.replace(/ 429 | ( // wrap whole match in $1 430 | \[ 431 | ( 432 | (?: 433 | \[[^\]]*\] // allow brackets nested one level 434 | | 435 | [^\[] // or anything else 436 | )* 437 | ) 438 | \] 439 | 440 | [ ]? // one optional space 441 | (?:\n[ ]*)? // one optional newline followed by spaces 442 | 443 | \[ 444 | (.*?) // id = $3 445 | \] 446 | )()()()() // pad remaining backreferences 447 | /g,_DoAnchors_callback); 448 | */ 449 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); 450 | 451 | // 452 | // Next, inline-style links: [link text](url "optional title") 453 | // 454 | 455 | /* 456 | text = text.replace(/ 457 | ( // wrap whole match in $1 458 | \[ 459 | ( 460 | (?: 461 | \[[^\]]*\] // allow brackets nested one level 462 | | 463 | [^\[\]] // or anything else 464 | ) 465 | ) 466 | \] 467 | \( // literal paren 468 | [ \t]* 469 | () // no id, so leave $3 empty 470 | ? // href = $4 471 | [ \t]* 472 | ( // $5 473 | (['"]) // quote char = $6 474 | (.*?) // Title = $7 475 | \6 // matching quote 476 | [ \t]* // ignore any spaces/tabs between closing quote and ) 477 | )? // title is optional 478 | \) 479 | ) 480 | /g,writeAnchorTag); 481 | */ 482 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); 483 | 484 | // 485 | // Last, handle reference-style shortcuts: [link text] 486 | // These must come last in case you've also got [link test][1] 487 | // or [link test](/foo) 488 | // 489 | 490 | /* 491 | text = text.replace(/ 492 | ( // wrap whole match in $1 493 | \[ 494 | ([^\[\]]+) // link text = $2; can't contain '[' or ']' 495 | \] 496 | )()()()()() // pad rest of backreferences 497 | /g, writeAnchorTag); 498 | */ 499 | text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); 500 | 501 | return text; 502 | } 503 | 504 | var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { 505 | if (m7 == undefined) m7 = ""; 506 | var whole_match = m1; 507 | var link_text = m2; 508 | var link_id = m3.toLowerCase(); 509 | var url = m4; 510 | var title = m7; 511 | 512 | if (url == "") { 513 | if (link_id == "") { 514 | // lower-case and turn embedded newlines into spaces 515 | link_id = link_text.toLowerCase().replace(/ ?\n/g," "); 516 | } 517 | url = "#"+link_id; 518 | 519 | if (g_urls[link_id] != undefined) { 520 | url = g_urls[link_id]; 521 | if (g_titles[link_id] != undefined) { 522 | title = g_titles[link_id]; 523 | } 524 | } 525 | else { 526 | if (whole_match.search(/\(\s*\)$/m)>-1) { 527 | // Special case for explicit empty url 528 | url = ""; 529 | } else { 530 | return whole_match; 531 | } 532 | } 533 | } 534 | 535 | url = escapeCharacters(url,"*_"); 536 | var result = ""; 545 | 546 | return result; 547 | } 548 | 549 | 550 | var _DoImages = function(text) { 551 | // 552 | // Turn Markdown image shortcuts into tags. 553 | // 554 | 555 | // 556 | // First, handle reference-style labeled images: ![alt text][id] 557 | // 558 | 559 | /* 560 | text = text.replace(/ 561 | ( // wrap whole match in $1 562 | !\[ 563 | (.*?) // alt text = $2 564 | \] 565 | 566 | [ ]? // one optional space 567 | (?:\n[ ]*)? // one optional newline followed by spaces 568 | 569 | \[ 570 | (.*?) // id = $3 571 | \] 572 | )()()()() // pad rest of backreferences 573 | /g,writeImageTag); 574 | */ 575 | text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); 576 | 577 | // 578 | // Next, handle inline images: ![alt text](url "optional title") 579 | // Don't forget: encode * and _ 580 | 581 | /* 582 | text = text.replace(/ 583 | ( // wrap whole match in $1 584 | !\[ 585 | (.*?) // alt text = $2 586 | \] 587 | \s? // One optional whitespace character 588 | \( // literal paren 589 | [ \t]* 590 | () // no id, so leave $3 empty 591 | ? // src url = $4 592 | [ \t]* 593 | ( // $5 594 | (['"]) // quote char = $6 595 | (.*?) // title = $7 596 | \6 // matching quote 597 | [ \t]* 598 | )? // title is optional 599 | \) 600 | ) 601 | /g,writeImageTag); 602 | */ 603 | text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); 604 | 605 | return text; 606 | } 607 | 608 | var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { 609 | var whole_match = m1; 610 | var alt_text = m2; 611 | var link_id = m3.toLowerCase(); 612 | var url = m4; 613 | var title = m7; 614 | 615 | if (!title) title = ""; 616 | 617 | if (url == "") { 618 | if (link_id == "") { 619 | // lower-case and turn embedded newlines into spaces 620 | link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); 621 | } 622 | url = "#"+link_id; 623 | 624 | if (g_urls[link_id] != undefined) { 625 | url = g_urls[link_id]; 626 | if (g_titles[link_id] != undefined) { 627 | title = g_titles[link_id]; 628 | } 629 | } 630 | else { 631 | return whole_match; 632 | } 633 | } 634 | 635 | alt_text = alt_text.replace(/"/g,"""); 636 | url = escapeCharacters(url,"*_"); 637 | var result = "\""" + _RunSpanGamut(m1) + "");}); 665 | 666 | text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, 667 | function(matchFound,m1){return hashBlock("

    " + _RunSpanGamut(m1) + "

    ");}); 668 | 669 | // atx-style headers: 670 | // # Header 1 671 | // ## Header 2 672 | // ## Header 2 with closing hashes ## 673 | // ... 674 | // ###### Header 6 675 | // 676 | 677 | /* 678 | text = text.replace(/ 679 | ^(\#{1,6}) // $1 = string of #'s 680 | [ \t]* 681 | (.+?) // $2 = Header text 682 | [ \t]* 683 | \#* // optional closing #'s (not counted) 684 | \n+ 685 | /gm, function() {...}); 686 | */ 687 | 688 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, 689 | function(wholeMatch,m1,m2) { 690 | var h_level = m1.length; 691 | return hashBlock("" + _RunSpanGamut(m2) + ""); 692 | }); 693 | 694 | return text; 695 | } 696 | 697 | // This declaration keeps Dojo compressor from outputting garbage: 698 | var _ProcessListItems; 699 | 700 | var _DoLists = function(text) { 701 | // 702 | // Form HTML ordered (numbered) and unordered (bulleted) lists. 703 | // 704 | 705 | // attacklab: add sentinel to hack around khtml/safari bug: 706 | // http://bugs.webkit.org/show_bug.cgi?id=11231 707 | text += "~0"; 708 | 709 | // Re-usable pattern to match any entirel ul or ol list: 710 | 711 | /* 712 | var whole_list = / 713 | ( // $1 = whole list 714 | ( // $2 715 | [ ]{0,3} // attacklab: g_tab_width - 1 716 | ([*+-]|\d+[.]) // $3 = first list item marker 717 | [ \t]+ 718 | ) 719 | [^\r]+? 720 | ( // $4 721 | ~0 // sentinel for workaround; should be $ 722 | | 723 | \n{2,} 724 | (?=\S) 725 | (?! // Negative lookahead for another list item marker 726 | [ \t]* 727 | (?:[*+-]|\d+[.])[ \t]+ 728 | ) 729 | ) 730 | )/g 731 | */ 732 | var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; 733 | 734 | if (g_list_level) { 735 | text = text.replace(whole_list,function(wholeMatch,m1,m2) { 736 | var list = m1; 737 | var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; 738 | 739 | // Turn double returns into triple returns, so that we can make a 740 | // paragraph for the last item in a list, if necessary: 741 | list = list.replace(/\n{2,}/g,"\n\n\n");; 742 | var result = _ProcessListItems(list); 743 | 744 | // Trim any trailing whitespace, to put the closing `` 745 | // up on the preceding line, to get it past the current stupid 746 | // HTML block parser. This is a hack to work around the terrible 747 | // hack that is the HTML block parser. 748 | result = result.replace(/\s+$/,""); 749 | result = "<"+list_type+">" + result + "\n"; 750 | return result; 751 | }); 752 | } else { 753 | whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; 754 | text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { 755 | var runup = m1; 756 | var list = m2; 757 | 758 | var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; 759 | // Turn double returns into triple returns, so that we can make a 760 | // paragraph for the last item in a list, if necessary: 761 | var list = list.replace(/\n{2,}/g,"\n\n\n");; 762 | var result = _ProcessListItems(list); 763 | result = runup + "<"+list_type+">\n" + result + "\n"; 764 | return result; 765 | }); 766 | } 767 | 768 | // attacklab: strip sentinel 769 | text = text.replace(/~0/,""); 770 | 771 | return text; 772 | } 773 | 774 | _ProcessListItems = function(list_str) { 775 | // 776 | // Process the contents of a single ordered or unordered list, splitting it 777 | // into individual list items. 778 | // 779 | // The $g_list_level global keeps track of when we're inside a list. 780 | // Each time we enter a list, we increment it; when we leave a list, 781 | // we decrement. If it's zero, we're not in a list anymore. 782 | // 783 | // We do this because when we're not inside a list, we want to treat 784 | // something like this: 785 | // 786 | // I recommend upgrading to version 787 | // 8. Oops, now this line is treated 788 | // as a sub-list. 789 | // 790 | // As a single paragraph, despite the fact that the second line starts 791 | // with a digit-period-space sequence. 792 | // 793 | // Whereas when we're inside a list (or sub-list), that line will be 794 | // treated as the start of a sub-list. What a kludge, huh? This is 795 | // an aspect of Markdown's syntax that's hard to parse perfectly 796 | // without resorting to mind-reading. Perhaps the solution is to 797 | // change the syntax rules such that sub-lists must start with a 798 | // starting cardinal number; e.g. "1." or "a.". 799 | 800 | g_list_level++; 801 | 802 | // trim trailing blank lines: 803 | list_str = list_str.replace(/\n{2,}$/,"\n"); 804 | 805 | // attacklab: add sentinel to emulate \z 806 | list_str += "~0"; 807 | 808 | /* 809 | list_str = list_str.replace(/ 810 | (\n)? // leading line = $1 811 | (^[ \t]*) // leading whitespace = $2 812 | ([*+-]|\d+[.]) [ \t]+ // list marker = $3 813 | ([^\r]+? // list item text = $4 814 | (\n{1,2})) 815 | (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) 816 | /gm, function(){...}); 817 | */ 818 | list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, 819 | function(wholeMatch,m1,m2,m3,m4){ 820 | var item = m4; 821 | var leading_line = m1; 822 | var leading_space = m2; 823 | 824 | if (leading_line || (item.search(/\n{2,}/)>-1)) { 825 | item = _RunBlockGamut(_Outdent(item)); 826 | } 827 | else { 828 | // Recursion for sub-lists: 829 | item = _DoLists(_Outdent(item)); 830 | item = item.replace(/\n$/,""); // chomp(item) 831 | item = _RunSpanGamut(item); 832 | } 833 | 834 | return "
  • " + item + "
  • \n"; 835 | } 836 | ); 837 | 838 | // attacklab: strip sentinel 839 | list_str = list_str.replace(/~0/g,""); 840 | 841 | g_list_level--; 842 | return list_str; 843 | } 844 | 845 | 846 | var _DoCodeBlocks = function(text) { 847 | // 848 | // Process Markdown `
    ` blocks.
     849 | //
     850 | 
     851 | 	/*
     852 | 		text = text.replace(text,
     853 | 			/(?:\n\n|^)
     854 | 			(								// $1 = the code block -- one or more lines, starting with a space/tab
     855 | 				(?:
     856 | 					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
     857 | 					.*\n+
     858 | 				)+
     859 | 			)
     860 | 			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
     861 | 		/g,function(){...});
     862 | 	*/
     863 | 
     864 | 	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
     865 | 	text += "~0";
     866 | 
     867 | 	text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
     868 | 		function(wholeMatch,m1,m2) {
     869 | 			var codeblock = m1;
     870 | 			var nextChar = m2;
     871 | 
     872 | 			codeblock = _EncodeCode( _Outdent(codeblock));
     873 | 			codeblock = _Detab(codeblock);
     874 | 			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
     875 | 			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
     876 | 
     877 | 			codeblock = "
    " + codeblock + "\n
    "; 878 | 879 | return hashBlock(codeblock) + nextChar; 880 | } 881 | ); 882 | 883 | // attacklab: strip sentinel 884 | text = text.replace(/~0/,""); 885 | 886 | return text; 887 | } 888 | 889 | var hashBlock = function(text) { 890 | text = text.replace(/(^\n+|\n+$)/g,""); 891 | return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; 892 | } 893 | 894 | 895 | var _DoCodeSpans = function(text) { 896 | // 897 | // * Backtick quotes are used for spans. 898 | // 899 | // * You can use multiple backticks as the delimiters if you want to 900 | // include literal backticks in the code span. So, this input: 901 | // 902 | // Just type ``foo `bar` baz`` at the prompt. 903 | // 904 | // Will translate to: 905 | // 906 | //

    Just type foo `bar` baz at the prompt.

    907 | // 908 | // There's no arbitrary limit to the number of backticks you 909 | // can use as delimters. If you need three consecutive backticks 910 | // in your code, use four for delimiters, etc. 911 | // 912 | // * You can use spaces to get literal backticks at the edges: 913 | // 914 | // ... type `` `bar` `` ... 915 | // 916 | // Turns to: 917 | // 918 | // ... type `bar` ... 919 | // 920 | 921 | /* 922 | text = text.replace(/ 923 | (^|[^\\]) // Character before opening ` can't be a backslash 924 | (`+) // $2 = Opening run of ` 925 | ( // $3 = The code block 926 | [^\r]*? 927 | [^`] // attacklab: work around lack of lookbehind 928 | ) 929 | \2 // Matching closer 930 | (?!`) 931 | /gm, function(){...}); 932 | */ 933 | 934 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, 935 | function(wholeMatch,m1,m2,m3,m4) { 936 | var c = m3; 937 | c = c.replace(/^([ \t]*)/g,""); // leading whitespace 938 | c = c.replace(/[ \t]*$/g,""); // trailing whitespace 939 | c = _EncodeCode(c); 940 | return m1+""+c+""; 941 | }); 942 | 943 | return text; 944 | } 945 | 946 | 947 | var _EncodeCode = function(text) { 948 | // 949 | // Encode/escape certain characters inside Markdown code runs. 950 | // The point is that in code, these characters are literals, 951 | // and lose their special Markdown meanings. 952 | // 953 | // Encode all ampersands; HTML entities are not 954 | // entities within a Markdown code span. 955 | text = text.replace(/&/g,"&"); 956 | 957 | // Do the angle bracket song and dance: 958 | text = text.replace(//g,">"); 960 | 961 | // Now, escape characters that are magic in Markdown: 962 | text = escapeCharacters(text,"\*_{}[]\\",false); 963 | 964 | // jj the line above breaks this: 965 | //--- 966 | 967 | //* Item 968 | 969 | // 1. Subitem 970 | 971 | // special char: * 972 | //--- 973 | 974 | return text; 975 | } 976 | 977 | 978 | var _DoItalicsAndBold = function(text) { 979 | 980 | // must go first: 981 | text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, 982 | "$2"); 983 | 984 | text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, 985 | "$2"); 986 | 987 | return text; 988 | } 989 | 990 | 991 | var _DoBlockQuotes = function(text) { 992 | 993 | /* 994 | text = text.replace(/ 995 | ( // Wrap whole match in $1 996 | ( 997 | ^[ \t]*>[ \t]? // '>' at the start of a line 998 | .+\n // rest of the first line 999 | (.+\n)* // subsequent consecutive lines 1000 | \n* // blanks 1001 | )+ 1002 | ) 1003 | /gm, function(){...}); 1004 | */ 1005 | 1006 | text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, 1007 | function(wholeMatch,m1) { 1008 | var bq = m1; 1009 | 1010 | // attacklab: hack around Konqueror 3.5.4 bug: 1011 | // "----------bug".replace(/^-/g,"") == "bug" 1012 | 1013 | bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting 1014 | 1015 | // attacklab: clean up hack 1016 | bq = bq.replace(/~0/g,""); 1017 | 1018 | bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines 1019 | bq = _RunBlockGamut(bq); // recurse 1020 | 1021 | bq = bq.replace(/(^|\n)/g,"$1 "); 1022 | // These leading spaces screw with
     content, so we need to fix that:
    1023 | 			bq = bq.replace(
    1024 | 					/(\s*
    [^\r]+?<\/pre>)/gm,
    1025 | 				function(wholeMatch,m1) {
    1026 | 					var pre = m1;
    1027 | 					// attacklab: hack around Konqueror 3.5.4 bug:
    1028 | 					pre = pre.replace(/^  /mg,"~0");
    1029 | 					pre = pre.replace(/~0/g,"");
    1030 | 					return pre;
    1031 | 				});
    1032 | 
    1033 | 			return hashBlock("
    \n" + bq + "\n
    "); 1034 | }); 1035 | return text; 1036 | } 1037 | 1038 | 1039 | var _FormParagraphs = function(text) { 1040 | // 1041 | // Params: 1042 | // $text - string to process with html

    tags 1043 | // 1044 | 1045 | // Strip leading and trailing lines: 1046 | text = text.replace(/^\n+/g,""); 1047 | text = text.replace(/\n+$/g,""); 1048 | 1049 | var grafs = text.split(/\n{2,}/g); 1050 | var grafsOut = new Array(); 1051 | 1052 | // 1053 | // Wrap

    tags. 1054 | // 1055 | var end = grafs.length; 1056 | for (var i=0; i= 0) { 1061 | grafsOut.push(str); 1062 | } 1063 | else if (str.search(/\S/) >= 0) { 1064 | str = _RunSpanGamut(str); 1065 | str = str.replace(/^([ \t]*)/g,"

    "); 1066 | str += "

    " 1067 | grafsOut.push(str); 1068 | } 1069 | 1070 | } 1071 | 1072 | // 1073 | // Unhashify HTML blocks 1074 | // 1075 | end = grafsOut.length; 1076 | for (var i=0; i= 0) { 1079 | var blockText = g_html_blocks[RegExp.$1]; 1080 | blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs 1081 | grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); 1082 | } 1083 | } 1084 | 1085 | return grafsOut.join("\n\n"); 1086 | } 1087 | 1088 | 1089 | var _EncodeAmpsAndAngles = function(text) { 1090 | // Smart processing for ampersands and angle brackets that need to be encoded. 1091 | 1092 | // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 1093 | // http://bumppo.net/projects/amputator/ 1094 | text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"); 1095 | 1096 | // Encode naked <'s 1097 | text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); 1098 | 1099 | return text; 1100 | } 1101 | 1102 | 1103 | var _EncodeBackslashEscapes = function(text) { 1104 | // 1105 | // Parameter: String. 1106 | // Returns: The string, with after processing the following backslash 1107 | // escape sequences. 1108 | // 1109 | 1110 | // attacklab: The polite way to do this is with the new 1111 | // escapeCharacters() function: 1112 | // 1113 | // text = escapeCharacters(text,"\\",true); 1114 | // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); 1115 | // 1116 | // ...but we're sidestepping its use of the (slow) RegExp constructor 1117 | // as an optimization for Firefox. This function gets called a LOT. 1118 | 1119 | text = text.replace(/\\(\\)/g,escapeCharacters_callback); 1120 | text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); 1121 | return text; 1122 | } 1123 | 1124 | 1125 | var _DoAutoLinks = function(text) { 1126 | 1127 | text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"
    $1"); 1128 | 1129 | // Email addresses: 1130 | 1131 | /* 1132 | text = text.replace(/ 1133 | < 1134 | (?:mailto:)? 1135 | ( 1136 | [-.\w]+ 1137 | \@ 1138 | [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ 1139 | ) 1140 | > 1141 | /gi, _DoAutoLinks_callback()); 1142 | */ 1143 | text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, 1144 | function(wholeMatch,m1) { 1145 | return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); 1146 | } 1147 | ); 1148 | 1149 | return text; 1150 | } 1151 | 1152 | 1153 | var _EncodeEmailAddress = function(addr) { 1154 | // 1155 | // Input: an email address, e.g. "foo@example.com" 1156 | // 1157 | // Output: the email address as a mailto link, with each character 1158 | // of the address encoded as either a decimal or hex entity, in 1159 | // the hopes of foiling most address harvesting spam bots. E.g.: 1160 | // 1161 | // foo 1163 | // @example.com 1164 | // 1165 | // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk 1166 | // mailing list: 1167 | // 1168 | 1169 | // attacklab: why can't javascript speak hex? 1170 | function char2hex(ch) { 1171 | var hexDigits = '0123456789ABCDEF'; 1172 | var dec = ch.charCodeAt(0); 1173 | return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15)); 1174 | } 1175 | 1176 | var encode = [ 1177 | function(ch){return "&#"+ch.charCodeAt(0)+";";}, 1178 | function(ch){return "&#x"+char2hex(ch)+";";}, 1179 | function(ch){return ch;} 1180 | ]; 1181 | 1182 | addr = "mailto:" + addr; 1183 | 1184 | addr = addr.replace(/./g, function(ch) { 1185 | if (ch == "@") { 1186 | // this *must* be encoded. I insist. 1187 | ch = encode[Math.floor(Math.random()*2)](ch); 1188 | } else if (ch !=":") { 1189 | // leave ':' alone (to spot mailto: later) 1190 | var r = Math.random(); 1191 | // roughly 10% raw, 45% hex, 45% dec 1192 | ch = ( 1193 | r > .9 ? encode[2](ch) : 1194 | r > .45 ? encode[1](ch) : 1195 | encode[0](ch) 1196 | ); 1197 | } 1198 | return ch; 1199 | }); 1200 | 1201 | addr = "" + addr + ""; 1202 | addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part 1203 | 1204 | return addr; 1205 | } 1206 | 1207 | 1208 | var _UnescapeSpecialChars = function(text) { 1209 | // 1210 | // Swap back in all the special characters we've hidden. 1211 | // 1212 | text = text.replace(/~E(\d+)E/g, 1213 | function(wholeMatch,m1) { 1214 | var charCodeToReplace = parseInt(m1); 1215 | return String.fromCharCode(charCodeToReplace); 1216 | } 1217 | ); 1218 | return text; 1219 | } 1220 | 1221 | 1222 | var _Outdent = function(text) { 1223 | // 1224 | // Remove one level of line-leading tabs or spaces 1225 | // 1226 | 1227 | // attacklab: hack around Konqueror 3.5.4 bug: 1228 | // "----------bug".replace(/^-/g,"") == "bug" 1229 | 1230 | text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width 1231 | 1232 | // attacklab: clean up hack 1233 | text = text.replace(/~0/g,"") 1234 | 1235 | return text; 1236 | } 1237 | 1238 | var _Detab = function(text) { 1239 | // attacklab: Detab's completely rewritten for speed. 1240 | // In perl we could fix it by anchoring the regexp with \G. 1241 | // In javascript we're less fortunate. 1242 | 1243 | // expand first n-1 tabs 1244 | text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width 1245 | 1246 | // replace the nth with two sentinels 1247 | text = text.replace(/\t/g,"~A~B"); 1248 | 1249 | // use the sentinel to anchor our regex so it doesn't explode 1250 | text = text.replace(/~B(.+?)~A/g, 1251 | function(wholeMatch,m1,m2) { 1252 | var leadingText = m1; 1253 | var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width 1254 | 1255 | // there *must* be a better way to do this: 1256 | for (var i=0; iFlexibleNav bookmarklet 19 | // 20 | // ...and **click on it in any website**! 21 | // 22 | // - - - 23 | // 24 | // **This page is a 3-in-one document!** 25 | // 26 | // * First, it explains goals and possible usages of the library. 27 | // * Second, it presents **a library usage** 28 | // _(see this flexible box right panel?)_. 29 | // * Third, it shows the **annotated JS code** _(thanks to docco)_ 30 | // 31 | // Download 32 | // -------- 33 | // You can also use it in your websites. 34 | // 35 | // Download or fork me on github 36 | // 37 | // Usages 38 | // ----- 39 | // 40 | // #### Generation and Flexibility #### 41 | // 42 | // The library can generate for you the nav sidebar content 43 | // providing elements to display in navigation. 44 | // 45 | // By default, it uses `h1` to `h3` elements to retrieve and locate 46 | // the different sections of a document but you can override these 47 | // elements selector. 48 | // 49 | // You have usually two kind of selected elements to use: 50 | // 51 | // * **Select a title**. The link will take the title as text. 52 | // * **Select any container** and add a **`data-navtext` attribute** 53 | // to define the title you want. 54 | // 55 | // Example: `
    ...` 56 | // 57 | // * **Simple usage used in this page** 58 | // 59 | // var nav = new FlexibleNavMaker().make().prependTo('body'); 60 | // new FlexibleNav(nav); 61 | // 62 | // * **Selecting element you want** 63 | // 64 | // new FlexibleNavMaker(".navtitle"); 65 | // 66 | // Using this code bellow, all nodes having this `navtitle` class 67 | // will appear in the flexible nav panel. 68 | // 69 | // #### Flexibility only #### 70 | // 71 | // You can also create your own nav links to have more controls of used links. 72 | // Conventionnally, you need to use 73 | // a `