Vastly improves appearance of options, which means I've figured out how to use Bootst...
authordsc <dsc@less.ly>
Thu, 23 Feb 2012 10:13:53 +0000 (02:13 -0800)
committerdsc <dsc@less.ly>
Thu, 23 Feb 2012 10:13:53 +0000 (02:13 -0800)
15 files changed:
lib/graph.co
lib/scaffold/index.co [new file with mode: 0644]
lib/scaffold/model.co [new file with mode: 0644]
lib/scaffold/view.co [moved from lib/scaffold.co with 61% similarity]
lib/template/browser-helpers.jade [new file with mode: 0644]
lib/template/graph-option.jade
lib/template/graph.jade
lib/underscore/functions.co [new file with mode: 0644]
static/vendor/showdown.js [new file with mode: 0644]
static/vendor/showdown.min.js [new file with mode: 0644]
www/css/graph.styl
www/css/isotope.css [new file with mode: 0644]
www/graph/test.jade
www/layout.jade
www/modules.yaml

index 0d23855..eda01aa 100644 (file)
@@ -31,6 +31,7 @@ GraphOptionList = exports.GraphOptionList = FieldList.extend do # {{{
  * The view for a single configurable option.
  */
 GraphOptionView = exports.GraphOptionView = FieldView.extend do # {{{
+    # __bind__  : <[ onClick ]>
     ctorName  : 'GraphOptionView'
     tagName   : 'div'
     className : 'field option'
@@ -41,37 +42,38 @@ GraphOptionView = exports.GraphOptionView = FieldView.extend do # {{{
     events :
         'blur .value'                   : 'update'
         'submit .value'                 : 'update'
+        'click .close'                  : 'toggleCollapsed'
+        'click'                         : 'onClick'
     
     
-    initialize: ->
-        # console.log "#this.initialize!"
-        FieldView::initialize ...
-        @$el.on 'click', (evt) ~>
-            target = $ evt.target
-            @toggleCollapsed() if @el is evt.target or not target.is '.value, label, input'
-        
+    # initialize: ->
+    #     console.log "#this.initialize!"
+    #     FieldView::initialize ...
+    #     
     
     update: ->
         val     = @$el.find('.value').val()
         current = @model.get 'value'
         return if val is current
-        
         console.log "#this.update( #current -> #val )"
         @model.setValue val, {+silent}
     
     render: ->
-        FieldView::render.call this
+        @__super__.render ...
         @$el.addClass 'collapsed' if @isCollapsed
-        # outer = $ @template @model.toJSON()
-        # @$el.html outer.html()
-        #     .attr do
-        #         id    : outer.attr 'id'
-        #         class : outer.attr('class') + if @isCollapsed then ' collapsed' else ''
         this
     
+    onClick: (evt) ->
+        target = $ evt.target
+        console.log "#this.onClick()", target
+        @toggleCollapsed() if @$el.hasClass('collapsed') and not target.hasClass('close')
+    
     toggleCollapsed: ->
+        starting = @$el.hasClass 'collapsed' #@isCollapsed
         @$el.toggleClass 'collapsed'
         @isCollapsed = @$el.hasClass 'collapsed'
+        console.log "#this.toggleCollapsed!", starting, '->', @isCollapsed
+        @trigger 'change:collapse', this
         this
     
 # }}}
@@ -85,18 +87,26 @@ GraphOptionsScaffold = exports.GraphOptionsScaffold = Scaffold.extend do # {{{
     collectionType : GraphOptionList
     subviewType    : GraphOptionView
     
-    # initialize : ->
-    #     Scaffold::initialize ...
+    
+    initialize : ->
+        Scaffold::initialize ...
+        @render = _.debounce @render.bind(this), 50
     
     render: ->
+        # console.log "#this.render() -> .isotope()"
         @__super__.render ...
         @$el.isotope do
             itemSelector    : '.field.option'
             layoutMode      : 'masonry'
-            masonry         : columnWidth : 100
+            masonry         : columnWidth : 10
+            # itemPositionDataEnabled : true
             # animationEngine : 'jquery'
-            itemPositionDataEnabled : true
-        
+    
+    addOne: ->
+        # console.log "#this.addOne!"
+        view = @__super__.addOne ...
+        view.on 'change:collapse render', @render
+        view
     
 # }}}
 
@@ -141,6 +151,7 @@ GraphView = exports.GraphView = BaseView.extend do # {{{
     initialize : (o={}) ->
         @model or= new GraphModel
         BaseView::initialize ...
+        # console.log "#this.initialize!"
         
         @model.on 'change',  @render, this
         @model.on 'destroy', @remove, this
@@ -172,8 +183,6 @@ GraphView = exports.GraphView = BaseView.extend do # {{{
             options.values()
     
     render: ->
-        # BaseView::render ...
-        
         options = @chartOptions()
         w = options.width  or= @scaffold.get 'width'  .getValue() or 480
         h = options.height or= @scaffold.get 'height' .getValue() or 320
diff --git a/lib/scaffold/index.co b/lib/scaffold/index.co
new file mode 100644 (file)
index 0000000..03f7aa7
--- /dev/null
@@ -0,0 +1,3 @@
+models = require 'kraken/scaffold/model'
+views  = require 'kraken/scaffold/view'
+exports import models import views
diff --git a/lib/scaffold/model.co b/lib/scaffold/model.co
new file mode 100644 (file)
index 0000000..422de92
--- /dev/null
@@ -0,0 +1,88 @@
+_  = require 'kraken/underscore'
+op = require 'kraken/util/op'
+
+
+
+### Scaffold Models
+
+Field = exports.Field = Backbone.Model.extend do # {{{
+    ctorName : 'Field'
+    idAttribute : 'name'
+    
+    initialize: ->
+        @set 'value', @get('default'), {+silent} if not @has 'value'
+        # console.log "#this.initialize!"
+    
+    defaults: ->
+        {
+            name     : ''
+            type     : 'String'
+            default  : null
+            desc     : ''
+            category : 'General'
+            include  : 'diff'
+            tags     : []
+            examples : []
+        }
+    
+    getParser: (type) ->
+        type or= @get 'type'
+        t = _ type.toLowerCase()
+        
+        parser = op.toStr
+        if t.startsWith 'integer'
+            parser = op.toInt
+        if t.startsWith 'float'
+            parser = op.toFloat
+        if t.startsWith 'boolean'
+            parser = op.toBool
+        if t.startsWith 'object' or t.startsWith 'array'
+            parser = op.toObject
+        if t.startsWith 'function'
+            parser = (fn) -> eval "(#fn)"
+        
+        # TODO: handle 'or' by returning an array of parsers
+        parser
+    
+    
+    getValue: (def) ->
+        @getParser() @get 'value', def
+    
+    setValue: (v, options) ->
+        def = @get 'default'
+        if not v and def == null
+            val = null
+        else
+            val = @getParser()(v)
+        @set 'value', val, options
+    
+    clearValue: ->
+        @set 'value', @get('default')
+    
+    isDefault: ->
+        @get('value') is @get('default')
+    
+    toJSON: ->
+        {id:@id} import do
+            _.clone(@attributes) import { value:@getValue(), def:@get('default') }
+    
+    toString: -> "(#{@id}: #{@get 'value'})"
+# }}}
+
+
+FieldList = exports.FieldList = Backbone.Collection.extend do # {{{
+    ctorName : 'FieldList'
+    model : Field
+    
+    /**
+     * Collects a map of fields to their values, excluding those set to `null` or their default.
+     * @returns {Object}
+     */
+    values: ->
+        _.synthesize do
+            @models.filter -> not it.isDefault()
+            -> [ it.get('name'), it.getValue() ]
+    
+    toString: -> "#{@ctorName}(length=#{@length})"
+# }}}
+
similarity index 61%
rename from lib/scaffold.co
rename to lib/scaffold/view.co
index 474ff3c..332e289 100644 (file)
@@ -1,92 +1,7 @@
 _  = require 'kraken/underscore'
 op = require 'kraken/util/op'
-
-
-
-### Scaffold Models
-
-Field = exports.Field = Backbone.Model.extend do # {{{
-    ctorName : 'Field'
-    idAttribute : 'name'
-    
-    initialize: ->
-        @set 'value', @get('default'), {+silent} if not @has 'value'
-        # console.log "#this.initialize!"
-    
-    defaults: ->
-        {
-            name     : ''
-            type     : 'String'
-            default  : null
-            desc     : ''
-            category : 'General'
-            include  : 'diff'
-            tags     : []
-            examples : []
-        }
-    
-    getParser: (type) ->
-        type or= @get 'type'
-        t = _ type.toLowerCase()
-        
-        parser = op.toStr
-        if t.startsWith 'integer'
-            parser = op.toInt
-        if t.startsWith 'float'
-            parser = op.toFloat
-        if t.startsWith 'boolean'
-            parser = op.toBool
-        if t.startsWith 'object' or t.startsWith 'array'
-            parser = op.toObject
-        if t.startsWith 'function'
-            parser = (fn) -> eval "(#fn)"
-        
-        # TODO: handle 'or' by returning an array of parsers
-        parser
-    
-    
-    getValue: (def) ->
-        @getParser() @get 'value', def
-    
-    setValue: (v, options) ->
-        def = @get 'default'
-        if not v and def == null
-            val = null
-        else
-            val = @getParser()(v)
-        @set 'value', val, options
-    
-    clearValue: ->
-        @set 'value', @get('default')
-    
-    isDefault: ->
-        @get('value') is @get('default')
-    
-    toJSON: ->
-        {id:@id} import do
-            _.clone(@attributes) import { value:@getValue(), def:@get('default') }
-    
-    toString: -> "(#{@id}: #{@get 'value'})"
-# }}}
-
-
-FieldList = exports.FieldList = Backbone.Collection.extend do # {{{
-    ctorName : 'FieldList'
-    model : Field
-    
-    /**
-     * Collects a map of fields to their values, excluding those set to `null` or their default.
-     * @returns {Object}
-     */
-    values: ->
-        _.synthesize do
-            @models.filter -> not it.isDefault()
-            -> [ it.get('name'), it.getValue() ]
-    
-    toString: -> "#{@ctorName}(length=#{@length})"
-# }}}
-
-
+{ Field, FieldList,
+}  = require 'kraken/scaffold/model'
 
 
 ### Views
@@ -97,9 +12,8 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{
     
     
     initialize: ->
-        _.bindAll this, ...@__bind__
+        _.bindAll this, ...@__bind__ if @__bind__.length
         @__super__ = @constructor.__super__
-        Backbone.View::initialize ...
         
         @model.view = this
         @$el.data { @model, view:this }
@@ -127,6 +41,8 @@ BaseView = exports.BaseView = Backbone.View.extend do # {{{
     
     render: ->
         @build()
+        @trigger 'render', this
+        this
     
     hide   : -> @$el.hide();      this
     show   : -> @$el.show();      this
@@ -170,11 +86,11 @@ FieldView = exports.FieldView = BaseView.extend do # {{{
         val     = @$el.find('.value').val()
         current = @model.get 'value'
         return if val is current
-        console.log "#this.onUIChange( #current -> #val )"
+        # console.log "#this.onUIChange( #current -> #val )"
         @model.setValue val, {+silent}
     
     render: ->
-        return @remove() if @model.get 'hidden'
+        return @remove() if @model.get 'hidden', false
         return BaseView::render ... if @template
         
         name  = @model.get 'name'
@@ -219,12 +135,15 @@ Scaffold = exports.Scaffold = BaseView.extend do # {{{
     
     
     addOne: (field) ->
+        # console.log "[S] #this.addOne!", @__super__
         _.remove @subviews, field.view if field.view
         
         SubviewType = @subviewType
         view = new SubviewType model:field
         @subviews.push view
-        @$el.append view.render().el
+        @$el.append view.render().el unless field.get 'hidden'
+        
+        @render()
         view
     
     addAll: ->
diff --git a/lib/template/browser-helpers.jade b/lib/template/browser-helpers.jade
new file mode 100644 (file)
index 0000000..db4034f
--- /dev/null
@@ -0,0 +1,3 @@
+- window.Markdown || (window.Markdown = new (require('showdown').Showdown).converter());
+- (jade.filters || (jade.filters = {})).markdown = function (s, name){ return s && Markdown.makeHtml(s.replace(/\n/g, '\n\n')); };
+
index 0da61a1..01fae16 100644 (file)
@@ -1,25 +1,27 @@
-- var _            = require('kraken/underscore');
+include browser-helpers
+- var option_id    = _.domize('option', id);
 - var value_id     = _.domize('value', id);
 - var category_cls = _.domize('category', category);
 - var tags_cls     = tags.map(_.domize('tag')).join(' ');
 - var type_cls     = _.domize('type', type); 
 
-.field.option(id=_.domize('option', id), class="#{category_cls} #{tags_cls}")
+.field.option(id=option_id, class="#{category_cls} #{tags_cls}")
+    a.close &times;
+    
     h3.shortname #{_.shortname(name)}
     label.name(for=value_id) #{name}
-    input.value(type="text", id=value_id, name=name, value=value)
+    input.value(type="text", id=value_id, name=name, class=type_cls, value=value)
     
     .type(class=type_cls) #{type}
-    .default(class=type_cls) #{def}
+    .default(class=type_cls, title="Default: #{def} (#{type})") #{def}
     
     .desc
-        #{desc}
-        //- != jade.filters.markdown(desc)
+        != (jade.filters || {}).markdown(desc)
     
-    .tags: ul
+    .tags(data-toggle="collapse", data-target="ul"): ul.collapse
         for tag in tags
             li.tag(class=_.domize('tag', tag)) #{tag}
-    .examples: ul
+    .examples(data-toggle="collapse", data-target="ul"): ul.collapse
         for example in examples
             li.example
                 a(href="http://dygraphs.com/tests/#{example}.html", target="_blank") #{example}
index 29b193a..7dd5df7 100644 (file)
@@ -7,6 +7,6 @@ section.graph(id=id)
         form.details
             label.name(for='#{id}_name') Name
     
-    fieldset
+    fieldset.options
         legend Graph Options
         
diff --git a/lib/underscore/functions.co b/lib/underscore/functions.co
new file mode 100644 (file)
index 0000000..0dc8bf6
--- /dev/null
@@ -0,0 +1,248 @@
+_ = require 'underscore'
+
+
+slice       = [].slice
+hasOwn      = {}.hasOwnProperty
+objToString = {}.toString
+
+toArray = _.toArray
+
+
+
+decorate = (fn) ->
+    if not fn.__decorated__
+        for name of _pet.FUNCTION_METHODS
+            m = _[name]
+            fn[name] = m.__methodized__ or methodize m
+        fn.__decorated__ = true
+    return fn
+
+methodize = (fn) ->
+    m = fn.__methodized__
+    return m if m
+    
+    g = fn.__genericized__
+    return g.__wraps__ if g and g.__wraps__
+    
+    m = fn.__methodized__ = (args...) ->
+        args.unshift this
+        return fn.apply this, args
+    
+    m.__wraps__ = fn
+    return decorate m
+
+
+
+_pet = module.exports = \
+    function pet (o, start=0, end=undefined) ->
+        if _.isArguments o
+            o = _.toArray o, start, end
+        
+        return decorate o if typeof o is 'function'
+        return _ o
+
+# function methods to be attached on call to _(fn)
+_pet.FUNCTION_METHODS = [
+    'bind', 'bindAll', 'memoize', 
+    'delay', 'defer', 'throttle', 'debounce', 'once', 'after', 
+    'wrap', 'compose',
+    'unwrap', 'partial', 'curry', 'flip', 'methodize', 'aritize', 'limit'
+]
+
+
+class2name = "Boolean Number String Function Array Date RegExp Object"
+        .split(" ")
+        .reduce ((class2name, name) ->
+            class2name[ "[object "+name+"]" ] = name
+            return class2name), {}
+
+
+## Objects
+_.mixin
+    
+    has: (o, v) ->
+        vals = if _.isArray(o) then o else _.values(o)
+        return vals.indexOf(v) is not -1
+    
+    remove: (o, vs...) ->
+        if _.isArray(o)
+            _.each vs, (v) ->
+                idx = o.indexOf v
+                if idx is not -1
+                    o.splice idx, 1
+        else
+            _.each o, (v, k) ->
+                if vs.indexOf(v) != -1
+                    delete o[k]
+        return o
+    
+    set: (o, key, value, def) ->
+        if o and key? and (value? or def?)
+            o[key] = value ? def
+        return o
+    
+    attr: (o, key, value, def) ->
+        return o if not o or key is undefined
+        
+        if _.isPlainObject key
+            return _.extend o, key
+        
+        if (value ? def) is not undefined
+            return _.set o, key, value, def
+        
+        return o[key]
+    
+    
+
+## Types
+_.mixin
+    
+    basicTypeName: (o) ->
+        return if o is null then "null" else (class2name[objToString.call(o)] || "Object")
+    
+    isWindow: (o) ->
+        return o and typeof o is "object" and "setInterval" of o
+    
+    isPlainObject: (o) ->
+        # Must be an Object.
+        # Because of IE, we also have to check the presence of the constructor property.
+        # Make sure that DOM nodes and window objects don't pass through, as well
+        if not o or basicTypeName(o) is not "Object" or o.nodeType or _.isWindow(o)
+            return false
+        
+        # Not own constructor property? must be Object
+        C = o.constructor
+        if C and not hasOwn.call(o, "constructor") and not hasOwn.call(C.prototype, "isPrototypeOf")
+            return false
+        
+        # Own properties are enumerated firstly, so to speed up,
+        # if last one is own, then all properties are own.
+        for key in o
+            ; # semicolon **on new line** is required by coffeescript to denote empty statement.
+        
+        return key is undefined or hasOwn.call(o, key)
+    
+
+## Arrays
+_.mixin
+    
+    toArray: (iterable, start=0, end=undefined) ->
+        _.slice toArray(iterable), start, end
+    
+    flatten: (A) ->
+        _.reduce do
+            slice.call(arguments)
+            (flat, v) ->
+                flat.concat( if _.isArray v then _.reduce(v, arguments.callee, []) else v )
+            []
+    
+
+
+## Functions
+_ofArity = _.memoize(
+    (n, limit) ->
+        args       = ( '$'+i for i from 0 til n ).join(',')
+        name       = ( if limit then 'limited' else 'artized' )
+        apply_with = ( if limit then "[].slice.call(arguments, 0, #{n})" else 'arguments' )
+        return eval "
+            (function #{name}(fn){
+                var _fn = function(#{args}){ return fn.apply(this, #{apply_with}); };
+                _fn.__wraps__ = fn;
+                return _(_fn);
+            })"
+    )
+
+_.mixin
+    methodize: methodize
+    
+    unwrap: (fn) ->
+        (fn and _.isFunction(fn) and _.unwrap(fn.__wraps__)) or fn
+    
+    
+    partial: (fn, args...) ->
+        partially =  ->
+            fn.apply this, args.concat(slice.call(arguments))
+        partially.__wraps__ = fn
+        return _ partially
+    
+    
+    genericize: (fn) ->
+        g = fn.__genericized__
+        return g if g
+        
+        m = fn.__methodized__
+        return m.__wraps__ if m and m.__wraps__
+
+        g = fn.__genericized__ = (args...) ->
+                fn.apply args.shift(), args
+        
+        g.__wraps__ = fn
+        return _ g
+    
+    
+    curry: (fn, args...) ->
+        if not _.isFunction fn
+            return fn
+        
+        if fn.__curried__
+            return fn.apply this, args
+        
+        L = fn.length or _.unwrap(fn).length
+        if args.length >= L
+            return fn.apply this, args
+        
+        curried =  ->
+            _args = args.concat slice.call(arguments)
+            if _args.length >= L
+                return fn.apply this, _args
+            _args.unshift fn
+            return _.curry.apply this, _args
+        
+        curried.__wraps__ = fn
+        curried.__curried__ = args
+        return _ curried
+    
+    
+    flip: (fn) ->
+        f = fn.__flipped__
+        return f if f
+        
+        f = fn.__flipped__ = \
+            flipped =  ->
+                args    = arguments
+                hd      = args[0]
+                args[0] = args[1]
+                args[1] = hd
+                return fn.apply this, args
+        
+        f.__wraps__ = fn
+        return _ f
+    
+    
+    aritize: (fn, n) ->
+        return fn if fn.length is n
+        
+        cache = fn.__aritized__
+        if not cache
+            cache = fn.__aritized__ = {}
+        else if cache[n]
+            return cache[n]
+        
+        return ( cache[n] = _ofArity(n, false)(fn) )
+    
+    
+    limit: (fn, n) ->
+        cache = fn.__limited__
+        if not cache
+            cache = fn.__limited__ = {}
+        else if cache[n]
+            return cache[n]
+        
+        return ( cache[n] = _ofArity(n, true)(fn) )
+    
+    
+    
+
+
+
+_.extend _pet, _
diff --git a/static/vendor/showdown.js b/static/vendor/showdown.js
new file mode 100644 (file)
index 0000000..43920d9
--- /dev/null
@@ -0,0 +1,1302 @@
+//\r
+// showdown.js -- A javascript port of Markdown.\r
+//\r
+// Copyright (c) 2007 John Fraser.\r
+//\r
+// Original Markdown Copyright (c) 2004-2005 John Gruber\r
+//   <http://daringfireball.net/projects/markdown/>\r
+//\r
+// Redistributable under a BSD-style open source license.\r
+// See license.txt for more information.\r
+//\r
+// The full source distribution is at:\r
+//\r
+//                             A A L\r
+//                             T C A\r
+//                             T K B\r
+//\r
+//   <http://www.attacklab.net/>\r
+//\r
+\r
+//\r
+// Wherever possible, Showdown is a straight, line-by-line port\r
+// of the Perl version of Markdown.\r
+//\r
+// This is not a normal parser design; it's basically just a\r
+// series of string substitutions.  It's hard to read and\r
+// maintain this way,  but keeping Showdown close to the original\r
+// design makes it easier to port new features.\r
+//\r
+// More importantly, Showdown behaves like markdown.pl in most\r
+// edge cases.  So web applications can do client-side preview\r
+// in Javascript, and then build identical HTML on the server.\r
+//\r
+// This port needs the new RegExp functionality of ECMA 262,\r
+// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers\r
+// should do fine.  Even with the new regular expression features,\r
+// We do a lot of work to emulate Perl's regex functionality.\r
+// The tricky changes in this file mostly have the "attacklab:"\r
+// label.  Major or self-explanatory changes don't.\r
+//\r
+// Smart diff tools like Araxis Merge will be able to match up\r
+// this file with markdown.pl in a useful way.  A little tweaking\r
+// helps: in a copy of markdown.pl, replace "#" with "//" and\r
+// replace "$text" with "text".  Be sure to ignore whitespace\r
+// and line endings.\r
+//\r
+\r
+\r
+//\r
+// Showdown usage:\r
+//\r
+//   var text = "Markdown *rocks*.";\r
+//\r
+//   var converter = new Showdown.converter();\r
+//   var html = converter.makeHtml(text);\r
+//\r
+//   alert(html);\r
+//\r
+// Note: move the sample code to the bottom of this\r
+// file before uncommenting it.\r
+//\r
+\r
+\r
+//\r
+// Showdown namespace\r
+//\r
+var Showdown = {};\r
+\r
+//\r
+// converter\r
+//\r
+// Wraps all "globals" so that the only thing\r
+// exposed is makeHtml().\r
+//\r
+Showdown.converter = function() {\r
+\r
+//\r
+// Globals:\r
+//\r
+\r
+// Global hashes, used by various utility routines\r
+var g_urls;\r
+var g_titles;\r
+var g_html_blocks;\r
+\r
+// Used to track when we're inside an ordered or unordered list\r
+// (see _ProcessListItems() for details):\r
+var g_list_level = 0;\r
+\r
+\r
+this.makeHtml = function(text) {\r
+//\r
+// Main function. The order in which other subs are called here is\r
+// essential. Link and image substitutions need to happen before\r
+// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>\r
+// and <img> tags get encoded.\r
+//\r
+\r
+       // Clear the global hashes. If we don't clear these, you get conflicts\r
+       // from other articles when generating a page which contains more than\r
+       // one article (e.g. an index page that shows the N most recent\r
+       // articles):\r
+       g_urls = new Array();\r
+       g_titles = new Array();\r
+       g_html_blocks = new Array();\r
+\r
+       // attacklab: Replace ~ with ~T\r
+       // This lets us use tilde as an escape char to avoid md5 hashes\r
+       // The choice of character is arbitray; anything that isn't\r
+    // magic in Markdown will work.\r
+       text = text.replace(/~/g,"~T");\r
+\r
+       // attacklab: Replace $ with ~D\r
+       // RegExp interprets $ as a special character\r
+       // when it's in a replacement string\r
+       text = text.replace(/\$/g,"~D");\r
+\r
+       // Standardize line endings\r
+       text = text.replace(/\r\n/g,"\n"); // DOS to Unix\r
+       text = text.replace(/\r/g,"\n"); // Mac to Unix\r
+\r
+       // Make sure text begins and ends with a couple of newlines:\r
+       text = "\n\n" + text + "\n\n";\r
+\r
+       // Convert all tabs to spaces.\r
+       text = _Detab(text);\r
+\r
+       // Strip any lines consisting only of spaces and tabs.\r
+       // This makes subsequent regexen easier to write, because we can\r
+       // match consecutive blank lines with /\n+/ instead of something\r
+       // contorted like /[ \t]*\n+/ .\r
+       text = text.replace(/^[ \t]+$/mg,"");\r
+\r
+       // Turn block-level HTML blocks into hash entries\r
+       text = _HashHTMLBlocks(text);\r
+\r
+       // Strip link definitions, store in hashes.\r
+       text = _StripLinkDefinitions(text);\r
+\r
+       text = _RunBlockGamut(text);\r
+\r
+       text = _UnescapeSpecialChars(text);\r
+\r
+       // attacklab: Restore dollar signs\r
+       text = text.replace(/~D/g,"$$");\r
+\r
+       // attacklab: Restore tildes\r
+       text = text.replace(/~T/g,"~");\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _StripLinkDefinitions = function(text) {\r
+//\r
+// Strips link definitions from text, stores the URLs and titles in\r
+// hash references.\r
+//\r
+\r
+       // Link defs are in the form: ^[id]: url "optional title"\r
+\r
+       /*\r
+               var text = text.replace(/\r
+                               ^[ ]{0,3}\[(.+)\]:  // id = $1  attacklab: g_tab_width - 1\r
+                                 [ \t]*\r
+                                 \n?                           // maybe *one* newline\r
+                                 [ \t]*\r
+                               <?(\S+?)>?                      // url = $2\r
+                                 [ \t]*\r
+                                 \n?                           // maybe one newline\r
+                                 [ \t]*\r
+                               (?:\r
+                                 (\n*)                         // any lines skipped = $3 attacklab: lookbehind removed\r
+                                 ["(]\r
+                                 (.+?)                         // title = $4\r
+                                 [")]\r
+                                 [ \t]*\r
+                               )?                                      // title is optional\r
+                               (?:\n+|$)\r
+                         /gm,\r
+                         function(){...});\r
+       */\r
+       var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,\r
+               function (wholeMatch,m1,m2,m3,m4) {\r
+                       m1 = m1.toLowerCase();\r
+                       g_urls[m1] = _EncodeAmpsAndAngles(m2);  // Link IDs are case-insensitive\r
+                       if (m3) {\r
+                               // Oops, found blank lines, so it's not a title.\r
+                               // Put back the parenthetical statement we stole.\r
+                               return m3+m4;\r
+                       } else if (m4) {\r
+                               g_titles[m1] = m4.replace(/"/g,"&quot;");\r
+                       }\r
+                       \r
+                       // Completely remove the definition from the text\r
+                       return "";\r
+               }\r
+       );\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _HashHTMLBlocks = function(text) {\r
+       // attacklab: Double up blank lines to reduce lookaround\r
+       text = text.replace(/\n/g,"\n\n");\r
+\r
+       // Hashify HTML blocks:\r
+       // We only want to do this for block-level HTML tags, such as headers,\r
+       // lists, and tables. That's because we still want to wrap <p>s around\r
+       // "paragraphs" that are wrapped in non-block-level tags, such as anchors,\r
+       // phrase emphasis, and spans. The list of tags we're looking for is\r
+       // hard-coded:\r
+       var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"\r
+       var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"\r
+\r
+       // First, look for nested blocks, e.g.:\r
+       //   <div>\r
+       //     <div>\r
+       //     tags for inner block must be indented.\r
+       //     </div>\r
+       //   </div>\r
+       //\r
+       // The outermost tags must start at the left margin for this to match, and\r
+       // the inner nested divs must be indented.\r
+       // We need to do this before the next, more liberal match, because the next\r
+       // match will start at the first `<div>` and stop at the first `</div>`.\r
+\r
+       // attacklab: This regex can be expensive when it fails.\r
+       /*\r
+               var text = text.replace(/\r
+               (                                               // save in $1\r
+                       ^                                       // start of line  (with /m)\r
+                       <($block_tags_a)        // start tag = $2\r
+                       \b                                      // word break\r
+                                                               // attacklab: hack around khtml/pcre bug...\r
+                       [^\r]*?\n                       // any number of lines, minimally matching\r
+                       </\2>                           // the matching end tag\r
+                       [ \t]*                          // trailing spaces/tabs\r
+                       (?=\n+)                         // followed by a newline\r
+               )                                               // attacklab: there are sentinel newlines at end of document\r
+               /gm,function(){...}};\r
+       */\r
+       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);\r
+\r
+       //\r
+       // Now match more liberally, simply from `\n<tag>` to `</tag>\n`\r
+       //\r
+\r
+       /*\r
+               var text = text.replace(/\r
+               (                                               // save in $1\r
+                       ^                                       // start of line  (with /m)\r
+                       <($block_tags_b)        // start tag = $2\r
+                       \b                                      // word break\r
+                                                               // attacklab: hack around khtml/pcre bug...\r
+                       [^\r]*?                         // any number of lines, minimally matching\r
+                       .*</\2>                         // the matching end tag\r
+                       [ \t]*                          // trailing spaces/tabs\r
+                       (?=\n+)                         // followed by a newline\r
+               )                                               // attacklab: there are sentinel newlines at end of document\r
+               /gm,function(){...}};\r
+       */\r
+       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);\r
+\r
+       // Special case just for <hr />. It was easier to make a special case than\r
+       // to make the other regex more complicated.  \r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // save in $1\r
+                       \n\n                            // Starting after a blank line\r
+                       [ ]{0,3}\r
+                       (<(hr)                          // start tag = $2\r
+                       \b                                      // word break\r
+                       ([^<>])*?                       // \r
+                       \/?>)                           // the matching end tag\r
+                       [ \t]*\r
+                       (?=\n{2,})                      // followed by a blank line\r
+               )\r
+               /g,hashElement);\r
+       */\r
+       text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+       // Special case for standalone HTML comments:\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // save in $1\r
+                       \n\n                            // Starting after a blank line\r
+                       [ ]{0,3}                        // attacklab: g_tab_width - 1\r
+                       <!\r
+                       (--[^\r]*?--\s*)+\r
+                       >\r
+                       [ \t]*\r
+                       (?=\n{2,})                      // followed by a blank line\r
+               )\r
+               /g,hashElement);\r
+       */\r
+       text = text.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+       // PHP and ASP-style processor instructions (<?...?> and <%...%>)\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (?:\r
+                       \n\n                            // Starting after a blank line\r
+               )\r
+               (                                               // save in $1\r
+                       [ ]{0,3}                        // attacklab: g_tab_width - 1\r
+                       (?:\r
+                               <([?%])                 // $2\r
+                               [^\r]*?\r
+                               \2>\r
+                       )\r
+                       [ \t]*\r
+                       (?=\n{2,})                      // followed by a blank line\r
+               )\r
+               /g,hashElement);\r
+       */\r
+       text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement);\r
+\r
+       // attacklab: Undo double lines (see comment at top of this function)\r
+       text = text.replace(/\n\n/g,"\n");\r
+       return text;\r
+}\r
+\r
+var hashElement = function(wholeMatch,m1) {\r
+       var blockText = m1;\r
+\r
+       // Undo double lines\r
+       blockText = blockText.replace(/\n\n/g,"\n");\r
+       blockText = blockText.replace(/^\n/,"");\r
+       \r
+       // strip trailing blank lines\r
+       blockText = blockText.replace(/\n+$/g,"");\r
+       \r
+       // Replace the element text with a marker ("~KxK" where x is its key)\r
+       blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n";\r
+       \r
+       return blockText;\r
+};\r
+\r
+var _RunBlockGamut = function(text) {\r
+//\r
+// These are all the transformations that form block-level\r
+// tags like paragraphs, headers, and list items.\r
+//\r
+       text = _DoHeaders(text);\r
+\r
+       // Do Horizontal Rules:\r
+       var key = hashBlock("<hr />");\r
+       text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);\r
+       text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);\r
+       text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);\r
+\r
+       text = _DoLists(text);\r
+       text = _DoCodeBlocks(text);\r
+       text = _DoBlockQuotes(text);\r
+\r
+       // We already ran _HashHTMLBlocks() before, in Markdown(), but that\r
+       // was to escape raw HTML in the original Markdown source. This time,\r
+       // we're escaping the markup we've just created, so that we don't wrap\r
+       // <p> tags around block-level tags.\r
+       text = _HashHTMLBlocks(text);\r
+       text = _FormParagraphs(text);\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _RunSpanGamut = function(text) {\r
+//\r
+// These are all the transformations that occur *within* block-level\r
+// tags like paragraphs, headers, and list items.\r
+//\r
+\r
+       text = _DoCodeSpans(text);\r
+       text = _EscapeSpecialCharsWithinTagAttributes(text);\r
+       text = _EncodeBackslashEscapes(text);\r
+\r
+       // Process anchor and image tags. Images must come first,\r
+       // because ![foo][f] looks like an anchor.\r
+       text = _DoImages(text);\r
+       text = _DoAnchors(text);\r
+\r
+       // Make links out of things like `<http://example.com/>`\r
+       // Must come after _DoAnchors(), because you can use < and >\r
+       // delimiters in inline links like [this](<url>).\r
+       text = _DoAutoLinks(text);\r
+       text = _EncodeAmpsAndAngles(text);\r
+       text = _DoItalicsAndBold(text);\r
+\r
+       // Do hard breaks:\r
+       text = text.replace(/  +\n/g," <br />\n");\r
+\r
+       return text;\r
+}\r
+\r
+var _EscapeSpecialCharsWithinTagAttributes = function(text) {\r
+//\r
+// Within tags -- meaning between < and > -- encode [\ ` * _] so they\r
+// don't conflict with their use in Markdown for code, italics and strong.\r
+//\r
+\r
+       // Build a regex to find HTML tags and comments.  See Friedl's \r
+       // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.\r
+       var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;\r
+\r
+       text = text.replace(regex, function(wholeMatch) {\r
+               var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");\r
+               tag = escapeCharacters(tag,"\\`*_");\r
+               return tag;\r
+       });\r
+\r
+       return text;\r
+}\r
+\r
+var _DoAnchors = function(text) {\r
+//\r
+// Turn Markdown link shortcuts into XHTML <a> tags.\r
+//\r
+       //\r
+       // First, handle reference-style links: [link text] [id]\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                                       // wrap whole match in $1\r
+                       \[\r
+                       (\r
+                               (?:\r
+                                       \[[^\]]*\]              // allow brackets nested one level\r
+                                       |\r
+                                       [^\[]                   // or anything else\r
+                               )*\r
+                       )\r
+                       \]\r
+\r
+                       [ ]?                                    // one optional space\r
+                       (?:\n[ ]*)?                             // one optional newline followed by spaces\r
+\r
+                       \[\r
+                       (.*?)                                   // id = $3\r
+                       \]\r
+               )()()()()                                       // pad remaining backreferences\r
+               /g,_DoAnchors_callback);\r
+       */\r
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);\r
+\r
+       //\r
+       // Next, inline-style links: [link text](url "optional title")\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       (                                               // wrap whole match in $1\r
+                               \[\r
+                               (\r
+                                       (?:\r
+                                               \[[^\]]*\]      // allow brackets nested one level\r
+                                       |\r
+                                       [^\[\]]                 // or anything else\r
+                               )\r
+                       )\r
+                       \]\r
+                       \(                                              // literal paren\r
+                       [ \t]*\r
+                       ()                                              // no id, so leave $3 empty\r
+                       <?(.*?)>?                               // href = $4\r
+                       [ \t]*\r
+                       (                                               // $5\r
+                               (['"])                          // quote char = $6\r
+                               (.*?)                           // Title = $7\r
+                               \6                                      // matching quote\r
+                               [ \t]*                          // ignore any spaces/tabs between closing quote and )\r
+                       )?                                              // title is optional\r
+                       \)\r
+               )\r
+               /g,writeAnchorTag);\r
+       */\r
+       text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);\r
+\r
+       //\r
+       // Last, handle reference-style shortcuts: [link text]\r
+       // These must come last in case you've also got [link test][1]\r
+       // or [link test](/foo)\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                                       // wrap whole match in $1\r
+                       \[\r
+                       ([^\[\]]+)                              // link text = $2; can't contain '[' or ']'\r
+                       \]\r
+               )()()()()()                                     // pad rest of backreferences\r
+               /g, writeAnchorTag);\r
+       */\r
+       text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);\r
+\r
+       return text;\r
+}\r
+\r
+var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {\r
+       if (m7 == undefined) m7 = "";\r
+       var whole_match = m1;\r
+       var link_text   = m2;\r
+       var link_id      = m3.toLowerCase();\r
+       var url         = m4;\r
+       var title       = m7;\r
+       \r
+       if (url == "") {\r
+               if (link_id == "") {\r
+                       // lower-case and turn embedded newlines into spaces\r
+                       link_id = link_text.toLowerCase().replace(/ ?\n/g," ");\r
+               }\r
+               url = "#"+link_id;\r
+               \r
+               if (g_urls[link_id] != undefined) {\r
+                       url = g_urls[link_id];\r
+                       if (g_titles[link_id] != undefined) {\r
+                               title = g_titles[link_id];\r
+                       }\r
+               }\r
+               else {\r
+                       if (whole_match.search(/\(\s*\)$/m)>-1) {\r
+                               // Special case for explicit empty url\r
+                               url = "";\r
+                       } else {\r
+                               return whole_match;\r
+                       }\r
+               }\r
+       }       \r
+       \r
+       url = escapeCharacters(url,"*_");\r
+       var result = "<a href=\"" + url + "\"";\r
+       \r
+       if (title != "") {\r
+               title = title.replace(/"/g,"&quot;");\r
+               title = escapeCharacters(title,"*_");\r
+               result +=  " title=\"" + title + "\"";\r
+       }\r
+       \r
+       result += ">" + link_text + "</a>";\r
+       \r
+       return result;\r
+}\r
+\r
+\r
+var _DoImages = function(text) {\r
+//\r
+// Turn Markdown image shortcuts into <img> tags.\r
+//\r
+\r
+       //\r
+       // First, handle reference-style labeled images: ![alt text][id]\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // wrap whole match in $1\r
+                       !\[\r
+                       (.*?)                           // alt text = $2\r
+                       \]\r
+\r
+                       [ ]?                            // one optional space\r
+                       (?:\n[ ]*)?                     // one optional newline followed by spaces\r
+\r
+                       \[\r
+                       (.*?)                           // id = $3\r
+                       \]\r
+               )()()()()                               // pad rest of backreferences\r
+               /g,writeImageTag);\r
+       */\r
+       text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);\r
+\r
+       //\r
+       // Next, handle inline images:  ![alt text](url "optional title")\r
+       // Don't forget: encode * and _\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                               // wrap whole match in $1\r
+                       !\[\r
+                       (.*?)                           // alt text = $2\r
+                       \]\r
+                       \s?                                     // One optional whitespace character\r
+                       \(                                      // literal paren\r
+                       [ \t]*\r
+                       ()                                      // no id, so leave $3 empty\r
+                       <?(\S+?)>?                      // src url = $4\r
+                       [ \t]*\r
+                       (                                       // $5\r
+                               (['"])                  // quote char = $6\r
+                               (.*?)                   // title = $7\r
+                               \6                              // matching quote\r
+                               [ \t]*\r
+                       )?                                      // title is optional\r
+               \)\r
+               )\r
+               /g,writeImageTag);\r
+       */\r
+       text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);\r
+\r
+       return text;\r
+}\r
+\r
+var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {\r
+       var whole_match = m1;\r
+       var alt_text   = m2;\r
+       var link_id      = m3.toLowerCase();\r
+       var url         = m4;\r
+       var title       = m7;\r
+\r
+       if (!title) title = "";\r
+       \r
+       if (url == "") {\r
+               if (link_id == "") {\r
+                       // lower-case and turn embedded newlines into spaces\r
+                       link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");\r
+               }\r
+               url = "#"+link_id;\r
+               \r
+               if (g_urls[link_id] != undefined) {\r
+                       url = g_urls[link_id];\r
+                       if (g_titles[link_id] != undefined) {\r
+                               title = g_titles[link_id];\r
+                       }\r
+               }\r
+               else {\r
+                       return whole_match;\r
+               }\r
+       }       \r
+       \r
+       alt_text = alt_text.replace(/"/g,"&quot;");\r
+       url = escapeCharacters(url,"*_");\r
+       var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";\r
+\r
+       // attacklab: Markdown.pl adds empty title attributes to images.\r
+       // Replicate this bug.\r
+\r
+       //if (title != "") {\r
+               title = title.replace(/"/g,"&quot;");\r
+               title = escapeCharacters(title,"*_");\r
+               result +=  " title=\"" + title + "\"";\r
+       //}\r
+       \r
+       result += " />";\r
+       \r
+       return result;\r
+}\r
+\r
+\r
+var _DoHeaders = function(text) {\r
+\r
+       // Setext-style headers:\r
+       //      Header 1\r
+       //      ========\r
+       //  \r
+       //      Header 2\r
+       //      --------\r
+       //\r
+       text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,\r
+               function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});\r
+\r
+       text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,\r
+               function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});\r
+\r
+       // atx-style headers:\r
+       //  # Header 1\r
+       //  ## Header 2\r
+       //  ## Header 2 with closing hashes ##\r
+       //  ...\r
+       //  ###### Header 6\r
+       //\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       ^(\#{1,6})                              // $1 = string of #'s\r
+                       [ \t]*\r
+                       (.+?)                                   // $2 = Header text\r
+                       [ \t]*\r
+                       \#*                                             // optional closing #'s (not counted)\r
+                       \n+\r
+               /gm, function() {...});\r
+       */\r
+\r
+       text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,\r
+               function(wholeMatch,m1,m2) {\r
+                       var h_level = m1.length;\r
+                       return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");\r
+               });\r
+\r
+       function headerId(m) {\r
+               return m.replace(/[^\w]/g, '').toLowerCase();\r
+       }\r
+       return text;\r
+}\r
+\r
+// This declaration keeps Dojo compressor from outputting garbage:\r
+var _ProcessListItems;\r
+\r
+var _DoLists = function(text) {\r
+//\r
+// Form HTML ordered (numbered) and unordered (bulleted) lists.\r
+//\r
+\r
+       // attacklab: add sentinel to hack around khtml/safari bug:\r
+       // http://bugs.webkit.org/show_bug.cgi?id=11231\r
+       text += "~0";\r
+\r
+       // Re-usable pattern to match any entirel ul or ol list:\r
+\r
+       /*\r
+               var whole_list = /\r
+               (                                                                       // $1 = whole list\r
+                       (                                                               // $2\r
+                               [ ]{0,3}                                        // attacklab: g_tab_width - 1\r
+                               ([*+-]|\d+[.])                          // $3 = first list item marker\r
+                               [ \t]+\r
+                       )\r
+                       [^\r]+?\r
+                       (                                                               // $4\r
+                               ~0                                                      // sentinel for workaround; should be $\r
+                       |\r
+                               \n{2,}\r
+                               (?=\S)\r
+                               (?!                                                     // Negative lookahead for another list item marker\r
+                                       [ \t]*\r
+                                       (?:[*+-]|\d+[.])[ \t]+\r
+                               )\r
+                       )\r
+               )/g\r
+       */\r
+       var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;\r
+\r
+       if (g_list_level) {\r
+               text = text.replace(whole_list,function(wholeMatch,m1,m2) {\r
+                       var list = m1;\r
+                       var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol";\r
+\r
+                       // Turn double returns into triple returns, so that we can make a\r
+                       // paragraph for the last item in a list, if necessary:\r
+                       list = list.replace(/\n{2,}/g,"\n\n\n");;\r
+                       var result = _ProcessListItems(list);\r
+       \r
+                       // Trim any trailing whitespace, to put the closing `</$list_type>`\r
+                       // up on the preceding line, to get it past the current stupid\r
+                       // HTML block parser. This is a hack to work around the terrible\r
+                       // hack that is the HTML block parser.\r
+                       result = result.replace(/\s+$/,"");\r
+                       result = "<"+list_type+">" + result + "</"+list_type+">\n";\r
+                       return result;\r
+               });\r
+       } else {\r
+               whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;\r
+               text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {\r
+                       var runup = m1;\r
+                       var list = m2;\r
+\r
+                       var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol";\r
+                       // Turn double returns into triple returns, so that we can make a\r
+                       // paragraph for the last item in a list, if necessary:\r
+                       var list = list.replace(/\n{2,}/g,"\n\n\n");;\r
+                       var result = _ProcessListItems(list);\r
+                       result = runup + "<"+list_type+">\n" + result + "</"+list_type+">\n";   \r
+                       return result;\r
+               });\r
+       }\r
+\r
+       // attacklab: strip sentinel\r
+       text = text.replace(/~0/,"");\r
+\r
+       return text;\r
+}\r
+\r
+_ProcessListItems = function(list_str) {\r
+//\r
+//  Process the contents of a single ordered or unordered list, splitting it\r
+//  into individual list items.\r
+//\r
+       // The $g_list_level global keeps track of when we're inside a list.\r
+       // Each time we enter a list, we increment it; when we leave a list,\r
+       // we decrement. If it's zero, we're not in a list anymore.\r
+       //\r
+       // We do this because when we're not inside a list, we want to treat\r
+       // something like this:\r
+       //\r
+       //    I recommend upgrading to version\r
+       //    8. Oops, now this line is treated\r
+       //    as a sub-list.\r
+       //\r
+       // As a single paragraph, despite the fact that the second line starts\r
+       // with a digit-period-space sequence.\r
+       //\r
+       // Whereas when we're inside a list (or sub-list), that line will be\r
+       // treated as the start of a sub-list. What a kludge, huh? This is\r
+       // an aspect of Markdown's syntax that's hard to parse perfectly\r
+       // without resorting to mind-reading. Perhaps the solution is to\r
+       // change the syntax rules such that sub-lists must start with a\r
+       // starting cardinal number; e.g. "1." or "a.".\r
+\r
+       g_list_level++;\r
+\r
+       // trim trailing blank lines:\r
+       list_str = list_str.replace(/\n{2,}$/,"\n");\r
+\r
+       // attacklab: add sentinel to emulate \z\r
+       list_str += "~0";\r
+\r
+       /*\r
+               list_str = list_str.replace(/\r
+                       (\n)?                                                   // leading line = $1\r
+                       (^[ \t]*)                                               // leading whitespace = $2\r
+                       ([*+-]|\d+[.]) [ \t]+                   // list marker = $3\r
+                       ([^\r]+?                                                // list item text   = $4\r
+                       (\n{1,2}))\r
+                       (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+))\r
+               /gm, function(){...});\r
+       */\r
+       list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,\r
+               function(wholeMatch,m1,m2,m3,m4){\r
+                       var item = m4;\r
+                       var leading_line = m1;\r
+                       var leading_space = m2;\r
+\r
+                       if (leading_line || (item.search(/\n{2,}/)>-1)) {\r
+                               item = _RunBlockGamut(_Outdent(item));\r
+                       }\r
+                       else {\r
+                               // Recursion for sub-lists:\r
+                               item = _DoLists(_Outdent(item));\r
+                               item = item.replace(/\n$/,""); // chomp(item)\r
+                               item = _RunSpanGamut(item);\r
+                       }\r
+\r
+                       return  "<li>" + item + "</li>\n";\r
+               }\r
+       );\r
+\r
+       // attacklab: strip sentinel\r
+       list_str = list_str.replace(/~0/g,"");\r
+\r
+       g_list_level--;\r
+       return list_str;\r
+}\r
+\r
+\r
+var _DoCodeBlocks = function(text) {\r
+//\r
+//  Process Markdown `<pre><code>` blocks.\r
+//  \r
+\r
+       /*\r
+               text = text.replace(text,\r
+                       /(?:\n\n|^)\r
+                       (                                                               // $1 = the code block -- one or more lines, starting with a space/tab\r
+                               (?:\r
+                                       (?:[ ]{4}|\t)                   // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width\r
+                                       .*\n+\r
+                               )+\r
+                       )\r
+                       (\n*[ ]{0,3}[^ \t\n]|(?=~0))    // attacklab: g_tab_width\r
+               /g,function(){...});\r
+       */\r
+\r
+       // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug\r
+       text += "~0";\r
+       \r
+       text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,\r
+               function(wholeMatch,m1,m2) {\r
+                       var codeblock = m1;\r
+                       var nextChar = m2;\r
+               \r
+                       codeblock = _EncodeCode( _Outdent(codeblock));\r
+                       codeblock = _Detab(codeblock);\r
+                       codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines\r
+                       codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace\r
+\r
+                       codeblock = "<pre><code>" + codeblock + "\n</code></pre>";\r
+\r
+                       return hashBlock(codeblock) + nextChar;\r
+               }\r
+       );\r
+\r
+       // attacklab: strip sentinel\r
+       text = text.replace(/~0/,"");\r
+\r
+       return text;\r
+}\r
+\r
+var hashBlock = function(text) {\r
+       text = text.replace(/(^\n+|\n+$)/g,"");\r
+       return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";\r
+}\r
+\r
+\r
+var _DoCodeSpans = function(text) {\r
+//\r
+//   *  Backtick quotes are used for <code></code> spans.\r
+// \r
+//   *  You can use multiple backticks as the delimiters if you want to\r
+//      include literal backticks in the code span. So, this input:\r
+//      \r
+//              Just type ``foo `bar` baz`` at the prompt.\r
+//      \r
+//        Will translate to:\r
+//      \r
+//              <p>Just type <code>foo `bar` baz</code> at the prompt.</p>\r
+//      \r
+//     There's no arbitrary limit to the number of backticks you\r
+//     can use as delimters. If you need three consecutive backticks\r
+//     in your code, use four for delimiters, etc.\r
+//\r
+//  *  You can use spaces to get literal backticks at the edges:\r
+//      \r
+//              ... type `` `bar` `` ...\r
+//      \r
+//        Turns to:\r
+//      \r
+//              ... type <code>`bar`</code> ...\r
+//\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       (^|[^\\])                                       // Character before opening ` can't be a backslash\r
+                       (`+)                                            // $2 = Opening run of `\r
+                       (                                                       // $3 = The code block\r
+                               [^\r]*?\r
+                               [^`]                                    // attacklab: work around lack of lookbehind\r
+                       )\r
+                       \2                                                      // Matching closer\r
+                       (?!`)\r
+               /gm, function(){...});\r
+       */\r
+\r
+       text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,\r
+               function(wholeMatch,m1,m2,m3,m4) {\r
+                       var c = m3;\r
+                       c = c.replace(/^([ \t]*)/g,""); // leading whitespace\r
+                       c = c.replace(/[ \t]*$/g,"");   // trailing whitespace\r
+                       c = _EncodeCode(c);\r
+                       return m1+"<code>"+c+"</code>";\r
+               });\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _EncodeCode = function(text) {\r
+//\r
+// Encode/escape certain characters inside Markdown code runs.\r
+// The point is that in code, these characters are literals,\r
+// and lose their special Markdown meanings.\r
+//\r
+       // Encode all ampersands; HTML entities are not\r
+       // entities within a Markdown code span.\r
+       text = text.replace(/&/g,"&amp;");\r
+\r
+       // Do the angle bracket song and dance:\r
+       text = text.replace(/</g,"&lt;");\r
+       text = text.replace(/>/g,"&gt;");\r
+\r
+       // Now, escape characters that are magic in Markdown:\r
+       text = escapeCharacters(text,"\*_{}[]\\",false);\r
+\r
+// jj the line above breaks this:\r
+//---\r
+\r
+//* Item\r
+\r
+//   1. Subitem\r
+\r
+//            special char: *\r
+//---\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _DoItalicsAndBold = function(text) {\r
+\r
+       // <strong> must go first:\r
+       text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,\r
+               "<strong>$2</strong>");\r
+\r
+       text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,\r
+               "<em>$2</em>");\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _DoBlockQuotes = function(text) {\r
+\r
+       /*\r
+               text = text.replace(/\r
+               (                                                               // Wrap whole match in $1\r
+                       (\r
+                               ^[ \t]*>[ \t]?                  // '>' at the start of a line\r
+                               .+\n                                    // rest of the first line\r
+                               (.+\n)*                                 // subsequent consecutive lines\r
+                               \n*                                             // blanks\r
+                       )+\r
+               )\r
+               /gm, function(){...});\r
+       */\r
+\r
+       text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,\r
+               function(wholeMatch,m1) {\r
+                       var bq = m1;\r
+\r
+                       // attacklab: hack around Konqueror 3.5.4 bug:\r
+                       // "----------bug".replace(/^-/g,"") == "bug"\r
+\r
+                       bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0");       // trim one level of quoting\r
+\r
+                       // attacklab: clean up hack\r
+                       bq = bq.replace(/~0/g,"");\r
+\r
+                       bq = bq.replace(/^[ \t]+$/gm,"");               // trim whitespace-only lines\r
+                       bq = _RunBlockGamut(bq);                                // recurse\r
+                       \r
+                       bq = bq.replace(/(^|\n)/g,"$1  ");\r
+                       // These leading spaces screw with <pre> content, so we need to fix that:\r
+                       bq = bq.replace(\r
+                                       /(\s*<pre>[^\r]+?<\/pre>)/gm,\r
+                               function(wholeMatch,m1) {\r
+                                       var pre = m1;\r
+                                       // attacklab: hack around Konqueror 3.5.4 bug:\r
+                                       pre = pre.replace(/^  /mg,"~0");\r
+                                       pre = pre.replace(/~0/g,"");\r
+                                       return pre;\r
+                               });\r
+                       \r
+                       return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");\r
+               });\r
+       return text;\r
+}\r
+\r
+\r
+var _FormParagraphs = function(text) {\r
+//\r
+//  Params:\r
+//    $text - string to process with html <p> tags\r
+//\r
+\r
+       // Strip leading and trailing lines:\r
+       text = text.replace(/^\n+/g,"");\r
+       text = text.replace(/\n+$/g,"");\r
+\r
+       var grafs = text.split(/\n{2,}/g);\r
+       var grafsOut = new Array();\r
+\r
+       //\r
+       // Wrap <p> tags.\r
+       //\r
+       var end = grafs.length;\r
+       for (var i=0; i<end; i++) {\r
+               var str = grafs[i];\r
+\r
+               // if this is an HTML marker, copy it\r
+               if (str.search(/~K(\d+)K/g) >= 0) {\r
+                       grafsOut.push(str);\r
+               }\r
+               else if (str.search(/\S/) >= 0) {\r
+                       str = _RunSpanGamut(str);\r
+                       str = str.replace(/^([ \t]*)/g,"<p>");\r
+                       str += "</p>"\r
+                       grafsOut.push(str);\r
+               }\r
+\r
+       }\r
+\r
+       //\r
+       // Unhashify HTML blocks\r
+       //\r
+       end = grafsOut.length;\r
+       for (var i=0; i<end; i++) {\r
+               // if this is a marker for an html block...\r
+               while (grafsOut[i].search(/~K(\d+)K/) >= 0) {\r
+                       var blockText = g_html_blocks[RegExp.$1];\r
+                       blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs\r
+                       grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText);\r
+               }\r
+       }\r
+\r
+       return grafsOut.join("\n\n");\r
+}\r
+\r
+\r
+var _EncodeAmpsAndAngles = function(text) {\r
+// Smart processing for ampersands and angle brackets that need to be encoded.\r
+       \r
+       // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:\r
+       //   http://bumppo.net/projects/amputator/\r
+       text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");\r
+       \r
+       // Encode naked <'s\r
+       text = text.replace(/<(?![a-z\/?\$!])/gi,"&lt;");\r
+       \r
+       return text;\r
+}\r
+\r
+\r
+var _EncodeBackslashEscapes = function(text) {\r
+//\r
+//   Parameter:  String.\r
+//   Returns:  The string, with after processing the following backslash\r
+//                        escape sequences.\r
+//\r
+\r
+       // attacklab: The polite way to do this is with the new\r
+       // escapeCharacters() function:\r
+       //\r
+       //      text = escapeCharacters(text,"\\",true);\r
+       //      text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);\r
+       //\r
+       // ...but we're sidestepping its use of the (slow) RegExp constructor\r
+       // as an optimization for Firefox.  This function gets called a LOT.\r
+\r
+       text = text.replace(/\\(\\)/g,escapeCharacters_callback);\r
+       text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback);\r
+       return text;\r
+}\r
+\r
+\r
+var _DoAutoLinks = function(text) {\r
+\r
+       text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");\r
+\r
+       // Email addresses: <address@domain.foo>\r
+\r
+       /*\r
+               text = text.replace(/\r
+                       <\r
+                       (?:mailto:)?\r
+                       (\r
+                               [-.\w]+\r
+                               \@\r
+                               [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+\r
+                       )\r
+                       >\r
+               /gi, _DoAutoLinks_callback());\r
+       */\r
+       text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,\r
+               function(wholeMatch,m1) {\r
+                       return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );\r
+               }\r
+       );\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var _EncodeEmailAddress = function(addr) {\r
+//\r
+//  Input: an email address, e.g. "foo@example.com"\r
+//\r
+//  Output: the email address as a mailto link, with each character\r
+//     of the address encoded as either a decimal or hex entity, in\r
+//     the hopes of foiling most address harvesting spam bots. E.g.:\r
+//\r
+//     <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;\r
+//        x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;\r
+//        &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>\r
+//\r
+//  Based on a filter by Matthew Wickline, posted to the BBEdit-Talk\r
+//  mailing list: <http://tinyurl.com/yu7ue>\r
+//\r
+\r
+       // attacklab: why can't javascript speak hex?\r
+       function char2hex(ch) {\r
+               var hexDigits = '0123456789ABCDEF';\r
+               var dec = ch.charCodeAt(0);\r
+               return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));\r
+       }\r
+\r
+       var encode = [\r
+               function(ch){return "&#"+ch.charCodeAt(0)+";";},\r
+               function(ch){return "&#x"+char2hex(ch)+";";},\r
+               function(ch){return ch;}\r
+       ];\r
+\r
+       addr = "mailto:" + addr;\r
+\r
+       addr = addr.replace(/./g, function(ch) {\r
+               if (ch == "@") {\r
+                       // this *must* be encoded. I insist.\r
+                       ch = encode[Math.floor(Math.random()*2)](ch);\r
+               } else if (ch !=":") {\r
+                       // leave ':' alone (to spot mailto: later)\r
+                       var r = Math.random();\r
+                       // roughly 10% raw, 45% hex, 45% dec\r
+                       ch =  (\r
+                                       r > .9  ?       encode[2](ch)   :\r
+                                       r > .45 ?       encode[1](ch)   :\r
+                                                               encode[0](ch)\r
+                               );\r
+               }\r
+               return ch;\r
+       });\r
+\r
+       addr = "<a href=\"" + addr + "\">" + addr + "</a>";\r
+       addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part\r
+\r
+       return addr;\r
+}\r
+\r
+\r
+var _UnescapeSpecialChars = function(text) {\r
+//\r
+// Swap back in all the special characters we've hidden.\r
+//\r
+       text = text.replace(/~E(\d+)E/g,\r
+               function(wholeMatch,m1) {\r
+                       var charCodeToReplace = parseInt(m1);\r
+                       return String.fromCharCode(charCodeToReplace);\r
+               }\r
+       );\r
+       return text;\r
+}\r
+\r
+\r
+var _Outdent = function(text) {\r
+//\r
+// Remove one level of line-leading tabs or spaces\r
+//\r
+\r
+       // attacklab: hack around Konqueror 3.5.4 bug:\r
+       // "----------bug".replace(/^-/g,"") == "bug"\r
+\r
+       text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width\r
+\r
+       // attacklab: clean up hack\r
+       text = text.replace(/~0/g,"")\r
+\r
+       return text;\r
+}\r
+\r
+var _Detab = function(text) {\r
+// attacklab: Detab's completely rewritten for speed.\r
+// In perl we could fix it by anchoring the regexp with \G.\r
+// In javascript we're less fortunate.\r
+\r
+       // expand first n-1 tabs\r
+       text = text.replace(/\t(?=\t)/g,"    "); // attacklab: g_tab_width\r
+\r
+       // replace the nth with two sentinels\r
+       text = text.replace(/\t/g,"~A~B");\r
+\r
+       // use the sentinel to anchor our regex so it doesn't explode\r
+       text = text.replace(/~B(.+?)~A/g,\r
+               function(wholeMatch,m1,m2) {\r
+                       var leadingText = m1;\r
+                       var numSpaces = 4 - leadingText.length % 4;  // attacklab: g_tab_width\r
+\r
+                       // there *must* be a better way to do this:\r
+                       for (var i=0; i<numSpaces; i++) leadingText+=" ";\r
+\r
+                       return leadingText;\r
+               }\r
+       );\r
+\r
+       // clean up sentinels\r
+       text = text.replace(/~A/g,"    ");  // attacklab: g_tab_width\r
+       text = text.replace(/~B/g,"");\r
+\r
+       return text;\r
+}\r
+\r
+\r
+//\r
+//  attacklab: Utility functions\r
+//\r
+\r
+\r
+var escapeCharacters = function(text, charsToEscape, afterBackslash) {\r
+       // First we have to escape the escape characters so that\r
+       // we can build a character class out of them\r
+       var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g,"\\$1") + "])";\r
+\r
+       if (afterBackslash) {\r
+               regexString = "\\\\" + regexString;\r
+       }\r
+\r
+       var regex = new RegExp(regexString,"g");\r
+       text = text.replace(regex,escapeCharacters_callback);\r
+\r
+       return text;\r
+}\r
+\r
+\r
+var escapeCharacters_callback = function(wholeMatch,m1) {\r
+       var charCodeToEscape = m1.charCodeAt(0);\r
+       return "~E"+charCodeToEscape+"E";\r
+}\r
+\r
+} // end of Showdown.converter\r
+\r
+// export\r
+if (typeof exports != 'undefined') exports.Showdown = Showdown;
\ No newline at end of file
diff --git a/static/vendor/showdown.min.js b/static/vendor/showdown.min.js
new file mode 100644 (file)
index 0000000..1aadf57
--- /dev/null
@@ -0,0 +1,62 @@
+//
+// showdown.js -- A javascript port of Markdown.
+//
+// Copyright (c) 2007 John Fraser.
+//
+// Original Markdown Copyright (c) 2004-2005 John Gruber
+//   <http://daringfireball.net/projects/markdown/>
+//
+// Redistributable under a BSD-style open source license.
+// See license.txt for more information.
+//
+// The full source distribution is at:
+//
+//                             A A L
+//                             T C A
+//                             T K B
+//
+//   <http://www.attacklab.net/>
+//
+//
+// Wherever possible, Showdown is a straight, line-by-line port
+// of the Perl version of Markdown.
+//
+// This is not a normal parser design; it's basically just a
+// series of string substitutions.  It's hard to read and
+// maintain this way,  but keeping Showdown close to the original
+// design makes it easier to port new features.
+//
+// More importantly, Showdown behaves like markdown.pl in most
+// edge cases.  So web applications can do client-side preview
+// in Javascript, and then build identical HTML on the server.
+//
+// This port needs the new RegExp functionality of ECMA 262,
+// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
+// should do fine.  Even with the new regular expression features,
+// We do a lot of work to emulate Perl's regex functionality.
+// The tricky changes in this file mostly have the "attacklab:"
+// label.  Major or self-explanatory changes don't.
+//
+// Smart diff tools like Araxis Merge will be able to match up
+// this file with markdown.pl in a useful way.  A little tweaking
+// helps: in a copy of markdown.pl, replace "#" with "//" and
+// replace "$text" with "text".  Be sure to ignore whitespace
+// and line endings.
+//
+//
+// Showdown usage:
+//
+//   var text = "Markdown *rocks*.";
+//
+//   var converter = new Showdown.converter();
+//   var html = converter.makeHtml(text);
+//
+//   alert(html);
+//
+// Note: move the sample code to the bottom of this
+// file before uncommenting it.
+//
+//
+// Showdown namespace
+//
+var Showdown={};Showdown.converter=function(){var a,b,c,d=0;this.makeHtml=function(d){a=[],b=[],c=[],d=d.replace(/~/g,"~T"),d=d.replace(/\$/g,"~D"),d=d.replace(/\r\n/g,"\n"),d=d.replace(/\r/g,"\n"),d="\n\n"+d+"\n\n",d=E(d),d=d.replace(/^[ \t]+$/mg,""),d=f(d),d=e(d),d=h(d),d=C(d),d=d.replace(/~D/g,"$$"),d=d.replace(/~T/g,"~");return d};var e=function(c){var c=c.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,function(c,d,e,f,g){d=d.toLowerCase(),a[d]=y(e);if(f)return f+g;g&&(b[d]=g.replace(/"/g,"&quot;"));return""});return c},f=function(a){a=a.replace(/\n/g,"\n\n");var b="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del",c="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";a=a.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,g),a=a.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,g),a=a.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,g),a=a.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,g),a=a.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,g),a=a.replace(/\n\n/g,"\n");return a},g=function(a,b){var d=b;d=d.replace(/\n\n/g,"\n"),d=d.replace(/^\n/,""),d=d.replace(/\n+$/g,""),d="\n\n~K"+(c.push(d)-1)+"K\n\n";return d},h=function(a){a=o(a);var b=s("<hr />");a=a.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,b),a=q(a),a=r(a),a=w(a),a=f(a),a=x(a);return a},i=function(a){a=t(a),a=j(a),a=z(a),a=m(a),a=k(a),a=A(a),a=y(a),a=v(a),a=a.replace(/  +\n/g," <br />\n");return a},j=function(a){var b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;a=a.replace(b,function(a){var b=a.replace(/(.)<\/?code>(?=.)/g,"$1`");b=F(b,"\\`*_");return b});return a},k=function(a){a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,l),a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,l),a=a.replace(/(\[([^\[\]]+)\])()()()()()/g,l);return a},l=function(c,d,e,f,g,h,i,j){j==undefined&&(j="");var k=d,l=e,m=f.toLowerCase(),n=g,o=j;if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(a[m]!=undefined)n=a[m],b[m]!=undefined&&(o=b[m]);else if(k.search(/\(\s*\)$/m)>-1)n="";else return k}n=F(n,"*_");var p='<a href="'+n+'"';o!=""&&(o=o.replace(/"/g,"&quot;"),o=F(o,"*_"),p+=' title="'+o+'"'),p+=">"+l+"</a>";return p},m=function(a){a=a.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,n),a=a.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,n);return a},n=function(c,d,e,f,g,h,i,j){var k=d,l=e,m=f.toLowerCase(),n=g,o=j;o||(o="");if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(a[m]!=undefined)n=a[m],b[m]!=undefined&&(o=b[m]);else return k}l=l.replace(/"/g,"&quot;"),n=F(n,"*_");var p='<img src="'+n+'" alt="'+l+'"';o=o.replace(/"/g,"&quot;"),o=F(o,"*_"),p+=' title="'+o+'"',p+=" />";return p},o=function(a){function b(a){return a.replace(/[^\w]/g,"").toLowerCase()}a=a.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(a,c){return s('<h1 id="'+b(c)+'">'+i(c)+"</h1>")}),a=a.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(a,c){return s('<h2 id="'+b(c)+'">'+i(c)+"</h2>")}),a=a.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(a,c,d){var e=c.length;return s("<h"+e+' id="'+b(d)+'">'+i(d)+"</h"+e+">")});return a},p,q=function(a){a+="~0";var b=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;d?a=a.replace(b,function(a,b,c){var d=b,e=c.search(/[*+-]/g)>-1?"ul":"ol";d=d.replace(/\n{2,}/g,"\n\n\n");var f=p(d);f=f.replace(/\s+$/,""),f="<"+e+">"+f+"</"+e+">\n";return f}):(b=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g,a=a.replace(b,function(a,b,c,d){var e=b,f=c,g=d.search(/[*+-]/g)>-1?"ul":"ol",f=f.replace(/\n{2,}/g,"\n\n\n"),h=p(f);h=e+"<"+g+">\n"+h+"</"+g+">\n";return h})),a=a.replace(/~0/,"");return a};p=function(a){d++,a=a.replace(/\n{2,}$/,"\n"),a+="~0",a=a.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(a,b,c,d,e){var f=e,g=b,j=c;g||f.search(/\n{2,}/)>-1?f=h(D(f)):(f=q(D(f)),f=f.replace(/\n$/,""),f=i(f));return"<li>"+f+"</li>\n"}),a=a.replace(/~0/g,""),d--;return a};var r=function(a){a+="~0",a=a.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(a,b,c){var d=b,e=c;d=u(D(d)),d=E(d),d=d.replace(/^\n+/g,""),d=d.replace(/\n+$/g,""),d="<pre><code>"+d+"\n</code></pre>";return s(d)+e}),a=a.replace(/~0/,"");return a},s=function(a){a=a.replace(/(^\n+|\n+$)/g,"");return"\n\n~K"+(c.push(a)-1)+"K\n\n"},t=function(a){a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,b,c,d,e){var f=d;f=f.replace(/^([ \t]*)/g,""),f=f.replace(/[ \t]*$/g,""),f=u(f);return b+"<code>"+f+"</code>"});return a},u=function(a){a=a.replace(/&/g,"&amp;"),a=a.replace(/</g,"&lt;"),a=a.replace(/>/g,"&gt;"),a=F(a,"*_{}[]\\",!1);return a},v=function(a){a=a.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"<strong>$2</strong>"),a=a.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"<em>$2</em>");return a},w=function(a){a=a.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,b){var c=b;c=c.replace(/^[ \t]*>[ \t]?/gm,"~0"),c=c.replace(/~0/g,""),c=c.replace(/^[ \t]+$/gm,""),c=h(c),c=c.replace(/(^|\n)/g,"$1  "),c=c.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(a,b){var c=b;c=c.replace(/^  /mg,"~0"),c=c.replace(/~0/g,"");return c});return s("<blockquote>\n"+c+"\n</blockquote>")});return a},x=function(a){a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");var b=a.split(/\n{2,}/g),d=[],e=b.length;for(var f=0;f<e;f++){var g=b[f];g.search(/~K(\d+)K/g)>=0?d.push(g):g.search(/\S/)>=0&&(g=i(g),g=g.replace(/^([ \t]*)/g,"<p>"),g+="</p>",d.push(g))}e=d.length;for(var f=0;f<e;f++)while(d[f].search(/~K(\d+)K/)>=0){var h=c[RegExp.$1];h=h.replace(/\$/g,"$$$$"),d[f]=d[f].replace(/~K\d+K/,h)}return d.join("\n\n")},y=function(a){a=a.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;"),a=a.replace(/<(?![a-z\/?\$!])/gi,"&lt;");return a},z=function(a){a=a.replace(/\\(\\)/g,G),a=a.replace(/\\([`*_{}\[\]()>#+-.!])/g,G);return a},A=function(a){a=a.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,'<a href="$1">$1</a>'),a=a.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(a,b){return B(C(b))});return a},B=function(a){function b(a){var b="0123456789ABCDEF",c=a.charCodeAt(0);return b.charAt(c>>4)+b.charAt(c&15)}var c=[function(a){return"&#"+a.charCodeAt(0)+";"},function(a){return"&#x"+b(a)+";"},function(a){return a}];a="mailto:"+a,a=a.replace(/./g,function(a){if(a=="@")a=c[Math.floor(Math.random()*2)](a);else if(a!=":"){var b=Math.random();a=b>.9?c[2](a):b>.45?c[1](a):c[0](a)}return a}),a='<a href="'+a+'">'+a+"</a>",a=a.replace(/">.+:/g,'">');return a},C=function(a){a=a.replace(/~E(\d+)E/g,function(a,b){var c=parseInt(b);return String.fromCharCode(c)});return a},D=function(a){a=a.replace(/^(\t|[ ]{1,4})/gm,"~0"),a=a.replace(/~0/g,"");return a},E=function(a){a=a.replace(/\t(?=\t)/g,"    "),a=a.replace(/\t/g,"~A~B"),a=a.replace(/~B(.+?)~A/g,function(a,b,c){var d=b,e=4-d.length%4;for(var f=0;f<e;f++)d+=" ";return d}),a=a.replace(/~A/g,"    "),a=a.replace(/~B/g,"");return a},F=function(a,b,c){var d="(["+b.replace(/([\[\]\\])/g,"\\$1")+"])";c&&(d="\\\\"+d);var e=new RegExp(d,"g");a=a.replace(e,G);return a},G=function(a,b){var c=b.charCodeAt(0);return"~E"+c+"E"}},typeof exports!="undefined"&&(exports.Showdown=Showdown)
\ No newline at end of file
index f23bf6b..4dc32bf 100644 (file)
@@ -14,41 +14,86 @@ section.graph
         position relative
     
     fieldset.options
+        border-radius 5px
         legend
-            position relative
+            width auto
+            display inline-block
     
     .field.option
+        float left
         padding 0.5em
         margin 0.5em
         width 200px
         min-height 2em
+        overflow hidden
+        
+        border-radius 5px
+        background-color #ccc
+        font-size 90%
+        
+        .close
+            z-index 10
+            absolute top 0.5em right 0.5em
+            width 1em
+            height 1em
+            
+            opacity 1
+            color rgba(51,51,51, 0.4)
+            text-align center
+            border 1px solid #afafaf
+            border-radius 50%
+            background-color #ccc
+            
+            text-decoration none
+            cursor pointer
+        
+        .close:hover
+            color rgba(51,51,51, 1)
+            border 1px solid #999
         
         .shortname
-            display none
+            z-index -1
+            absolute bottom 0.5em right 0.5em
+            font-weight bold
+            color white
+        .name
+            font-weight bold
+            line-height 1.5
+            font-size 100%
         .value
-            position relative
+            width auto
         .type
-            position relative
+            &::before
+                content "Type: "
         .default
-            position relative
+            &::before
+                content "Default: "
         .desc
             position relative
         .tags
+            &::before
+                content "Tags"
+            ul
+                display none
             .tag
                 position relative
         .examples
+            &::before
+                content "Examples"
             .example
                 position relative
         
         
         &.collapsed
-            float left
-            min-width 2em
+            width auto
+            min-width 50px
             min-height 2em
-            background-color #ccc
+            line-height 2em
+            text-align center
             
             *
                 display none
             .shortname
                 display inline
-            
+                position static
+        
diff --git a/www/css/isotope.css b/www/css/isotope.css
new file mode 100644 (file)
index 0000000..e36bf78
--- /dev/null
@@ -0,0 +1,57 @@
+/**** Isotope Filtering ****/
+
+.isotope-item {
+  z-index: 2;
+}
+
+.isotope-hidden.isotope-item {
+  pointer-events: none;
+  z-index: 1;
+}
+
+/**** Isotope CSS3 transitions ****/
+
+.isotope,
+.isotope .isotope-item {
+  -webkit-transition-duration: 0.8s;
+     -moz-transition-duration: 0.8s;
+       -o-transition-duration: 0.8s;
+          transition-duration: 0.8s;
+}
+
+.isotope {
+  -webkit-transition-property: height, width;
+     -moz-transition-property: height, width;
+       -o-transition-property: height, width;
+          transition-property: height, width;
+}
+
+.isotope .isotope-item {
+  -webkit-transition-property: -webkit-transform, opacity;
+     -moz-transition-property:    -moz-transform, opacity;
+       -o-transition-property:         top, left, opacity;
+          transition-property:         transform, opacity;
+}
+
+/**** disabling Isotope CSS3 transitions ****/
+
+.isotope.no-transition,
+.isotope.no-transition .isotope-item,
+.isotope .isotope-item.no-transition {
+  -webkit-transition-duration: 0s;
+     -moz-transition-duration: 0s;
+       -o-transition-duration: 0s;
+          transition-duration: 0s;
+}
+
+/* End: Recommended Isotope styles */
+
+
+
+/* disable CSS transitions for containers with infinite scrolling*/
+.isotope.infinite-scrolling {
+  -webkit-transition: none;
+     -moz-transition: none;
+       -o-transition: none;
+          transition: none;
+}
index 4a30559..906ecf6 100644 (file)
@@ -6,5 +6,6 @@ block title
 append styles
     mixin css('forms.css')
     mixin css('graph.css')
+    mixin css('isotope.css')
 
 
index f9b910a..4f64153 100644 (file)
@@ -9,7 +9,7 @@ html
             meta(name="viewport", content="width=device-width, initial-scale=1.0")
             
             block styles
-                mixin css('reset.min.css')
+                //- mixin css('reset.min.css')
                 mixin css('/vendor/bootstrap/css/bootstrap.css')
                 mixin css('layout.css')
             
@@ -21,7 +21,7 @@ html
     
     body
         block body
-            header
+            header.navbar.navbar-fixed-top: .navbar-inner: .container
                 block header
                     h1: a(href="/") Kraken
                     block nav
index 6671c68..8de244f 100644 (file)
@@ -18,6 +18,7 @@ all:
         - underscore.mod
         - underscore.string.mod
         - backbone
+        - showdown.mod
         - jade.runtime.min
         - dygraph
 
@@ -37,7 +38,10 @@ all:
             - template:
                 - graph.jade
                 - graph-option.jade
-            - scaffold
+            - scaffold:
+                - model
+                - view
+                - index
             - graph
 
 # -   suffix: .js