├── .gitignore ├── Clippings ├── Javascript.js │ ├── case │ ├── fori - for loop, incremental │ ├── function │ ├── objd - object definition │ ├── propf - Object property function │ ├── prototype - function │ └── switch ├── Python.py │ ├── class │ ├── classi - class with initialize │ ├── def │ ├── try │ └── unittest │ │ ├── asde - assertDictEqual │ │ ├── ase - assertEqual │ │ ├── asle - assertListEqual │ │ ├── asn - assertIsNone │ │ ├── asnn - assertIsNotNone │ │ └── asra - assertRaises └── Ruby.rb │ ├── def │ ├── do │ ├── if │ ├── ifelse - if … else │ └── ifelsif - if … elsif … else ├── Color Schemes ├── Daring Fireball.bbcolors ├── Default.bbcolors ├── Developer.bbcolors ├── GitHub.bbcolors ├── Gruber Dark.bbcolors ├── Gruber Light.bbcolors ├── Monokai.bbcolors ├── Solarized Dark.bbcolors └── Solarized Light.bbcolors ├── Language Modules ├── BBColors Scheme.plist ├── CoffeeScript.plist ├── Diff.plist └── GitBlame.plist ├── Preview CSS ├── Byword.css └── iA Writer.css ├── README.markdown ├── Rakefile ├── Scripts ├── Elements.applescript ├── Make Package.rb ├── Maketags.applescript ├── Markdown Syntax.applescript ├── PeepOpen.applescript ├── Preview in Marked.scpt └── manpage.applescript ├── Text Filters ├── JSON Pretty Print.py ├── JavaScript Beautify.py └── Markdown.pl └── init.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Auto-Save Recovery 2 | Scratchpad 3 | -------------------------------------------------------------------------------- /Clippings/Javascript.js/case: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | case #SELSTART#val#SELEND#: 3 | #PLACEHOLDERSTART#code here#PLACEHOLDEREND#; 4 | break; 5 | 6 | case#INSERTION# -------------------------------------------------------------------------------- /Clippings/Javascript.js/fori - for loop, incremental: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | for (#SELSTART#i = 0; i < max; i++#SELEND#) { 3 | #INSERTION# 4 | } -------------------------------------------------------------------------------- /Clippings/Javascript.js/function: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | function #SELSTART#name#SELEND#(#PLACEHOLDERSTART#parameters#PLACEHOLDEREND#) { 3 | #PLACEHOLDERSTART#function_body#PLACEHOLDEREND# 4 | } -------------------------------------------------------------------------------- /Clippings/Javascript.js/objd - object definition: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | #SELSTART#objectName#SELEND# = { 3 | #PLACEHOLDERSTART#propName#PLACEHOLDEREND#: function(#PLACEHOLDERSTART#parameters#PLACEHOLDEREND#) { 4 | #PLACEHOLDERSTART#function_body#PLACEHOLDEREND# 5 | }, 6 | 7 | #INSERTION# 8 | }; -------------------------------------------------------------------------------- /Clippings/Javascript.js/propf - Object property function: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | #SELSTART#propName#SELEND#: function(#PLACEHOLDERSTART#parameters#PLACEHOLDEREND#) { 3 | #PLACEHOLDERSTART#function_body#PLACEHOLDEREND# 4 | }, -------------------------------------------------------------------------------- /Clippings/Javascript.js/prototype - function: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | #SELSTART#className#SELEND#.prototype.#PLACEHOLDERSTART#methodName#PLACEHOLDEREND# = function(#PLACEHOLDERSTART#parameters#PLACEHOLDEREND#) { 3 | #PLACEHOLDERSTART#function_body#PLACEHOLDEREND# 4 | } -------------------------------------------------------------------------------- /Clippings/Javascript.js/switch: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | switch (#SELSTART#test_value#SELEND#) { 3 | case #SELSTART#val#SELEND#: 4 | #PLACEHOLDERSTART#code here#PLACEHOLDEREND#; 5 | break; 6 | 7 | case#INSERTION# 8 | 9 | default: 10 | #PLACEHOLDERSTART#code here#PLACEHOLDEREND# 11 | } -------------------------------------------------------------------------------- /Clippings/Python.py/class: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | class #PLACEHOLDERSTART#name#PLACEHOLDEREND#: 3 | #INSERTION# 4 | -------------------------------------------------------------------------------- /Clippings/Python.py/classi - class with initialize: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | class #PLACEHOLDERSTART#name#PLACEHOLDEREND#(#PLACEHOLDERSTART#parent#PLACEHOLDEREND#): 3 | def __init__(self, #PLACEHOLDERSTART##PLACEHOLDEREND#): 4 | #PLACEHOLDERSTART#body#PLACEHOLDEREND# 5 | 6 | #INSERTION# 7 | -------------------------------------------------------------------------------- /Clippings/Python.py/def: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | def #SELSTART#method#SELEND#(#PLACEHOLDERSTART#args#PLACEHOLDEREND#): 3 | #PLACEHOLDERSTART#body#PLACEHOLDEREND# 4 | 5 | -------------------------------------------------------------------------------- /Clippings/Python.py/try: -------------------------------------------------------------------------------- 1 | #INDENT#try: 2 | #PLACEHOLDERSTART#body#PLACEHOLDEREND# 3 | except #PLACEHOLDERSTART#exception#PLACEHOLDEREND#, e: 4 | #PLACEHOLDERSTART#exception_handler#PLACEHOLDEREND# 5 | 6 | -------------------------------------------------------------------------------- /Clippings/Python.py/unittest/asde - assertDictEqual: -------------------------------------------------------------------------------- 1 | #INDENT#self.assertDictEqual(#PLACEHOLDERSTART#expected#PLACEHOLDEREND#, #PLACEHOLDERSTART#actual#PLACEHOLDEREND#) -------------------------------------------------------------------------------- /Clippings/Python.py/unittest/ase - assertEqual: -------------------------------------------------------------------------------- 1 | #INDENT#self.assertEqual(#PLACEHOLDERSTART#expected#PLACEHOLDEREND#, #PLACEHOLDERSTART#actual#PLACEHOLDEREND#) -------------------------------------------------------------------------------- /Clippings/Python.py/unittest/asle - assertListEqual: -------------------------------------------------------------------------------- 1 | #INDENT#self.assertListEqual(#PLACEHOLDERSTART#expected#PLACEHOLDEREND#, #PLACEHOLDERSTART#actual#PLACEHOLDEREND#) -------------------------------------------------------------------------------- /Clippings/Python.py/unittest/asn - assertIsNone: -------------------------------------------------------------------------------- 1 | #INDENT#self.assertIsNone(#PLACEHOLDERSTART#actual#PLACEHOLDEREND#) -------------------------------------------------------------------------------- /Clippings/Python.py/unittest/asnn - assertIsNotNone: -------------------------------------------------------------------------------- 1 | #INDENT#self.assertIsNotNone(#PLACEHOLDERSTART#actual#PLACEHOLDEREND#) -------------------------------------------------------------------------------- /Clippings/Python.py/unittest/asra - assertRaises: -------------------------------------------------------------------------------- 1 | #INDENT#with self.assertRaises(#PLACEHOLDERSTART#exception#PLACEHOLDEREND#): 2 | #PLACEHOLDERSTART#testbody#PLACEHOLDEREND# -------------------------------------------------------------------------------- /Clippings/Ruby.rb/def: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | def #SELSTART#method#SELEND#(#PLACEHOLDERSTART#args#PLACEHOLDEREND#) 3 | #PLACEHOLDERSTART#body#PLACEHOLDEREND# 4 | end 5 | -------------------------------------------------------------------------------- /Clippings/Ruby.rb/do: -------------------------------------------------------------------------------- 1 | #INDENT# 2 | do |#SELSTART#var#SELEND#| 3 | #PLACEHOLDERSTART#body#PLACEHOLDEREND# 4 | end 5 | -------------------------------------------------------------------------------- /Clippings/Ruby.rb/if: -------------------------------------------------------------------------------- 1 | #INDENT#if #SELSTART#condition#SELEND# 2 | #PLACEHOLDERSTART#true#PLACEHOLDEREND# 3 | end 4 | -------------------------------------------------------------------------------- /Clippings/Ruby.rb/ifelse - if … else: -------------------------------------------------------------------------------- 1 | #INDENT#if #SELSTART#condition#SELEND# 2 | #PLACEHOLDERSTART#true#PLACEHOLDEREND# 3 | else 4 | #PLACEHOLDERSTART#false#PLACEHOLDEREND# 5 | end 6 | -------------------------------------------------------------------------------- /Clippings/Ruby.rb/ifelsif - if … elsif … else: -------------------------------------------------------------------------------- 1 | #INDENT#if #SELSTART#condition#SELEND# 2 | #PLACEHOLDERSTART#true#PLACEHOLDEREND# 3 | elsif #PLACEHOLDERSTART#else if condition#PLACEHOLDEREND# 4 | #PLACEHOLDERSTART#true#PLACEHOLDEREND# 5 | else 6 | #PLACEHOLDERSTART#false#PLACEHOLDEREND# 7 | end 8 | -------------------------------------------------------------------------------- /Color Schemes/Daring Fireball.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BackgroundColor 6 | rgba(0.228681,0.252353,0.279177,1.000000) 7 | CommentsColor 8 | rgba(0.666667,0.666667,0.666667,1.0) 9 | CtagsIdentifierColor 10 | rgba(0.000000,0.501961,0.501961,1.0) 11 | ForegroundColor 12 | rgba(0.916529,0.916705,0.916500,1.000000) 13 | HTMLAnchorColor 14 | rgba(0.666667,0.000000,0.000000,1.0) 15 | HTMLAttributeNameColor 16 | rgba(0.000000,0.501961,0.501961,1.0) 17 | HTMLAttributeValueColor 18 | rgba(0.866667,0.066667,0.266667,1.0) 19 | HTMLImageColor 20 | rgba(0.000000,0.466667,0.466667,1.0) 21 | HTMLProcessingDirectiveColor 22 | rgba(0.600000,0.600000,0.600000,1.0) 23 | HTMLTagColor 24 | rgba(0.000000,0.000000,0.501961,1.0) 25 | HighlightInsertionPoint 26 | 27 | InsertionPointLineHighlightColor 28 | rgba(0.228681,0.252353,0.279177,1.000000) 29 | InvisibleOthersColor 30 | rgba(0.866666667,0.031372549,0.023529412,1.0) 31 | InvisibleSpacesColor 32 | rgba(0.498039216,0.498039216,0.498039216,1.0) 33 | KeywordsColor 34 | rgba(0.000000,0.525490,0.701961,1.0) 35 | NumericConstantColor 36 | rgba(0.000000,0.600000,0.600000,1.0) 37 | PredefinedNamesColor 38 | rgba(0.067765,0.504417,0.719463,1.0) 39 | PrimaryHighlightColor 40 | rgba(0.333088,0.366723,0.400253,1.000000) 41 | PythonDecoratorColor 42 | rgba(0.200, 0.400, 0.600, 1.000) 43 | SecondaryHighlightColor 44 | rgba(0.677958,0.682479,0.687188,1.000000) 45 | SpellingColor 46 | rgba(1.0, 0.498039216, 0.498039216,1.0) 47 | StringColor 48 | rgba(0.866667,0.066667,0.266667,1.0) 49 | UseCustomHighlightColor 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Color Schemes/Default.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | (null)_Other 6 | rgba(0.866667,0.031373,0.023529,1.0) 7 | (null)_Spaces 8 | rgba(0.498039,0.498039,0.498039,1.0) 9 | BackgroundColor 10 | rgba(1.000000,1.000000,1.000000,1.0) 11 | Color:GuideContrast 12 | 4 13 | Color:UseCustomHighlight 14 | 1 15 | CommentsColor 16 | rgba(0.333333,0.333333,0.333333,1.0) 17 | CtagsIdentifierColor 18 | rgba(0.600000,0.000000,0.400000,1.0) 19 | ForegroundColor 20 | rgba(0.000000,0.000000,0.000000,1.0) 21 | HTMLAnchorColor 22 | rgba(0.666667,0.000000,0.000000,1.0) 23 | HTMLAttributeNameColor 24 | rgba(0.600000,0.000000,0.400000,1.0) 25 | HTMLAttributeValueColor 26 | rgba(0.600000,0.200000,0.000000,1.0) 27 | HTMLImageColor 28 | rgba(0.000000,0.466667,0.466667,1.0) 29 | HTMLProcessingDirectiveColor 30 | rgba(0.200000,0.400000,0.600000,1.0) 31 | HTMLTagColor 32 | rgba(0.000000,0.000000,0.800000,1.0) 33 | InsertionPointLineHighlightColor 34 | rgba(1.000000,1.000000,0.800000,1.0) 35 | KeywordsColor 36 | rgba(0.000000,0.000000,0.800000,1.0) 37 | NumericConstantColor 38 | rgba(0.000000,0.466667,0.000000,1.0) 39 | PredefinedNamesColor 40 | rgba(0.067765,0.504417,0.719463,1.0) 41 | PrimaryHighlightColor 42 | rgba(0.749020,0.847059,0.980392,1.0) 43 | SecondaryHighlightColor 44 | rgba(0.815686,0.815686,0.815686,1.0) 45 | StringColor 46 | rgba(1.000000,0.200000,0.600000,1.0) 47 | 48 | 49 | -------------------------------------------------------------------------------- /Color Schemes/Developer.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BackgroundColor 6 | rgba(0.816144,0.816301,0.816118,1.000000) 7 | CommentsColor 8 | rgba(0.666667,0.666667,0.666667,1.0) 9 | CtagsIdentifierColor 10 | rgba(0.000000,0.501961,0.501961,1.0) 11 | ForegroundColor 12 | rgba(0.000000,0.000000,0.000000,1.0) 13 | HTMLAnchorColor 14 | rgba(0.666667,0.000000,0.000000,1.0) 15 | HTMLAttributeNameColor 16 | rgba(0.000000,0.501961,0.501961,1.0) 17 | HTMLAttributeValueColor 18 | rgba(0.866667,0.066667,0.266667,1.0) 19 | HTMLImageColor 20 | rgba(0.000000,0.466667,0.466667,1.0) 21 | HTMLProcessingDirectiveColor 22 | rgba(0.600000,0.600000,0.600000,1.0) 23 | HTMLTagColor 24 | rgba(0.000000,0.000000,0.501961,1.0) 25 | HighlightInsertionPoint 26 | 27 | InsertionPointLineHighlightColor 28 | rgba(0.649518,0.788481,1.000000,1.000000) 29 | InvisibleOthersColor 30 | rgba(0.866666667,0.031372549,0.023529412,1.0) 31 | InvisibleSpacesColor 32 | rgba(0.498039216,0.498039216,0.498039216,1.0) 33 | KeywordsColor 34 | rgba(0.000000,0.525490,0.701961,1.0) 35 | NumericConstantColor 36 | rgba(0.000000,0.600000,0.600000,1.0) 37 | PredefinedNamesColor 38 | rgba(0.067765,0.504417,0.719463,1.0) 39 | PrimaryHighlightColor 40 | rgba(0.649518,0.788481,1.000000,1.000000) 41 | PythonDecoratorColor 42 | rgba(0.200, 0.400, 0.600, 1.000) 43 | SecondaryHighlightColor 44 | rgba(0.677958,0.682479,0.687188,1.000000) 45 | SpellingColor 46 | rgba(1.0, 0.498039216, 0.498039216,1.0) 47 | StringColor 48 | rgba(0.866667,0.066667,0.266667,1.0) 49 | UseCustomHighlightColor 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Color Schemes/GitHub.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | (null)_Other 6 | rgba(0.866667,0.031373,0.023529,1.0) 7 | (null)_Spaces 8 | rgba(0.498039,0.498039,0.498039,1.0) 9 | BackgroundColor 10 | rgba(1.000000,1.000000,1.000000,1.0) 11 | BackgroundColor_Grep Replace Pattern 12 | rgba(0.960021,0.960021,0.960021,1.0) 13 | BackgroundColor_Grep Search Pattern 14 | rgba(0.960021,0.960021,0.960021,1.0) 15 | Color:ColorAttributesSeparately 16 | 1 17 | Color:GuideContrast 18 | 4 19 | Color:HighlightInsertionPoint 20 | 1 21 | Color:UseCustomHighlight 22 | 1 23 | Color:UseCustomHighlight:Grep Replace Pattern 24 | 0 25 | Color:UseCustomHighlight:Grep Search Pattern 26 | 0 27 | CommentsColor 28 | rgba(0.666667,0.666667,0.666667,1.0) 29 | CommentsColor_Markdown 30 | rgba(0.628199,0.028321,0.000000,1.0) 31 | CtagsIdentifierColor 32 | rgba(0.000000,0.501961,0.501961,1.0) 33 | ForegroundColor 34 | rgba(0.000000,0.000000,0.000000,1.0) 35 | ForegroundColor_Grep Replace Pattern 36 | rgba(0.000000,0.000000,0.000000,1.0) 37 | ForegroundColor_Grep Search Pattern 38 | rgba(0.000000,0.000000,0.000000,1.0) 39 | HTMLAnchorColor 40 | rgba(0.666667,0.000000,0.000000,1.0) 41 | HTMLAnchorColor_Markdown 42 | rgba(0.717174,0.717174,0.717174,1.0) 43 | HTMLAttributeNameColor 44 | rgba(0.000000,0.501961,0.501961,1.0) 45 | HTMLAttributeNameColor_Markdown 46 | rgba(0.366934,0.592737,0.956863,1.0) 47 | HTMLAttributeValueColor 48 | rgba(0.866667,0.066667,0.266667,1.0) 49 | HTMLAttributeValueColor_Markdown 50 | rgba(0.488289,0.699229,0.277348,1.0) 51 | HTMLImageColor 52 | rgba(0.000000,0.466667,0.466667,1.0) 53 | HTMLImageColor_Markdown 54 | rgba(0.717174,0.717174,0.717174,1.0) 55 | HTMLProcessingDirectiveColor 56 | rgba(0.600000,0.600000,0.600000,1.0) 57 | HTMLProcessingDirectiveColor_Markdown 58 | rgba(0.800000,0.549020,0.235294,1.0) 59 | HTMLTagColor 60 | rgba(0.000000,0.000000,0.501961,1.0) 61 | InsertionPointLineHighlightColor 62 | rgba(0.972549,0.972549,1.000000,1.0) 63 | KeywordsColor 64 | rgba(0.000000,0.525490,0.701961,1.0) 65 | KeywordsColor_Grep Replace Pattern 66 | rgba(0.000000,0.000000,0.915541,1.0) 67 | KeywordsColor_Grep Search Pattern 68 | rgba(0.000000,0.000000,0.915541,1.0) 69 | KeywordsColor_Markdown 70 | rgba(0.449027,0.531975,0.605020,1.0) 71 | NumericConstantColor 72 | rgba(0.000000,0.600000,0.600000,1.0) 73 | PredefinedNamesColor 74 | rgba(0.067765,0.504417,0.719463,1.0) 75 | PrimaryHighlightColor 76 | rgba(0.909804,0.909804,0.933333,1.0) 77 | SecondaryHighlightColor 78 | rgba(0.847059,0.847059,0.866667,1.0) 79 | StringColor 80 | rgba(0.866667,0.066667,0.266667,1.0) 81 | StringColor_Markdown 82 | rgba(0.200000,0.200000,0.705882,1.0) 83 | 84 | 85 | -------------------------------------------------------------------------------- /Color Schemes/Gruber Dark.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BackgroundColor 6 | rgba(0.157259,0.157259,0.157259,1.0) 7 | BackgroundColor_Grep Replace Pattern 8 | rgba(0.960021,0.960021,0.960021,1.0) 9 | BackgroundColor_Grep Search Pattern 10 | rgba(0.960021,0.960021,0.960021,1.0) 11 | Color:ColorAttributesSeparately 12 | 1 13 | Color:GuideContrast 14 | 2 15 | Color:HighlightInsertionPoint 16 | 0 17 | Color:UseCustomHighlight 18 | 1 19 | Color:UseCustomHighlight:Grep Replace Pattern 20 | 0 21 | Color:UseCustomHighlight:Grep Search Pattern 22 | 0 23 | CommentsColor 24 | rgba(0.800000,0.549020,0.235294,1.0) 25 | CommentsColor_Markdown 26 | rgba(0.656687,0.656687,0.656687,1.0) 27 | CtagsIdentifierColor 28 | rgba(0.600000,0.000000,0.400000,1.0) 29 | ForegroundColor 30 | rgba(0.955642,0.955642,1.000000,1.0) 31 | ForegroundColor_Grep Replace Pattern 32 | rgba(0.000000,0.000000,0.000000,1.0) 33 | ForegroundColor_Grep Search Pattern 34 | rgba(0.000000,0.000000,0.000000,1.0) 35 | HTMLAnchorColor 36 | rgba(0.366934,0.592737,0.956863,1.0) 37 | HTMLAnchorColor_Markdown 38 | rgba(0.717174,0.717174,0.717174,1.0) 39 | HTMLAttributeNameColor 40 | rgba(0.584680,0.662745,0.623529,1.0) 41 | HTMLAttributeNameColor_Markdown 42 | rgba(0.366934,0.592737,0.956863,1.0) 43 | HTMLAttributeValueColor 44 | rgba(0.450980,0.800000,0.211765,1.0) 45 | HTMLAttributeValueColor_Markdown 46 | rgba(0.488289,0.699229,0.277348,1.0) 47 | HTMLImageColor 48 | rgba(0.521569,0.556863,0.956863,1.0) 49 | HTMLImageColor_Markdown 50 | rgba(0.717174,0.717174,0.717174,1.0) 51 | HTMLProcessingDirectiveColor 52 | rgba(1.000000,0.866941,0.200000,1.0) 53 | HTMLProcessingDirectiveColor_Markdown 54 | rgba(0.800000,0.549020,0.235294,1.0) 55 | HTMLTagColor 56 | rgba(0.521569,0.556863,0.956863,1.0) 57 | InsertionPointLineHighlightColor 58 | rgba(0.152941,0.152941,0.152941,1.0) 59 | KeywordsColor 60 | rgba(0.588235,0.650980,0.784314,1.0) 61 | KeywordsColor_Grep Replace Pattern 62 | rgba(0.000000,0.000000,0.915541,1.0) 63 | KeywordsColor_Grep Search Pattern 64 | rgba(0.000000,0.000000,0.915541,1.0) 65 | KeywordsColor_Markdown 66 | rgba(0.449027,0.531975,0.605020,1.0) 67 | PrimaryHighlightColor 68 | rgba(0.373800,0.383276,0.496376,1.0) 69 | SecondaryHighlightColor 70 | rgba(0.283711,0.286687,0.317082,1.0) 71 | StringColor 72 | rgba(0.450980,0.786297,0.211765,1.0) 73 | StringColor_Markdown 74 | rgba(0.592233,0.599557,0.718212,1.0) 75 | 76 | 77 | -------------------------------------------------------------------------------- /Color Schemes/Gruber Light.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BackgroundColor 6 | rgba(0.929412,0.929412,0.925490,1.0) 7 | BackgroundColor_Grep Replace Pattern 8 | rgba(0.960021,0.960021,0.960021,1.0) 9 | BackgroundColor_Grep Search Pattern 10 | rgba(0.960021,0.960021,0.960021,1.0) 11 | Color:ColorAttributesSeparately 12 | 1 13 | Color:GuideContrast 14 | 4 15 | Color:HighlightInsertionPoint 16 | 0 17 | Color:UseCustomHighlight 18 | 1 19 | Color:UseCustomHighlight:Grep Replace Pattern 20 | 0 21 | Color:UseCustomHighlight:Grep Search Pattern 22 | 0 23 | CommentsColor 24 | rgba(0.607156,0.246418,0.136690,1.0) 25 | CommentsColor_Markdown 26 | rgba(0.628199,0.028321,0.000000,1.0) 27 | CtagsIdentifierColor 28 | rgba(0.600000,0.000000,0.400000,1.0) 29 | ForegroundColor 30 | rgba(0.000000,0.000000,0.000000,1.0) 31 | ForegroundColor_Grep Replace Pattern 32 | rgba(0.000000,0.000000,0.000000,1.0) 33 | ForegroundColor_Grep Search Pattern 34 | rgba(0.000000,0.000000,0.000000,1.0) 35 | HTMLAnchorColor 36 | rgba(0.450004,0.450004,0.450004,1.0) 37 | HTMLAttributeNameColor 38 | rgba(0.298039,0.298039,0.498039,1.0) 39 | HTMLAttributeValueColor 40 | rgba(0.221576,0.493416,0.218296,1.0) 41 | HTMLImageColor 42 | rgba(0.450004,0.450004,0.450004,1.0) 43 | HTMLProcessingDirectiveColor 44 | rgba(0.200000,0.400000,0.600000,1.0) 45 | HTMLTagColor 46 | rgba(0.450004,0.450004,0.450004,1.0) 47 | InsertionPointLineHighlightColor 48 | rgba(0.823529,0.823529,0.823529,1.0) 49 | KeywordsColor 50 | rgba(0.200000,0.200000,0.705882,1.0) 51 | PrimaryHighlightColor 52 | rgba(0.721569,0.737255,0.752941,1.0) 53 | SecondaryHighlightColor 54 | rgba(0.768627,0.768627,0.784314,1.0) 55 | StringColor 56 | rgba(0.000000,0.502464,0.000000,1.0) 57 | StringColor_Markdown 58 | rgba(0.200000,0.200000,0.705882,1.0) 59 | 60 | 61 | -------------------------------------------------------------------------------- /Color Schemes/Monokai.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | (null)_Other 6 | rgba(0.286275,0.282353,0.243137,1.0) 7 | (null)_Spaces 8 | rgba(0.286275,0.282353,0.243137,1.0) 9 | BackgroundColor 10 | rgba(0.152941,0.156863,0.133333,1.0) 11 | BackgroundColor_Grep Replace Pattern 12 | rgba(0.960021,0.960021,0.960021,1.0) 13 | BackgroundColor_Grep Search Pattern 14 | rgba(0.960021,0.960021,0.960021,1.0) 15 | Color:ColorAttributesSeparately 16 | 1 17 | Color:GuideContrast 18 | 4 19 | Color:HighlightInsertionPoint 20 | 1 21 | Color:UseCustomHighlight 22 | 1 23 | Color:UseCustomHighlight:Grep Replace Pattern 24 | 0 25 | Color:UseCustomHighlight:Grep Search Pattern 26 | 0 27 | CommentsColor 28 | rgba(0.458824,0.443137,0.368627,1.0) 29 | CommentsColor_Markdown 30 | rgba(0.628199,0.028321,0.000000,1.0) 31 | CtagsIdentifierColor 32 | rgba(0.600000,0.000000,0.400000,1.0) 33 | ForegroundColor 34 | rgba(0.972549,0.972549,0.949020,1.0) 35 | ForegroundColor_Grep Replace Pattern 36 | rgba(0.000000,0.000000,0.000000,1.0) 37 | ForegroundColor_Grep Search Pattern 38 | rgba(0.000000,0.000000,0.000000,1.0) 39 | HTMLAnchorColor 40 | rgba(0.992157,0.592157,0.121569,1.0) 41 | HTMLAnchorColor_Markdown 42 | rgba(0.717174,0.717174,0.717174,1.0) 43 | HTMLAttributeNameColor 44 | rgba(0.976471,0.149020,0.447059,1.0) 45 | HTMLAttributeNameColor_Markdown 46 | rgba(0.366934,0.592737,0.956863,1.0) 47 | HTMLAttributeValueColor 48 | rgba(0.992157,0.592157,0.121569,1.0) 49 | HTMLAttributeValueColor_Markdown 50 | rgba(0.488289,0.699229,0.277348,1.0) 51 | HTMLImageColor 52 | rgba(0.650980,0.886275,0.180392,1.0) 53 | HTMLImageColor_Markdown 54 | rgba(0.717174,0.717174,0.717174,1.0) 55 | HTMLProcessingDirectiveColor 56 | rgba(0.400000,0.850980,0.937255,1.0) 57 | HTMLProcessingDirectiveColor_Markdown 58 | rgba(0.800000,0.549020,0.235294,1.0) 59 | HTMLTagColor 60 | rgba(0.682353,0.505882,1.000000,1.0) 61 | InsertionPointLineHighlightColor 62 | rgba(0.286275,0.282353,0.243137,1.0) 63 | KeywordsColor 64 | rgba(0.976471,0.149020,0.447059,1.0) 65 | KeywordsColor_Grep Replace Pattern 66 | rgba(0.000000,0.000000,0.915541,1.0) 67 | KeywordsColor_Grep Search Pattern 68 | rgba(0.000000,0.000000,0.915541,1.0) 69 | KeywordsColor_Markdown 70 | rgba(0.449027,0.531975,0.605020,1.0) 71 | NumericConstantColor 72 | rgba(0.682353,0.505882,1.000000,1.0) 73 | PredefinedNamesColor 74 | rgba(0.650980,0.886275,0.180392,1.0) 75 | PrimaryHighlightColor 76 | rgba(0.458824,0.443137,0.368627,1.0) 77 | SecondaryHighlightColor 78 | rgba(0.286275,0.282353,0.243137,1.0) 79 | StringColor 80 | rgba(0.901961,0.858824,0.454902,1.0) 81 | StringColor_Markdown 82 | rgba(0.200000,0.200000,0.705882,1.0) 83 | 84 | 85 | -------------------------------------------------------------------------------- /Color Schemes/Solarized Dark.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | (null)_Other 6 | rgba(0.039384,0.160113,0.198337,1.0) 7 | (null)_Spaces 8 | rgba(0.039384,0.160113,0.198337,1.0) 9 | BackgroundColor 10 | rgba(0.015930,0.126528,0.159701,1.0) 11 | Color:UseCustomHighlight 12 | 1 13 | CommentsColor 14 | rgba(0.440574,0.509636,0.516854,1.0) 15 | ForegroundColor 16 | rgba(0.916106,0.890013,0.797818,1.0) 17 | HTMLAnchorColor 18 | rgba(0.741756,0.213260,0.073533,1.0) 19 | HTMLAttributeNameColor 20 | rgba(0.127550,0.462654,0.782315,1.0) 21 | HTMLAttributeValueColor 22 | rgba(0.146792,0.570825,0.525017,1.0) 23 | HTMLImageColor 24 | rgba(0.777386,0.108019,0.435172,1.0) 25 | HTMLProcessingDirectiveColor 26 | rgba(0.647471,0.467521,0.023484,1.0) 27 | HTMLTagColor 28 | rgba(0.440574,0.509636,0.516854,1.0) 29 | InsertionPointLineHighlightColor 30 | rgba(0.039384,0.160113,0.198337,1.0) 31 | KeywordsColor 32 | rgba(0.127550,0.462654,0.782315,1.0) 33 | NumericConstantColor 34 | rgba(0.127550,0.462654,0.782315,1.0) 35 | PredefinedNamesColor 36 | rgba(0.741756,0.213260,0.073533,1.0) 37 | PrimaryHighlightColor 38 | rgba(0.324361,0.407172,0.438499,1.0) 39 | SecondaryHighlightColor 40 | rgba(0.039384,0.160113,0.198337,1.0) 41 | StringColor 42 | rgba(0.146792,0.570825,0.525017,1.0) 43 | 44 | 45 | -------------------------------------------------------------------------------- /Color Schemes/Solarized Light.bbcolors: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | (null)_Other 6 | rgba(0.916106,0.890013,0.797818,1.0) 7 | (null)_Spaces 8 | rgba(0.916106,0.890013,0.797818,1.0) 9 | BackgroundColor 10 | rgba(0.989441,0.957946,0.864057,1.0) 11 | Color:UseCustomHighlight 12 | 1 13 | CommentsColor 14 | rgba(0.440574,0.509636,0.516854,1.0) 15 | ForegroundColor 16 | rgba(0.015930,0.126528,0.159701,1.0) 17 | HTMLAnchorColor 18 | rgba(0.741756,0.213260,0.073533,1.0) 19 | HTMLAttributeNameColor 20 | rgba(0.127550,0.462654,0.782315,1.0) 21 | HTMLAttributeValueColor 22 | rgba(0.146792,0.570825,0.525017,1.0) 23 | HTMLImageColor 24 | rgba(0.777386,0.108019,0.435172,1.0) 25 | HTMLProcessingDirectiveColor 26 | rgba(0.647471,0.467521,0.023484,1.0) 27 | HTMLTagColor 28 | rgba(0.440574,0.509636,0.516854,1.0) 29 | InsertionPointLineHighlightColor 30 | rgba(0.916106,0.890013,0.797818,1.0) 31 | KeywordsColor 32 | rgba(0.127550,0.462654,0.782315,1.0) 33 | NumericConstantColor 34 | rgba(0.127550,0.462654,0.782315,1.0) 35 | PredefinedNamesColor 36 | rgba(0.741756,0.213260,0.073533,1.0) 37 | PrimaryHighlightColor 38 | rgba(0.505989,0.564858,0.563638,1.0) 39 | SecondaryHighlightColor 40 | rgba(0.916106,0.890013,0.797818,1.0) 41 | StringColor 42 | rgba(0.146792,0.570825,0.525017,1.0) 43 | 44 | 45 | -------------------------------------------------------------------------------- /Language Modules/BBColors Scheme.plist: -------------------------------------------------------------------------------- 1 | // BBEdit codeless language module for BBColors color setting files. // John Gruber // Version 1.0 // Wed 05 Oct 2006 // { BBEditDocumentType = 'CodelessLanguageModule'; BBLMLanguageDisplayName = 'BBColors Scheme File'; BBLMLanguageCode = 'BCol'; BBLMSuffixMap = ({BBLMLanguageSuffix = ".bbcolors"; }); BBLMIsCaseSensitive = 0; BBLMColorsSyntax = 1; BBLMScansFunctions = 0; BBLMKeywordList = (); "Language Features" = { "String Pattern" = '"(\\.|[^"])*+"'; "Comment Pattern" = "^[ \t]*//.*"; }; } // // Local Variables: // coding: utf-8-mac // tab-width: 4 // End: // -------------------------------------------------------------------------------- /Language Modules/CoffeeScript.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | BBEditDocumentType 15 | CodelessLanguageModule 16 | BBLMLanguageDisplayName 17 | CoffeeScript 18 | BBLMLanguageCode 19 | CoSc 20 | BBLMColorsSyntax 21 | 22 | BBLMScansFunctions 23 | 24 | BBLMIsCaseSensitive 25 | 26 | BBLMKeywordList 27 | 28 | if 29 | for 30 | in 31 | then 32 | else 33 | or 34 | of 35 | while 36 | until 37 | do 38 | try 39 | catch 40 | is 41 | isnt 42 | unless 43 | and 44 | not 45 | class 46 | extends 47 | super 48 | new 49 | this 50 | switch 51 | when 52 | finally 53 | return 54 | true 55 | false 56 | null 57 | 58 | BBLMSuffixMap 59 | 60 | 61 | BBLMLanguageSuffix 62 | .cs 63 | 64 | 65 | BBLMLanguageSuffix 66 | .coffee 67 | 68 | 69 | Language Features 70 | 71 | Identifier and Keyword Character Class 72 | 0-9A-Z_a-z 73 | 74 | Comment Pattern 75 | (?x: 76 | (?> \( ) | 77 | (?> \) ) | 78 | (?> \[ ) | 79 | (?> \] ) | 80 | (?> \{ ) | 81 | (?> \} ) | 82 | (?> \: ) | 83 | (?> \@ ) | 84 | (?> -> ) | 85 | (?> \# .* $ ) | 86 | (?> \#\#\# (?s:.*?) (?: \#\#\# | \z )) 87 | ) 88 | 89 | String Pattern 90 | (?x: 91 | (?> ` (?s: \\. | [^`] )*? (?: ` ) ) | 92 | (?> /// (?s: \\. | [^/] )*? (?: /// ) ) | 93 | (?> """ (?s: \\. | [^"] )*? (?: """ ) ) | 94 | (?> ''' (?s: \\. | [^'] )*? (?: ''' ) ) | 95 | (?> " (?s: \\. | [^"] )*? (?: " ) ) | 96 | (?> ' (?s: \\. | [^'] )*? (?: ' ) ) 97 | ) 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Language Modules/Diff.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 32 | 33 | BBEditDocumentType 34 | CodelessLanguageModule 35 | BBLMLanguageCode 36 | Diff 37 | BBLMColorsSyntax 38 | 39 | BBLMIsCaseSensitive 40 | 41 | BBLMLanguageDisplayName 42 | Diff Output 43 | BBLMScansFunctions 44 | 45 | 46 | BBLMSuffixMap 47 | 48 | 49 | BBLMLanguageSuffix 50 | .patch 51 | 52 | 53 | BBLMLanguageSuffix 54 | .diff 55 | 56 | 57 | 58 | BBLMKeywordList 59 | 60 | @@ 61 | 62 | 63 | Language Features 64 | 65 | Function Pattern 66 | ^(?:(?:commit|diff|\+\+\+).*?[\s/\\:](?P<function_name>\w[^\s/\\:~]+))(?:~|\s|$) 67 | String Pattern 68 | ^(?:-|< |>>>|--- ).*?$ 69 | Comment Pattern 70 | ^(?:\+|> |\+\+\+ ).*?$ 71 | Identifier and Keyword Characters 72 | 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz@. 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Language Modules/GitBlame.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | BBEditDocumentType 26 | CodelessLanguageModule 27 | BBLMLanguageCode 28 | GBlm 29 | BBLMColorsSyntax 30 | 31 | BBLMIsCaseSensitive 32 | 33 | BBLMLanguageDisplayName 34 | Git Blame 35 | BBLMScansFunctions 36 | 37 | 38 | BBLMSuffixMap 39 | 40 | 41 | BBLMLanguageSuffix 42 | .gitblame 43 | 44 | 45 | 46 | Language Features 47 | 48 | String Pattern 49 | ^\w{8} 50 | Comment Pattern 51 | \(.+ \d{4}-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d{4}\s+\d+\) 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /Preview CSS/Byword.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This document has been created with Marked.app . 3 | * Copyright 2011 Brett Terpstra 4 | * --------------------------------------------------------------------------- 5 | * Please leave this notice in place, along with any additional credits below. 6 | * 7 | * Byword.css theme is based on Byword.app 8 | * Authors: @brunodecarvalho, @jpedroso, @rcabaco 9 | * Copyright 2011 Metaclassy, Lda. 10 | */ 11 | 12 | html { 13 | font-size: 62.5%; /* base font-size: 10px */ 14 | } 15 | 16 | body { 17 | background-color: #f2f2f2; 18 | color: #3c3c3c; 19 | 20 | /* Change font size below */ 21 | font-size: 1.7em; 22 | line-height: 1.4em; 23 | 24 | /* Change font below */ 25 | 26 | /* Sans-serif fonts */ 27 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 28 | -webkit-font-smoothing: antialiased; 29 | 30 | /* Serif fonts */ 31 | /* 32 | font-family: "Cochin", "Baskerville", "Georgia", serif; 33 | -webkit-font-smoothing: subpixel-antialiased; 34 | */ 35 | 36 | /* Monospaced fonts */ 37 | /* 38 | font-family: "Courier New", Menlo, Monaco, mono; 39 | -webkit-font-smoothing: antialiased; 40 | */ 41 | } 42 | a { 43 | color: #308bd8; 44 | text-decoration:none; 45 | } 46 | a:hover { 47 | text-decoration: underline; 48 | } 49 | /* headings */ 50 | h1, h2 { 51 | line-height:1.2em; 52 | margin-top:32px; 53 | margin-bottom:12px; 54 | } 55 | h1:first-child { 56 | margin-top:0; 57 | } 58 | h3, h4, h5, h6 { 59 | margin-top:12px; 60 | margin-bottom:0; 61 | } 62 | h5, h6 { 63 | font-size:0.9em; 64 | line-height:1.0em; 65 | } 66 | /* end of headings */ 67 | p { 68 | margin:0 0 24px 0; 69 | } 70 | p:last-child { 71 | margin:0; 72 | } 73 | #wrapper hr { 74 | width: 100%; 75 | margin: 3em auto; 76 | border: 0; 77 | color: #eee; 78 | background-color: #ccc; 79 | height: 1px; 80 | -webkit-box-shadow:0px 1px 0px rgba(255, 255, 255, 0.75); 81 | } 82 | /* lists */ 83 | ol { 84 | list-style: outside decimal; 85 | } 86 | ul { 87 | list-style: outside disc; 88 | } 89 | ol, ul { 90 | padding-left:0; 91 | margin-bottom:24px; 92 | } 93 | ol li { 94 | margin-left:28px; 95 | } 96 | ul li { 97 | margin-bottom:8px; 98 | margin-left:16px; 99 | } 100 | ol:last-child, ul:last-child { 101 | margin:0; 102 | } 103 | li > ol, li > ul { 104 | padding-left:12px; 105 | } 106 | dl { 107 | margin-bottom:24px; 108 | } 109 | dl dt { 110 | font-weight:bold; 111 | margin-bottom:8px; 112 | } 113 | dl dd { 114 | margin-left:0; 115 | margin-bottom:12px; 116 | } 117 | dl dd:last-child, dl:last-child { 118 | margin-bottom:0; 119 | } 120 | /* end of lists */ 121 | pre { 122 | white-space: pre-wrap; 123 | width: 96%; 124 | margin-bottom: 24px; 125 | overflow: hidden; 126 | padding: 3px 10px; 127 | -webkit-border-radius: 3px; 128 | background-color: #eee; 129 | border: 1px solid #ddd; 130 | } 131 | code { 132 | white-space: nowrap; 133 | font-size: 1.1em; 134 | padding: 2px; 135 | -webkit-border-radius: 3px; 136 | background-color: #eee; 137 | border: 1px solid #ddd; 138 | } 139 | pre code { 140 | white-space: pre-wrap; 141 | border: none; 142 | padding: 0; 143 | background-color: transparent; 144 | -webkit-border-radius: 0; 145 | } 146 | blockquote { 147 | margin-left: 0; 148 | margin-right: 0; 149 | width: 96%; 150 | padding: 0 10px; 151 | border-left: 3px solid #ddd; 152 | color: #777; 153 | } 154 | table { 155 | margin-left: auto; 156 | margin-right: auto; 157 | margin-bottom: 24px; 158 | border-bottom: 1px solid #ddd; 159 | border-right: 1px solid #ddd; 160 | border-spacing: 0; 161 | } 162 | table th { 163 | padding: 3px 10px; 164 | background-color: #eee; 165 | border-top: 1px solid #ddd; 166 | border-left: 1px solid #ddd; 167 | } 168 | table tr { 169 | } 170 | table td { 171 | padding: 3px 10px; 172 | border-top: 1px solid #ddd; 173 | border-left: 1px solid #ddd; 174 | } 175 | caption { 176 | font-size: 1.2em; 177 | font-weight: bold; 178 | margin-bottom: 5px; 179 | } 180 | figure { 181 | display: block; 182 | text-align: center; 183 | } 184 | #wrapper img { 185 | border: none; 186 | display: block; 187 | margin: 1em auto; 188 | max-width: 100%; 189 | } 190 | figcaption { 191 | font-size: 0.8em; 192 | font-style: italic; 193 | } 194 | mark { 195 | background: #fefec0; 196 | padding:1px 3px; 197 | } 198 | 199 | 200 | /* classes */ 201 | 202 | .markdowncitation { 203 | } 204 | .footnote { 205 | font-size: 0.8em; 206 | vertical-align: super; 207 | } 208 | .footnotes ol { 209 | font-weight: bold; 210 | } 211 | .footnotes ol li p { 212 | font-weight: normal; 213 | } 214 | 215 | /* custom formatting classes */ 216 | 217 | .shadow { 218 | -webkit-box-shadow: 0 2px 4px #999; 219 | } 220 | 221 | .source { 222 | text-align: center; 223 | font-size: 0.8em; 224 | color: #777; 225 | margin: -40px; 226 | } 227 | 228 | @media screen { 229 | .inverted, .inverted #wrapper { 230 | background-color: #1a1a1a !important; 231 | color: #bebebe !important; 232 | 233 | /* SANS-SERIF */ 234 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !important; 235 | -webkit-font-smoothing: antialiased !important; 236 | 237 | /* SERIF */ 238 | /* 239 | font-family: "Cochin", "Baskerville", "Georgia", serif !important; 240 | -webkit-font-smoothing: subpixel-antialiased !important; 241 | */ 242 | /* MONO */ 243 | /* 244 | font-family: "Courier", mono !important; 245 | -webkit-font-smoothing: antialiased !important; 246 | */ 247 | } 248 | .inverted a { 249 | color: #308bd8 !important; 250 | } 251 | .inverted hr { 252 | color: #666 !important; 253 | border: 0; 254 | background-color: #666 !important; 255 | -webkit-box-shadow: none !important; 256 | } 257 | .inverted pre { 258 | background-color: #222 !important; 259 | border-color: #3c3c3c !important; 260 | } 261 | .inverted code { 262 | background-color: #222 !important; 263 | border-color: #3c3c3c !important; 264 | } 265 | .inverted blockquote { 266 | border-color: #333 !important; 267 | color: #999 !important; 268 | } 269 | .inverted table { 270 | border-color: #3c3c3c !important; 271 | } 272 | .inverted table th { 273 | background-color: #222 !important; 274 | border-color: #3c3c3c !important; 275 | } 276 | .inverted table td { 277 | border-color: #3c3c3c !important; 278 | } 279 | .inverted mark { 280 | background: #bc990b !important; 281 | color:#000 !important; 282 | } 283 | .inverted .shadow { -webkit-box-shadow: 0 2px 4px #000 !important; } 284 | #wrapper { 285 | background: transparent; 286 | margin: 40px; 287 | } 288 | } 289 | 290 | /* Printing support */ 291 | @media print { 292 | body { 293 | overflow: auto; 294 | } 295 | img, pre, blockquote, table, figure { 296 | page-break-inside: avoid; 297 | } 298 | pre, code { 299 | border: none !important; 300 | } 301 | #wrapper { 302 | background: #fff; 303 | position: relative; 304 | text-indent: 0px; 305 | padding: 10px; 306 | font-size:85%; 307 | } 308 | .footnotes { 309 | page-break-before: always; 310 | } 311 | } -------------------------------------------------------------------------------- /Preview CSS/iA Writer.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #222; 3 | font: normal normal 400 100%/1.5em georgia,serif; 4 | margin: 3em auto; 5 | width: 40em 6 | } 7 | 8 | a:link { 9 | color: #3366CC 10 | } 11 | 12 | a:visited { 13 | color: #224488 14 | } 15 | 16 | blockquote,ol,p,ul { 17 | display: block; 18 | margin: 0 0 1.5em 19 | } 20 | 21 | blockquote { 22 | border-left: solid .1em #E4E4E4; 23 | color: #919191; 24 | padding: 0 1.5em 0 1.4em 25 | } 26 | 27 | code { 28 | font: normal normal 87.5%/1.71428571em monospace,sans-serif 29 | } 30 | 31 | img { 32 | display: block; 33 | margin: 1.5em auto 34 | } 35 | 36 | pre { 37 | display: block; 38 | font: normal normal 87.5%/1.71428571em monospace,sans-serif; 39 | margin: 1.71428571em 40 | } 41 | 42 | h1,h2,h3,h4,h5,h6 { 43 | font-weight: 400 44 | } 45 | 46 | h1 { 47 | font-size: 225%; 48 | line-height: 1.3334em; 49 | margin: 0 0 .1666em 50 | } 51 | 52 | h2 { 53 | font-size: 175%; 54 | line-height: 1.28571429em; 55 | margin: 0 0 .35714286em 56 | } 57 | 58 | h3 { 59 | font-size: 137.5%; 60 | line-height: 1.3636em; 61 | margin: 0 0 .5em 62 | } 63 | 64 | h4,h5,h6 { 65 | font-size: 112.5%; 66 | line-height: 1.3334em; 67 | margin: 0 0 .7778em 68 | } 69 | 70 | ol,ul { 71 | list-style-position: outside; 72 | padding: 0 73 | } 74 | 75 | ol ol,ol ul,ul ol,ul ul { 76 | margin: 0 0 .75em 2em 77 | } 78 | 79 | table { 80 | border-collapse: collapse; 81 | margin: 1.5em 0; 82 | width: 100% 83 | } 84 | 85 | td,th { 86 | border: solid .1em #000; 87 | font-family: sans-serif; 88 | font-size: 87.5%; 89 | line-height: 1.71428571em; 90 | text-align: center 91 | } -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | BBEdit Application Support 2 | ========================== 3 | 4 | BBEdit language modules, clippings, scripts and customizations. 5 | 6 | I use BBEdit's Dropbox support. I maintain my customizations in this git repository. When I want to push my changes to my BBEdit install, I use the Rake installer to copy them. 7 | 8 | The Rakefile will prefer Dropbox but will fall back to the local `~/Library/Application Support` if Dropbox is not found. 9 | 10 | # Install 11 | 12 | git clone git@github.com:ascarter/BBEdit-ApplicationSupport.git 13 | cd BBEdit-ApplicationSupport 14 | rake install 15 | 16 | The `rakefile` script will create empty directories for the components if they do not exist in the BBEdit support directory. It will also prompt you to decide if you want to override existing files or not. 17 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'erb' 3 | require 'fileutils' 4 | 5 | task :default => [ :install ] 6 | 7 | desc "Install BBEdit support files to Application Support/BBEdit" 8 | task :install do 9 | replace_all = false 10 | source_root = File.expand_path(File.join(__FILE__, '..')) 11 | source_dirs = [ 12 | 'Clippings', 'Color Schemes', 'Language Modules', 'Preview CSS', 13 | 'Scripts', 'Text Filters' 14 | ] 15 | home_dir = File.expand_path(ENV['HOME']) 16 | 17 | # Find application support directory 18 | appsupport_root = File.join(home_dir, 'Dropbox', 'Application Support', 'BBEdit') 19 | if not File.exist?(appsupport_root) 20 | appsupport_root = File.join(home_dir, 'Library', 'Application Support', 'BBEdit') 21 | if not File.exist?(appsupport_root) 22 | raise IOError.new('No BBEdit directory found in ~/Dropbox/Application Support or ~/Library/Application Support') 23 | end 24 | end 25 | 26 | puts "Installing to #{appsupport_root}" 27 | 28 | # Install files 29 | source_dirs.each do |d| 30 | source_dir = File.join(source_root, d) 31 | target_dir = File.join(appsupport_root, d) 32 | 33 | # Create the target dir if it doesn't exist 34 | Dir.mkdir(target_dir) unless File.directory?(target_dir) 35 | 36 | # Get entry in source 37 | Dir.glob(File.join(source_dir, '*')).each do |f| 38 | filename = File.basename(f) 39 | target = File.join(target_dir, filename) 40 | if File.exist?(target) 41 | if File.identical?(f, target) 42 | puts "Identical #{d}/#{filename}" 43 | else 44 | if replace_all 45 | copy(f, target) 46 | else 47 | print "Replace existing file #{d}/#{filename}? [ynaq] " 48 | case $stdin.gets.chomp 49 | when 'a' 50 | replace_all = true 51 | copy(f, target) 52 | when 'y' 53 | copy(f, target) 54 | when 'q' 55 | puts "Abort" 56 | exit 57 | else 58 | puts "Skipping #{d}/#{filename}" 59 | end 60 | end 61 | end 62 | else 63 | copy(f, target) 64 | end 65 | end 66 | end 67 | 68 | puts "Done." 69 | end 70 | 71 | def copy(source, target) 72 | dirname = File.dirname(source).split('/').last 73 | filename = File.basename(source) 74 | puts "Copy #{dirname}/#{filename}" 75 | FileUtils.cp_r(source, target) 76 | end 77 | -------------------------------------------------------------------------------- /Scripts/Elements.applescript: -------------------------------------------------------------------------------- 1 | -- Elements Notepad 2 | -- 3 | -- Open ~/Dropbox/Elements folder in BBEdit 4 | -- 5 | -- Author: Andrew Carter 6 | -- Version: 0.1 7 | -- 8 | -- Copyright (c) 2012 Andrew Carter 9 | -- 10 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 11 | -- of this software and associated documentation files (the "Software"), to deal 12 | -- in the Software without restriction, including without limitation the rights 13 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | -- copies of the Software, and to permit persons to whom the Software is 15 | -- furnished to do so, subject to the following conditions: 16 | -- 17 | -- The above copyright notice and this permission notice shall be included in 18 | -- all copies or substantial portions of the Software. 19 | -- 20 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | -- THE SOFTWARE. 27 | -- 28 | 29 | set homePath to path to home folder as string 30 | set elementsFolder to alias (homePath & "Dropbox:Elements:") 31 | 32 | tell application "BBEdit" 33 | activate 34 | open elementsFolder 35 | end tell -------------------------------------------------------------------------------- /Scripts/Make Package.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # Create a new BBEdit package 4 | 5 | # BBEdit now supports installing “packages” of items to extend its functionality. 6 | # A package is simply a pre-defined group of the sort of items you can individually 7 | # place into BBEdit’s application support folder (and subfolders) to extend BBEdit; 8 | # however, the package format makes it easier to handle and install sets of related 9 | # items. 10 | # 11 | # Each package is a folder whose name must ends in “.bbpackage”, and the items 12 | # within this folder must conform exactly to the following requirements. 13 | # 14 | # The package folder must contain the following item: 15 | # * Contents [folder] 16 | # 17 | # The “Contents” folder may contain these two items (which are currently not 18 | # required, and are reserved for future use): 19 | # * Resources [folder] 20 | # * Info.plist [file] 21 | # 22 | # The “Contents” folder may also contain any or all of the following subfolders: 23 | # * Clippings 24 | # * Language Modules 25 | # * Scripts 26 | # * Text Filters 27 | # 28 | # The items contained within each subfolder will behave as though they were 29 | # present within the subfolder of the same name inside BBEdit’s application 30 | # support folder. (These subfolders are all optional, though obviously a package 31 | # which contains none of them will be of no benefit.) 32 | # 33 | # In order to use a populated package, you should place it within the “Packages” 34 | # subfolder of BBEdit’s application support folder 35 | # (~/Library/Application Support/BBEdit/Packages/). 36 | 37 | require 'fileutils' 38 | 39 | # Find application support directory 40 | def find_appsupport_root 41 | home_dir = File.expand_path(ENV['HOME']) 42 | root = File.join(home_dir, 'Dropbox', 'Application Support', 'BBEdit') 43 | if not File.exist?(root) 44 | root = File.join(home_dir, 'Library', 'Application Support', 'BBEdit') 45 | if not File.exist?(root) 46 | raise IOError.new('No BBEdit directory found in ~/Dropbox/Application Support or ~/Library/Application Support') 47 | end 48 | end 49 | return root 50 | end 51 | 52 | package_name = ARGV[0] == nil ? "Untitled Package.bbpackage" : "#{ARGV[0]}.bbpackage" 53 | appsupport_root = find_appsupport_root 54 | packages_root = File.join(appsupport_root, "Packages") 55 | target_root = File.join(packages_root, package_name) 56 | 57 | # Create Packages directory 58 | Dir.mkdir(packages_root) unless File.directory?(packages_root) 59 | 60 | # Make sure package doesn't already exist 61 | if File.directory?(target_root) 62 | raise IOError.new("Package already exists") 63 | end 64 | 65 | # Create the package 66 | package_dirs = [ 67 | 'Resources', 'Clippings', 'Language Modules', 'Scripts', 'Text Filters' 68 | ] 69 | 70 | Dir.mkdir(target_root) 71 | Dir.mkdir(File.join(target_root, "Contents")) 72 | File.new(File.join(target_root, "Contents", "Info.plist"), File::CREAT) 73 | package_dirs.each { |d| Dir.mkdir(File.join(target_root, "Contents", d)) } 74 | 75 | puts "Package #{package_name} created" 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /Scripts/Maketags.applescript: -------------------------------------------------------------------------------- 1 | -- Maketags 2 | -- 3 | -- Builds ctags file for currently active project 4 | -- 5 | -- Installation: 6 | -- 7 | -- Copy script to either location: 8 | -- ~/Library/Application Support/BBEdit/Scripts 9 | -- ~/Dropbox/Application Support/BBEdit/Scripts 10 | -- 11 | -- To add a shortcut key: 12 | -- 13 | -- Window -> Palettes -> Scripts 14 | -- Select Maketags and click Set Key ... 15 | -- Enter a shortcut key combination (recommend Command + Option + T) 16 | -- 17 | -- Author: Andrew Carter 18 | -- Version: 0.1 19 | -- 20 | -- Copyright (c) 2011 Andrew Carter 21 | -- 22 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 23 | -- of this software and associated documentation files (the "Software"), to deal 24 | -- in the Software without restriction, including without limitation the rights 25 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 26 | -- copies of the Software, and to permit persons to whom the Software is 27 | -- furnished to do so, subject to the following conditions: 28 | -- 29 | -- The above copyright notice and this permission notice shall be included in 30 | -- all copies or substantial portions of the Software. 31 | -- 32 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 37 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 38 | -- THE SOFTWARE. 39 | -- 40 | 41 | on makeTags() 42 | set theFile to missing value 43 | 44 | tell application "BBEdit" 45 | set activeWindow to front window 46 | 47 | if (class of activeWindow is project window) then 48 | set projectDocument to project document of activeWindow 49 | 50 | if ((count of items of projectDocument) > 0) then 51 | set firstFileItem to item 1 of projectDocument as alias 52 | else 53 | set firstFileItem to file of document of activeWindow as alias 54 | end if 55 | 56 | if (on disk of projectDocument) then 57 | set theProjectFile to file of projectDocument as alias 58 | 59 | tell application "Finder" 60 | set theProjectDir to container of theProjectFile 61 | set firstFileDir to container of firstFileItem 62 | end tell 63 | 64 | if (firstFileDir is equal to theProjectDir) then 65 | -- Use project file 66 | set theFile to theProjectDir as alias 67 | else 68 | -- External project file -> use first item to set context 69 | set theFile to firstFileItem 70 | end if 71 | else 72 | -- BBEdit doesn't provide direct access to the Instaproject root 73 | -- Use the first node from the project list 74 | set theFile to firstFileItem 75 | end if 76 | end if 77 | end tell 78 | 79 | if theFile is equal to missing value then 80 | -- No base file found for reference 81 | -- Signal error by beep and end 82 | beep 83 | else 84 | -- Run the maketags script 85 | set thePath to POSIX path of theFile 86 | set cmd to "cd " & thePath & "; /usr/local/bin/bbedit --maketags" 87 | do shell script cmd 88 | end if 89 | end makeTags 90 | 91 | makeTags() -------------------------------------------------------------------------------- /Scripts/Markdown Syntax.applescript: -------------------------------------------------------------------------------- 1 | -- Markdown Syntax 2 | -- 3 | -- Displays Markdown Syntax guide 4 | -- 5 | -- Installation: 6 | -- 7 | -- Copy script to either location: 8 | -- ~/Library/Application Support/BBEdit/Scripts 9 | -- ~/Dropbox/Application Support/BBEdit/Scripts 10 | -- 11 | -- To add a shortcut key: 12 | -- 13 | -- Window -> Palettes -> Scripts 14 | -- Select Markdown Syntax and click Set Key ... 15 | -- Enter a shortcut key combination (recommend Command + Option + M) 16 | -- 17 | -- Credits: 18 | -- 19 | -- Markdown Syntax source by John Gruber 20 | -- http://daringfireball.net/projects/markdown/syntax.text 21 | -- 22 | -- Author: Andrew Carter 23 | -- Version: 0.1 24 | -- 25 | -- Copyright (c) 2011 Andrew Carter 26 | -- 27 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 28 | -- of this software and associated documentation files (the "Software"), to deal 29 | -- in the Software without restriction, including without limitation the rights 30 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | -- copies of the Software, and to permit persons to whom the Software is 32 | -- furnished to do so, subject to the following conditions: 33 | -- 34 | -- The above copyright notice and this permission notice shall be included in 35 | -- all copies or substantial portions of the Software. 36 | -- 37 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 43 | -- THE SOFTWARE. 44 | 45 | on fetchMarkdown() 46 | set markdownUrl to "http://daringfireball.net/projects/markdown/syntax.text" 47 | set cmd to "curl " & markdownUrl 48 | log ("Fetch markdown text => " & markdownUrl) 49 | set markdownText to do shell script cmd 50 | -- Clean up the submenu links to reference Daring Fireball 51 | tell application "BBEdit" 52 | set markdownText to replace "/projects/markdown/" using "http://daringfireball.net/projects/markdown/" searchingString markdownText 53 | end tell 54 | return markdownText 55 | end fetchMarkdown 56 | 57 | on findWindowByName(theName) 58 | tell application "BBEdit" 59 | repeat with x from 1 to the count of windows 60 | set _window to window x 61 | if name of _window is equal to theName then 62 | log ("Found window " & theName) 63 | return _window 64 | end if 65 | end repeat 66 | log ("Did not find " & theName) 67 | return missing value 68 | end tell 69 | end findWindowByName 70 | 71 | on markdownDoc(theTitle) 72 | tell application "BBEdit" 73 | set markdownText to fetchMarkdown() of me 74 | set _window to make new text window with properties {contents:markdownText, visible:false} 75 | set _doc to document of _window 76 | set source language of _doc to "Markdown" 77 | set name of _doc to theTitle 78 | set bounds of _window to {0, 0, 1, 1} 79 | return _window 80 | end tell 81 | end markdownDoc 82 | 83 | on previewMarkdown(sourceWindow) 84 | tell application "BBEdit" 85 | activate 86 | set collapsed of sourceWindow to false 87 | select sourceWindow 88 | log ("Use Preview in BBEdit for markdown") 89 | tell application "System Events" 90 | tell process "BBEdit" 91 | click menu item "Preview in BBEdit" of menu 1 of menu bar item "Markup" of menu bar 1 92 | end tell 93 | end tell 94 | end tell 95 | end previewMarkdown 96 | 97 | set theWindowTitle to "Markdown Syntax" 98 | set theDoc to missing value 99 | set theWindow to missing value 100 | set thePreviewWindow to missing value 101 | 102 | tell application "BBEdit" 103 | -- Is the preview window already available? 104 | set thePreviewWindow to findWindowByName("Preview: " & theWindowTitle) of me 105 | 106 | if thePreviewWindow is equal to missing value then 107 | -- Is there already a window with Markdown Syntax? 108 | set theWindow to findWindowByName(theWindowTitle) of me 109 | if theWindow is equal to missing value then 110 | set theWindow to markdownDoc(theWindowTitle) of me 111 | set theDoc to document of theWindow 112 | else 113 | set theDoc to the document of theWindow 114 | end if 115 | 116 | if theWindow is equal to missing value then 117 | -- Abort 118 | log ("No markdown syntax window") 119 | beep 120 | return 121 | end if 122 | 123 | -- Preview in BBEdit 124 | previewMarkdown(theWindow) of me 125 | end if 126 | 127 | -- Focus on the preview 128 | set thePreviewWindow to findWindowByName("Preview: " & theWindowTitle) of me 129 | if thePreviewWindow is not equal to missing value then 130 | select thePreviewWindow 131 | end if 132 | 133 | if theWindow is not equal to missing value then 134 | -- Minimize syntax window 135 | log ("Minimize the syntax window") 136 | -- set collapsed of theWindow to true 137 | end if 138 | end tell -------------------------------------------------------------------------------- /Scripts/PeepOpen.applescript: -------------------------------------------------------------------------------- 1 | -- PeepOpen for BBEdit 2 | -- 3 | -- Launches PeepOpen for either the current project directory, 4 | -- first project file, or the current file 5 | -- 6 | -- Installation: 7 | -- 8 | -- Copy script to either location: 9 | -- ~/Library/Application Support/BBEdit/Scripts 10 | -- ~/Dropbox/Application Support/BBEdit/Scripts 11 | -- 12 | -- To add a shortcut key: 13 | -- 14 | -- Window -> Palettes -> Scripts 15 | -- Select PeepOpen and click Set Key ... 16 | -- Enter a shortcut key combination (recommend Command + Option + T) 17 | -- 18 | -- Credits: 19 | -- 20 | -- Thanks to Bare Bones Software, Inc. for the initial AppleScript code. 21 | -- 22 | -- Author: Andrew Carter 23 | -- Version: 0.1.2 24 | -- 25 | -- ============================================================================= 26 | -- Copyright (c) 2011 Andrew Carter 27 | -- 28 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 29 | -- of this software and associated documentation files (the "Software"), to deal 30 | -- in the Software without restriction, including without limitation the rights 31 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | -- copies of the Software, and to permit persons to whom the Software is 33 | -- furnished to do so, subject to the following conditions: 34 | -- 35 | -- The above copyright notice and this permission notice shall be included in 36 | -- all copies or substantial portions of the Software. 37 | -- 38 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | -- THE SOFTWARE. 45 | -- ============================================================================= 46 | 47 | on urlencode(theURL) 48 | set theScript to "require \"uri\"; puts URI.escape(ARGV[0])" 49 | do shell script "/usr/bin/ruby -e '" & theScript & "' " & quoted form of theURL 50 | end urlencode 51 | 52 | on isAppRunning(appName) 53 | tell application "System Events" to (name of processes) contains appName 54 | end isAppRunning 55 | 56 | -- Check to see if PeepOpen is installed 57 | if not isAppRunning("PeepOpen") of me then 58 | beep 59 | display alert "PeepOpen is not running" 60 | return 61 | end if 62 | 63 | set theFile to missing value 64 | 65 | tell application "BBEdit" 66 | set activeWindow to front window 67 | 68 | if (class of activeWindow is project window) then 69 | set projectDocument to project document of activeWindow 70 | 71 | if ((count of items of projectDocument) > 0) then 72 | set firstFileItem to item 1 of projectDocument as alias 73 | else 74 | set firstFileItem to file of document of activeWindow as alias 75 | end if 76 | 77 | if (on disk of projectDocument) then 78 | set theProjectFile to file of projectDocument as alias 79 | 80 | tell application "Finder" 81 | set theProjectDir to container of theProjectFile 82 | set firstFileDir to container of firstFileItem 83 | end tell 84 | 85 | if (firstFileDir is equal to theProjectDir) then 86 | -- Use project file 87 | set theFile to theProjectDir as alias 88 | else 89 | -- External project file -> use first item to set context 90 | set theFile to firstFileItem 91 | end if 92 | else 93 | -- BBEdit doesn't provide direct access to the Instaproject root 94 | -- Use the first node from the project list 95 | set theFile to firstFileItem 96 | end if 97 | end if 98 | end tell 99 | 100 | if theFile is equal to missing value then 101 | -- No base file found for reference 102 | -- Signal error by beep and end 103 | beep 104 | else 105 | -- Construct the URI for file and launch PeepOpen 106 | -- set fullPath to POSIX path of theFile 107 | set fullPath to POSIX path of theFile 108 | set theURL to "peepopen://" & fullPath & "?editor=BBEdit" 109 | open location urlencode(theURL) of me 110 | end if 111 | -------------------------------------------------------------------------------- /Scripts/Preview in Marked.scpt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascarter/BBEdit-ApplicationSupport/bea943c00362b483ed57966252ba5c2c450cc9e1/Scripts/Preview in Marked.scpt -------------------------------------------------------------------------------- /Scripts/manpage.applescript: -------------------------------------------------------------------------------- 1 | -- manpage 2 | -- 3 | -- View man page in BBEdit 4 | -- 5 | 6 | -- man ls | col -b | bbedit --new-window 7 | -- ============================================================================= 8 | -- Copyright (c) 2011 Andrew Carter 9 | -- 10 | -- Permission is hereby granted, free of charge, to any person obtaining a copy 11 | -- of this software and associated documentation files (the "Software"), to deal 12 | -- in the Software without restriction, including without limitation the rights 13 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | -- copies of the Software, and to permit persons to whom the Software is 15 | -- furnished to do so, subject to the following conditions: 16 | -- 17 | -- The above copyright notice and this permission notice shall be included in 18 | -- all copies or substantial portions of the Software. 19 | -- 20 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | -- THE SOFTWARE. 27 | -- ============================================================================= 28 | 29 | on fetchManpage(theManpageName) 30 | set theScript to "man " & theManpageName & " | man2html" 31 | set thePage to do shell script theScript 32 | return thePage 33 | end fetchManpage 34 | 35 | on manpageDoc(the 36 | 37 | tell application "BBEdit" 38 | set theResponse to display dialog "Manpage" default answer "" 39 | if button returned of theResponse is "OK" then 40 | set theManpageName to text returned of theResponse 41 | 42 | -- Get HTML version of man page 43 | set thePage to fetchManpage(theManpageName) 44 | 45 | -- Put in a text window 46 | set theWindow to make new text window with properties {contents:thePage} 47 | set theDoc to document of theWindow 48 | set name of theDoc to "man: " & theManpageName 49 | -- select line 1 of theDoc 50 | log insertion point of theDoc 51 | select insertion point before line 1 of theDoc 52 | end if 53 | end tell -------------------------------------------------------------------------------- /Text Filters/JSON Pretty Print.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import fileinput 4 | import json 5 | 6 | if __name__ == "__main__": 7 | jsonStr = '' 8 | for a_line in fileinput.input(): 9 | jsonStr = jsonStr + ' ' + a_line.strip() 10 | jsonObj = json.loads(jsonStr) 11 | print json.dumps(jsonObj, sort_keys=True, indent=2) 12 | -------------------------------------------------------------------------------- /Text Filters/JavaScript Beautify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import getopt 5 | import re 6 | import os 7 | 8 | # 9 | # Originally written by Einar Lielmanis et al., 10 | # Conversion to python by Einar Lielmanis, einar@jsbeautifier.org, 11 | # MIT licence, enjoy. 12 | # 13 | # Python is not my native language, feel free to push things around. 14 | # 15 | # Use either from command line (script displays its usage when run 16 | # without any parameters), 17 | # 18 | # 19 | # or, alternatively, use it as a module: 20 | # 21 | # import jsbeautifier 22 | # res = jsbeautifier.beautify('your javascript string') 23 | # res = jsbeautifier.beautify_file('some_file.js') 24 | # 25 | # you may specify some options: 26 | # 27 | # opts = jsbeautifier.default_options() 28 | # opts.indent_size = 2 29 | # res = jsbeautifier.beautify('some javascript', opts) 30 | # 31 | # 32 | # Here are the available options: (read source) 33 | 34 | 35 | class BeautifierOptions: 36 | def __init__(self): 37 | self.indent_size = 4 38 | self.indent_char = ' ' 39 | self.preserve_newlines = True 40 | self.max_preserve_newlines = 10. 41 | self.jslint_happy = False 42 | self.brace_style = 'collapse' 43 | self.keep_array_indentation = False 44 | 45 | 46 | 47 | def __repr__(self): 48 | return \ 49 | """indent_size = %d 50 | indent_char = [%s] 51 | preserve_newlines = %s 52 | max_preserve_newlines = %d 53 | jslint_happy = %s 54 | brace_style = %s 55 | keep_array_indentation = %s 56 | """ % ( self.indent_size, 57 | self.indent_char, 58 | self.preserve_newlines, 59 | self.max_preserve_newlines, 60 | self.jslint_happy, 61 | self.brace_style, 62 | self.keep_array_indentation 63 | ) 64 | 65 | 66 | class BeautifierFlags: 67 | def __init__(self, mode): 68 | self.previous_mode = 'BLOCK' 69 | self.mode = mode 70 | self.var_line = False 71 | self.var_line_tainted = False 72 | self.var_line_reindented = False 73 | self.in_html_comment = False 74 | self.if_line = False 75 | self.in_case = False 76 | self.eat_next_space = False 77 | self.indentation_baseline = -1 78 | self.indentation_level = 0 79 | self.ternary_depth = 0 80 | 81 | 82 | def default_options(): 83 | return BeautifierOptions() 84 | 85 | 86 | def beautify(string, opts = default_options() ): 87 | b = Beautifier() 88 | return b.beautify(string, opts) 89 | 90 | 91 | def beautify_file(file_name, opts = default_options() ): 92 | 93 | if file_name == '-': # stdin 94 | f = sys.stdin 95 | else: 96 | f = open(file_name) 97 | 98 | b = Beautifier() 99 | return b.beautify(''.join(f.readlines()), opts) 100 | 101 | 102 | def usage(): 103 | 104 | print("""Javascript beautifier (http://jsbeautifier.org/) 105 | 106 | Usage: jsbeautifier.py [options] 107 | 108 | can be "-", which means stdin. 109 | defaults to stdout 110 | 111 | Input options: 112 | 113 | -i, --stdin read input from stdin 114 | 115 | Output options: 116 | 117 | -s, --indent-size=NUMBER indentation size. (default 4). 118 | -c, --indent-char=CHAR character to indent with. (default space). 119 | -d, --disable-preserve-newlines do not preserve existing line breaks. 120 | -j, --jslint-happy more jslint-compatible output 121 | -b, --brace-style=collapse brace style (collapse, expand, end-expand) 122 | -k, --keep-array-indentation keep array indentation. 123 | -o, --outfile=FILE specify a file to output to (default stdout) 124 | 125 | Rarely needed options: 126 | 127 | -l, --indent-level=NUMBER initial indentation level. (default 0). 128 | 129 | -h, --help, --usage prints this help statement. 130 | 131 | """); 132 | 133 | 134 | 135 | 136 | 137 | 138 | class Beautifier: 139 | 140 | def __init__(self, opts = default_options() ): 141 | 142 | self.opts = opts 143 | self.blank_state() 144 | 145 | def blank_state(self): 146 | 147 | # internal flags 148 | self.flags = BeautifierFlags('BLOCK') 149 | self.flag_store = [] 150 | self.wanted_newline = False 151 | self.just_added_newline = False 152 | self.do_block_just_closed = False 153 | 154 | 155 | self.indent_string = self.opts.indent_char * self.opts.indent_size 156 | self.preindent_string = '' 157 | self.last_word = '' # last TK_WORD seen 158 | self.last_type = 'TK_START_EXPR' # last token type 159 | self.last_text = '' # last token text 160 | self.last_last_text = '' # pre-last token text 161 | 162 | self.input = None 163 | self.output = [] # formatted javascript gets built here 164 | 165 | self.whitespace = ["\n", "\r", "\t", " "] 166 | self.wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$' 167 | self.digits = '0123456789' 168 | self.punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' '); 169 | 170 | 171 | # Words which always should start on a new line 172 | self.line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',') 173 | self.set_mode('BLOCK') 174 | 175 | global parser_pos 176 | parser_pos = 0 177 | 178 | 179 | def beautify(self, s, opts = None ): 180 | 181 | if opts != None: 182 | self.opts = opts 183 | 184 | 185 | if self.opts.brace_style not in ['expand', 'collapse', 'end-expand']: 186 | raise(Exception('opts.brace_style must be "expand", "collapse" or "end-expand".')) 187 | 188 | self.blank_state() 189 | 190 | while s and s[0] in [' ', '\t']: 191 | self.preindent_string += s[0] 192 | s = s[1:] 193 | 194 | self.input = s 195 | 196 | parser_pos = 0 197 | while True: 198 | token_text, token_type = self.get_next_token() 199 | #print (token_text, token_type, self.flags.mode) 200 | if token_type == 'TK_EOF': 201 | break 202 | 203 | handlers = { 204 | 'TK_START_EXPR': self.handle_start_expr, 205 | 'TK_END_EXPR': self.handle_end_expr, 206 | 'TK_START_BLOCK': self.handle_start_block, 207 | 'TK_END_BLOCK': self.handle_end_block, 208 | 'TK_WORD': self.handle_word, 209 | 'TK_SEMICOLON': self.handle_semicolon, 210 | 'TK_STRING': self.handle_string, 211 | 'TK_EQUALS': self.handle_equals, 212 | 'TK_OPERATOR': self.handle_operator, 213 | 'TK_BLOCK_COMMENT': self.handle_block_comment, 214 | 'TK_INLINE_COMMENT': self.handle_inline_comment, 215 | 'TK_COMMENT': self.handle_comment, 216 | 'TK_UNKNOWN': self.handle_unknown, 217 | } 218 | 219 | handlers[token_type](token_text) 220 | 221 | self.last_last_text = self.last_text 222 | self.last_type = token_type 223 | self.last_text = token_text 224 | 225 | sweet_code = self.preindent_string + re.sub('[\n ]+$', '', ''.join(self.output)) 226 | return sweet_code 227 | 228 | 229 | 230 | def trim_output(self, eat_newlines = False): 231 | while len(self.output) \ 232 | and ( 233 | self.output[-1] == ' '\ 234 | or self.output[-1] == self.indent_string \ 235 | or self.output[-1] == self.preindent_string \ 236 | or (eat_newlines and self.output[-1] in ['\n', '\r'])): 237 | self.output.pop() 238 | 239 | 240 | def is_array(self, mode): 241 | return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]'] 242 | 243 | 244 | def is_expression(self, mode): 245 | return mode in ['[EXPRESSION]', '[INDENDED-EXPRESSION]', '(EXPRESSION)'] 246 | 247 | 248 | def append_newline_forced(self): 249 | old_array_indentation = self.opts.keep_array_indentation 250 | self.opts.keep_array_indentation = False 251 | self.append_newline() 252 | self.opts.keep_array_indentation = old_array_indentation 253 | 254 | def append_newline(self, ignore_repeated = True): 255 | 256 | self.flags.eat_next_space = False; 257 | 258 | if self.opts.keep_array_indentation and self.is_array(self.flags.mode): 259 | return 260 | 261 | self.flags.if_line = False; 262 | self.trim_output(); 263 | 264 | if len(self.output) == 0: 265 | # no newline on start of file 266 | return 267 | 268 | if self.output[-1] != '\n' or not ignore_repeated: 269 | self.just_added_newline = True 270 | self.output.append('\n') 271 | 272 | if self.preindent_string: 273 | self.output.append(self.preindent_string) 274 | 275 | for i in range(self.flags.indentation_level): 276 | self.output.append(self.indent_string) 277 | 278 | if self.flags.var_line and self.flags.var_line_reindented: 279 | self.output.append(self.indent_string) 280 | 281 | 282 | def append(self, s): 283 | if s == ' ': 284 | # make sure only single space gets drawn 285 | if self.flags.eat_next_space: 286 | self.flags.eat_next_space = False 287 | elif len(self.output) and self.output[-1] not in [' ', '\n', self.indent_string]: 288 | self.output.append(' ') 289 | else: 290 | self.just_added_newline = False 291 | self.flags.eat_next_space = False 292 | self.output.append(s) 293 | 294 | 295 | def indent(self): 296 | self.flags.indentation_level = self.flags.indentation_level + 1 297 | 298 | 299 | def remove_indent(self): 300 | if len(self.output) and self.output[-1] in [self.indent_string, self.preindent_string]: 301 | self.output.pop() 302 | 303 | 304 | def set_mode(self, mode): 305 | 306 | prev = BeautifierFlags('BLOCK') 307 | 308 | if self.flags: 309 | self.flag_store.append(self.flags) 310 | prev = self.flags 311 | 312 | self.flags = BeautifierFlags(mode) 313 | 314 | if len(self.flag_store) == 1: 315 | self.flags.indentation_level = 0 316 | else: 317 | self.flags.indentation_level = prev.indentation_level 318 | if prev.var_line and prev.var_line_reindented: 319 | self.flags.indentation_level = self.flags.indentation_level + 1 320 | self.flags.previous_mode = prev.mode 321 | 322 | 323 | def restore_mode(self): 324 | self.do_block_just_closed = self.flags.mode == 'DO_BLOCK' 325 | if len(self.flag_store) > 0: 326 | self.flags = self.flag_store.pop() 327 | 328 | 329 | def get_next_token(self): 330 | 331 | global parser_pos 332 | 333 | self.n_newlines = 0 334 | 335 | if parser_pos >= len(self.input): 336 | return '', 'TK_EOF' 337 | 338 | self.wanted_newline = False; 339 | c = self.input[parser_pos] 340 | parser_pos += 1 341 | 342 | keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode) 343 | 344 | if keep_whitespace: 345 | # slight mess to allow nice preservation of array indentation and reindent that correctly 346 | # first time when we get to the arrays: 347 | # var a = [ 348 | # ....'something' 349 | # we make note of whitespace_count = 4 into flags.indentation_baseline 350 | # so we know that 4 whitespaces in original source match indent_level of reindented source 351 | # 352 | # and afterwards, when we get to 353 | # 'something, 354 | # .......'something else' 355 | # we know that this should be indented to indent_level + (7 - indentation_baseline) spaces 356 | 357 | whitespace_count = 0 358 | while c in self.whitespace: 359 | if c == '\n': 360 | self.trim_output() 361 | self.output.append('\n') 362 | self.just_added_newline = True 363 | whitespace_count = 0 364 | elif c == '\t': 365 | whitespace_count += 4 366 | elif c == '\r': 367 | pass 368 | else: 369 | whitespace_count += 1 370 | 371 | if parser_pos >= len(self.input): 372 | return '', 'TK_EOF' 373 | 374 | c = self.input[parser_pos] 375 | parser_pos += 1 376 | 377 | if self.flags.indentation_baseline == -1: 378 | 379 | self.flags.indentation_baseline = whitespace_count 380 | 381 | if self.just_added_newline: 382 | for i in range(self.flags.indentation_level + 1): 383 | self.output.append(self.indent_string) 384 | 385 | if self.flags.indentation_baseline != -1: 386 | for i in range(whitespace_count - self.flags.indentation_baseline): 387 | self.output.append(' ') 388 | 389 | else: # not keep_whitespace 390 | while c in self.whitespace: 391 | if c == '\n': 392 | if self.opts.max_preserve_newlines == 0 or self.opts.max_preserve_newlines > self.n_newlines: 393 | self.n_newlines += 1 394 | 395 | if parser_pos >= len(self.input): 396 | return '', 'TK_EOF' 397 | 398 | c = self.input[parser_pos] 399 | parser_pos += 1 400 | 401 | if self.opts.preserve_newlines and self.n_newlines > 1: 402 | for i in range(self.n_newlines): 403 | self.append_newline(i == 0) 404 | self.just_added_newline = True 405 | 406 | self.wanted_newline = self.n_newlines > 0 407 | 408 | 409 | if c in self.wordchar: 410 | if parser_pos < len(self.input): 411 | while self.input[parser_pos] in self.wordchar: 412 | c = c + self.input[parser_pos] 413 | parser_pos += 1 414 | if parser_pos == len(self.input): 415 | break 416 | 417 | # small and surprisingly unugly hack for 1E-10 representation 418 | if parser_pos != len(self.input) and self.input[parser_pos] in '+-' \ 419 | and re.match('^[0-9]+[Ee]$', c): 420 | 421 | sign = self.input[parser_pos] 422 | parser_pos += 1 423 | t = self.get_next_token() 424 | c += sign + t[0] 425 | return c, 'TK_WORD' 426 | 427 | if c == 'in': # in is an operator, need to hack 428 | return c, 'TK_OPERATOR' 429 | 430 | if self.wanted_newline and \ 431 | self.last_type != 'TK_OPERATOR' and\ 432 | self.last_type != 'TK_EQUALS' and\ 433 | not self.flags.if_line and \ 434 | (self.opts.preserve_newlines or self.last_text != 'var'): 435 | self.append_newline() 436 | 437 | return c, 'TK_WORD' 438 | 439 | if c in '([': 440 | return c, 'TK_START_EXPR' 441 | 442 | if c in ')]': 443 | return c, 'TK_END_EXPR' 444 | 445 | if c == '{': 446 | return c, 'TK_START_BLOCK' 447 | 448 | if c == '}': 449 | return c, 'TK_END_BLOCK' 450 | 451 | if c == ';': 452 | return c, 'TK_SEMICOLON' 453 | 454 | if c == '/': 455 | comment = '' 456 | inline_comment = True 457 | comment_mode = 'TK_INLINE_COMMENT' 458 | if self.input[parser_pos] == '*': # peek /* .. */ comment 459 | parser_pos += 1 460 | if parser_pos < len(self.input): 461 | while not (self.input[parser_pos] == '*' and \ 462 | parser_pos + 1 < len(self.input) and \ 463 | self.input[parser_pos + 1] == '/')\ 464 | and parser_pos < len(self.input): 465 | c = self.input[parser_pos] 466 | comment += c 467 | if c in '\r\n': 468 | comment_mode = 'TK_BLOCK_COMMENT' 469 | parser_pos += 1 470 | if parser_pos >= len(self.input): 471 | break 472 | parser_pos += 2 473 | return '/*' + comment + '*/', comment_mode 474 | if self.input[parser_pos] == '/': # peek // comment 475 | comment = c 476 | while self.input[parser_pos] not in '\r\n': 477 | comment += self.input[parser_pos] 478 | parser_pos += 1 479 | if parser_pos >= len(self.input): 480 | break 481 | parser_pos += 1 482 | if self.wanted_newline: 483 | self.append_newline() 484 | return comment, 'TK_COMMENT' 485 | 486 | 487 | 488 | if c == "'" or c == '"' or \ 489 | (c == '/' and ((self.last_type == 'TK_WORD' and self.last_text in ['return', 'do']) or \ 490 | (self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', 491 | 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON']))): 492 | sep = c 493 | esc = False 494 | resulting_string = c 495 | in_char_class = False 496 | 497 | if parser_pos < len(self.input): 498 | if sep == '/': 499 | # handle regexp 500 | in_char_class = False 501 | while esc or in_char_class or self.input[parser_pos] != sep: 502 | resulting_string += self.input[parser_pos] 503 | if not esc: 504 | esc = self.input[parser_pos] == '\\' 505 | if self.input[parser_pos] == '[': 506 | in_char_class = True 507 | elif self.input[parser_pos] == ']': 508 | in_char_class = False 509 | else: 510 | esc = False 511 | parser_pos += 1 512 | if parser_pos >= len(self.input): 513 | # incomplete regex when end-of-file reached 514 | # bail out with what has received so far 515 | return resulting_string, 'TK_STRING' 516 | else: 517 | # handle string 518 | while esc or self.input[parser_pos] != sep: 519 | resulting_string += self.input[parser_pos] 520 | if not esc: 521 | esc = self.input[parser_pos] == '\\' 522 | else: 523 | esc = False 524 | parser_pos += 1 525 | if parser_pos >= len(self.input): 526 | # incomplete string when end-of-file reached 527 | # bail out with what has received so far 528 | return resulting_string, 'TK_STRING' 529 | 530 | 531 | parser_pos += 1 532 | resulting_string += sep 533 | if sep == '/': 534 | # regexps may have modifiers /regexp/MOD, so fetch those too 535 | while parser_pos < len(self.input) and self.input[parser_pos] in self.wordchar: 536 | resulting_string += self.input[parser_pos] 537 | parser_pos += 1 538 | return resulting_string, 'TK_STRING' 539 | 540 | if c == '#': 541 | 542 | # she-bang 543 | if len(self.output) == 0 and len(self.input) > 1 and self.input[parser_pos] == '!': 544 | resulting_string = c 545 | while parser_pos < len(self.input) and c != '\n': 546 | c = self.input[parser_pos] 547 | resulting_string += c 548 | parser_pos += 1 549 | self.output.append(resulting_string.strip() + "\n") 550 | self.append_newline() 551 | return self.get_next_token() 552 | 553 | 554 | # Spidermonkey-specific sharp variables for circular references 555 | # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript 556 | # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 557 | sharp = '#' 558 | if parser_pos < len(self.input) and self.input[parser_pos] in self.digits: 559 | while True: 560 | c = self.input[parser_pos] 561 | sharp += c 562 | parser_pos += 1 563 | if parser_pos >= len(self.input) or c == '#' or c == '=': 564 | break 565 | if c == '#' or parser_pos >= len(self.input): 566 | pass 567 | elif self.input[parser_pos] == '[' and self.input[parser_pos + 1] == ']': 568 | sharp += '[]' 569 | parser_pos += 2 570 | elif self.input[parser_pos] == '{' and self.input[parser_pos + 1] == '}': 571 | sharp += '{}' 572 | parser_pos += 2 573 | return sharp, 'TK_WORD' 574 | 575 | if c == '<' and self.input[parser_pos - 1 : parser_pos + 3] == '': 581 | self.flags.in_html_comment = False 582 | parser_pos += 2 583 | if self.wanted_newline: 584 | self.append_newline() 585 | return '-->', 'TK_COMMENT' 586 | 587 | if c in self.punct: 588 | while parser_pos < len(self.input) and c + self.input[parser_pos] in self.punct: 589 | c += self.input[parser_pos] 590 | parser_pos += 1 591 | if parser_pos >= len(self.input): 592 | break 593 | if c == '=': 594 | return c, 'TK_EQUALS' 595 | else: 596 | return c, 'TK_OPERATOR' 597 | return c, 'TK_UNKNOWN' 598 | 599 | 600 | 601 | def handle_start_expr(self, token_text): 602 | if token_text == '[': 603 | if self.last_type == 'TK_WORD' or self.last_text == ')': 604 | if self.last_text in self.line_starters: 605 | self.append(' ') 606 | self.set_mode('(EXPRESSION)') 607 | self.append(token_text) 608 | return 609 | 610 | if self.flags.mode in ['[EXPRESSION]', '[INDENTED-EXPRESSION]']: 611 | if self.last_last_text == ']' and self.last_text == ',': 612 | # ], [ goes to a new line 613 | if self.flags.mode == '[EXPRESSION]': 614 | self.flags.mode = '[INDENTED-EXPRESSION]' 615 | if not self.opts.keep_array_indentation: 616 | self.indent() 617 | self.set_mode('[EXPRESSION]') 618 | if not self.opts.keep_array_indentation: 619 | self.append_newline() 620 | elif self.last_text == '[': 621 | if self.flags.mode == '[EXPRESSION]': 622 | self.flags.mode = '[INDENTED-EXPRESSION]' 623 | if not self.opts.keep_array_indentation: 624 | self.indent() 625 | self.set_mode('[EXPRESSION]') 626 | 627 | if not self.opts.keep_array_indentation: 628 | self.append_newline() 629 | else: 630 | self.set_mode('[EXPRESSION]') 631 | else: 632 | self.set_mode('[EXPRESSION]') 633 | else: 634 | self.set_mode('(EXPRESSION)') 635 | 636 | 637 | if self.last_text == ';' or self.last_type == 'TK_START_BLOCK': 638 | self.append_newline() 639 | elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.last_text == '.': 640 | # do nothing on (( and )( and ][ and ]( and .( 641 | pass 642 | elif self.last_type not in ['TK_WORD', 'TK_OPERATOR']: 643 | self.append(' ') 644 | elif self.last_word == 'function' or self.last_word == 'typeof': 645 | # function() vs function (), typeof() vs typeof () 646 | if self.opts.jslint_happy: 647 | self.append(' ') 648 | elif self.last_text in self.line_starters or self.last_text == 'catch': 649 | self.append(' ') 650 | 651 | self.append(token_text) 652 | 653 | 654 | def handle_end_expr(self, token_text): 655 | if token_text == ']': 656 | if self.opts.keep_array_indentation: 657 | if self.last_text == '}': 658 | self.remove_indent() 659 | self.append(token_text) 660 | self.restore_mode() 661 | return 662 | else: 663 | if self.flags.mode == '[INDENTED-EXPRESSION]': 664 | if self.last_text == ']': 665 | self.restore_mode() 666 | self.append_newline() 667 | self.append(token_text) 668 | return 669 | self.restore_mode() 670 | self.append(token_text) 671 | 672 | 673 | def handle_start_block(self, token_text): 674 | if self.last_word == 'do': 675 | self.set_mode('DO_BLOCK') 676 | else: 677 | self.set_mode('BLOCK') 678 | 679 | if self.opts.brace_style == 'expand': 680 | if self.last_type != 'TK_OPERATOR': 681 | if self.last_text in ['return', '=']: 682 | self.append(' ') 683 | else: 684 | self.append_newline(True) 685 | 686 | self.append(token_text) 687 | self.indent() 688 | else: 689 | if self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']: 690 | if self.last_type == 'TK_START_BLOCK': 691 | self.append_newline() 692 | else: 693 | self.append(' ') 694 | else: 695 | # if TK_OPERATOR or TK_START_EXPR 696 | if self.is_array(self.flags.previous_mode) and self.last_text == ',': 697 | if self.last_last_text == '}': 698 | self.append(' ') 699 | else: 700 | self.append_newline() 701 | self.indent() 702 | self.append(token_text) 703 | 704 | 705 | def handle_end_block(self, token_text): 706 | self.restore_mode() 707 | if self.opts.brace_style == 'expand': 708 | if self.last_text != '{': 709 | self.append_newline() 710 | else: 711 | if self.last_type == 'TK_START_BLOCK': 712 | if self.just_added_newline: 713 | self.remove_indent() 714 | else: 715 | # {} 716 | self.trim_output() 717 | else: 718 | if self.is_array(self.flags.mode) and self.opts.keep_array_indentation: 719 | self.opts.keep_array_indentation = False 720 | self.append_newline() 721 | self.opts.keep_array_indentation = True 722 | else: 723 | self.append_newline() 724 | 725 | self.append(token_text) 726 | 727 | 728 | def handle_word(self, token_text): 729 | if self.do_block_just_closed: 730 | self.append(' ') 731 | self.append(token_text) 732 | self.append(' ') 733 | self.do_block_just_closed = False 734 | return 735 | 736 | if token_text == 'function': 737 | 738 | if self.flags.var_line: 739 | self.flags.var_line_reindented = True 740 | if (self.just_added_newline or self.last_text == ';') and self.last_text != '{': 741 | # make sure there is a nice clean space of at least one blank line 742 | # before a new function definition 743 | have_newlines = self.n_newlines 744 | if not self.just_added_newline: 745 | have_newlines = 0 746 | if not self.opts.preserve_newlines: 747 | have_newlines = 1 748 | for i in range(2 - have_newlines): 749 | self.append_newline(False) 750 | 751 | if token_text in ['case', 'default']: 752 | if self.last_text == ':': 753 | self.remove_indent() 754 | else: 755 | self.flags.indentation_level -= 1 756 | self.append_newline() 757 | self.flags.indentation_level += 1 758 | self.append(token_text) 759 | self.flags.in_case = True 760 | return 761 | 762 | prefix = 'NONE' 763 | 764 | if self.last_type == 'TK_END_BLOCK': 765 | if token_text not in ['else', 'catch', 'finally']: 766 | prefix = 'NEWLINE' 767 | else: 768 | if self.opts.brace_style in ['expand', 'end-expand']: 769 | prefix = 'NEWLINE' 770 | else: 771 | prefix = 'SPACE' 772 | self.append(' ') 773 | elif self.last_type == 'TK_SEMICOLON' and self.flags.mode in ['BLOCK', 'DO_BLOCK']: 774 | prefix = 'NEWLINE' 775 | elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode): 776 | prefix = 'SPACE' 777 | elif self.last_type == 'TK_STRING': 778 | prefix = 'NEWLINE' 779 | elif self.last_type == 'TK_WORD': 780 | if self.last_text == 'else': 781 | # eat newlines between ...else *** some_op... 782 | # won't preserve extra newlines in this place (if any), but don't care that much 783 | self.trim_output(True); 784 | prefix = 'SPACE' 785 | elif self.last_type == 'TK_START_BLOCK': 786 | prefix = 'NEWLINE' 787 | elif self.last_type == 'TK_END_EXPR': 788 | self.append(' ') 789 | prefix = 'NEWLINE' 790 | 791 | if self.flags.if_line and self.last_type == 'TK_END_EXPR': 792 | self.flags.if_line = False 793 | 794 | if token_text in self.line_starters: 795 | if self.last_text == 'else': 796 | prefix = 'SPACE' 797 | else: 798 | prefix = 'NEWLINE' 799 | 800 | if token_text in ['else', 'catch', 'finally']: 801 | if self.last_type != 'TK_END_BLOCK' \ 802 | or self.opts.brace_style == 'expand' \ 803 | or self.opts.brace_style == 'end-expand': 804 | self.append_newline() 805 | else: 806 | self.trim_output(True) 807 | self.append(' ') 808 | elif prefix == 'NEWLINE': 809 | if token_text == 'function' and (self.last_type == 'TK_START_EXPR' or self.last_text in '=,'): 810 | # no need to force newline on "function" - 811 | # (function... 812 | pass 813 | elif token_text == 'function' and self.last_text == 'new': 814 | self.append(' ') 815 | elif self.last_text in ['return', 'throw']: 816 | # no newline between return nnn 817 | self.append(' ') 818 | elif self.last_type != 'TK_END_EXPR': 819 | if (self.last_type != 'TK_START_EXPR' or token_text != 'var') and self.last_text != ':': 820 | # no need to force newline on VAR - 821 | # for (var x = 0... 822 | if token_text == 'if' and self.last_word == 'else' and self.last_text != '{': 823 | self.append(' ') 824 | else: 825 | self.flags.var_line = False 826 | self.flags.var_line_reindented = False 827 | self.append_newline() 828 | elif token_text in self.line_starters and self.last_text != ')': 829 | self.flags.var_line = False 830 | self.flags.var_line_reindented = False 831 | self.append_newline() 832 | elif self.is_array(self.flags.mode) and self.last_text == ',' and self.last_last_text == '}': 833 | self.append_newline() # }, in lists get a newline 834 | elif prefix == 'SPACE': 835 | self.append(' ') 836 | 837 | 838 | self.append(token_text) 839 | self.last_word = token_text 840 | 841 | if token_text == 'var': 842 | self.flags.var_line = True 843 | self.flags.var_line_reindented = False 844 | self.flags.var_line_tainted = False 845 | 846 | 847 | if token_text == 'if': 848 | self.flags.if_line = True 849 | 850 | if token_text == 'else': 851 | self.flags.if_line = False 852 | 853 | 854 | def handle_semicolon(self, token_text): 855 | self.append(token_text) 856 | self.flags.var_line = False 857 | self.flags.var_line_reindented = False 858 | if self.flags.mode == 'OBJECT': 859 | # OBJECT mode is weird and doesn't get reset too well. 860 | self.flags.mode = 'BLOCK' 861 | 862 | 863 | def handle_string(self, token_text): 864 | if self.last_type in ['TK_START_BLOCK', 'TK_END_BLOCK', 'TK_SEMICOLON']: 865 | self.append_newline() 866 | elif self.last_type == 'TK_WORD': 867 | self.append(' ') 868 | 869 | # Try to replace \x-encoded characters with their readable equivalent, 870 | # if it is possible (e.g. '\x41\x42\x43\x01' becomes 'ABC\x01'). 871 | try: 872 | token_text = token_text.encode().decode('unicode_escape') 873 | except UnicodeError: 874 | pass 875 | 876 | self.append(token_text) 877 | 878 | 879 | def handle_equals(self, token_text): 880 | if self.flags.var_line: 881 | # just got an '=' in a var-line, different line breaking rules will apply 882 | self.flags.var_line_tainted = True 883 | 884 | self.append(' ') 885 | self.append(token_text) 886 | self.append(' ') 887 | 888 | 889 | def handle_operator(self, token_text): 890 | space_before = True 891 | space_after = True 892 | 893 | if self.flags.var_line and token_text == ',' and self.is_expression(self.flags.mode): 894 | # do not break on comma, for ( var a = 1, b = 2 895 | self.flags.var_line_tainted = False 896 | 897 | if self.flags.var_line and token_text == ',': 898 | if self.flags.var_line_tainted: 899 | self.append(token_text) 900 | self.flags.var_line_reindented = True 901 | self.flags.var_line_tainted = False 902 | self.append_newline() 903 | return 904 | else: 905 | self.flags.var_line_tainted = False 906 | 907 | if self.last_text in ['return', 'throw']: 908 | # return had a special handling in TK_WORD 909 | self.append(' ') 910 | self.append(token_text) 911 | return 912 | 913 | if token_text == ':' and self.flags.in_case: 914 | self.append(token_text) 915 | self.append_newline() 916 | self.flags.in_case = False 917 | return 918 | 919 | if token_text == '::': 920 | # no spaces around the exotic namespacing syntax operator 921 | self.append(token_text) 922 | return 923 | 924 | if token_text == ',': 925 | if self.flags.var_line: 926 | if self.flags.var_line_tainted: 927 | # This never happens, as it's handled previously, right? 928 | self.append(token_text) 929 | self.append_newline() 930 | self.flags.var_line_tainted = False 931 | else: 932 | self.append(token_text) 933 | self.append(' ') 934 | elif self.last_type == 'TK_END_BLOCK' and self.flags.mode != '(EXPRESSION)': 935 | self.append(token_text) 936 | if self.flags.mode == 'OBJECT' and self.last_text == '}': 937 | self.append_newline() 938 | else: 939 | self.append(' ') 940 | else: 941 | if self.flags.mode == 'OBJECT': 942 | self.append(token_text) 943 | self.append_newline() 944 | else: 945 | # EXPR or DO_BLOCK 946 | self.append(token_text) 947 | self.append(' ') 948 | # comma handled 949 | return 950 | elif token_text in ['--', '++', '!'] \ 951 | or (token_text in ['+', '-'] \ 952 | and self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) \ 953 | or self.last_text in self.line_starters: 954 | 955 | space_before = False 956 | space_after = False 957 | 958 | if self.last_text == ';' and self.is_expression(self.flags.mode): 959 | # for (;; ++i) 960 | # ^^ 961 | space_before = True 962 | 963 | if self.last_type == 'TK_WORD' and self.last_text in self.line_starters: 964 | space_before = True 965 | 966 | if self.flags.mode == 'BLOCK' and self.last_text in ['{', ';']: 967 | # { foo: --i } 968 | # foo(): --bar 969 | self.append_newline() 970 | 971 | elif token_text == '.': 972 | # decimal digits or object.property 973 | space_before = False 974 | 975 | elif token_text == ':': 976 | if self.flags.ternary_depth == 0: 977 | self.flags.mode = 'OBJECT' 978 | space_before = False 979 | else: 980 | self.flags.ternary_depth -= 1 981 | elif token_text == '?': 982 | self.flags.ternary_depth += 1 983 | 984 | if space_before: 985 | self.append(' ') 986 | 987 | self.append(token_text) 988 | 989 | if space_after: 990 | self.append(' ') 991 | 992 | 993 | 994 | def handle_block_comment(self, token_text): 995 | 996 | lines = token_text.replace('\x0d', '').split('\x0a') 997 | # all lines start with an asterisk? that's a proper box comment 998 | if not any(l for l in lines[1:] if (l.lstrip())[0] != '*'): 999 | self.append_newline() 1000 | self.append(lines[0]) 1001 | for line in lines[1:]: 1002 | self.append_newline() 1003 | self.append(' ' + line.strip()) 1004 | else: 1005 | # simple block comment: leave intact 1006 | if len(lines) > 1: 1007 | # multiline comment starts on a new line 1008 | self.append_newline() 1009 | self.trim_output() 1010 | else: 1011 | # single line /* ... */ comment stays on the same line 1012 | self.append(' ') 1013 | for line in lines: 1014 | self.append(line) 1015 | self.append('\n') 1016 | self.append_newline() 1017 | 1018 | 1019 | def handle_inline_comment(self, token_text): 1020 | self.append(' ') 1021 | self.append(token_text) 1022 | if self.is_expression(self.flags.mode): 1023 | self.append(' ') 1024 | else: 1025 | self.append_newline_forced() 1026 | 1027 | 1028 | def handle_comment(self, token_text): 1029 | if self.wanted_newline: 1030 | self.append_newline() 1031 | else: 1032 | self.append(' ') 1033 | 1034 | self.append(token_text) 1035 | self.append_newline_forced() 1036 | 1037 | 1038 | def handle_unknown(self, token_text): 1039 | if self.last_text in ['return', 'throw']: 1040 | self.append(' ') 1041 | 1042 | self.append(token_text) 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | def main(): 1049 | 1050 | argv = sys.argv[1:] 1051 | 1052 | try: 1053 | opts, args = getopt.getopt(argv, "s:c:o:djbkil:h", ['indent-size=','indent-char=','outfile=', 'disable-preserve-newlines', 1054 | 'jslint-happy', 'brace-style=', 1055 | 'keep-array-indentation', 'indent-level=', 'help', 1056 | 'usage', 'stdin']) 1057 | except getopt.GetoptError: 1058 | usage() 1059 | sys.exit(2) 1060 | 1061 | js_options = default_options() 1062 | 1063 | file = None 1064 | outfile = 'stdout' 1065 | if len(args) == 1: 1066 | file = args[0] 1067 | elif len(args) == 0: 1068 | if os.isatty(0): 1069 | return usage() 1070 | else: 1071 | file = '-' 1072 | 1073 | for opt, arg in opts: 1074 | if opt in ('--keep-array-indentation', '-k'): 1075 | js_options.keep_array_indentation = True 1076 | elif opt in ('--outfile', '-o'): 1077 | outfile = arg 1078 | elif opt in ('--indent-size', '-s'): 1079 | js_options.indent_size = int(arg) 1080 | elif opt in ('--indent-char', '-c'): 1081 | js_options.indent_char = arg 1082 | elif opt in ('--disable-preserve_newlines', '-d'): 1083 | js_options.preserve_newlines = False 1084 | elif opt in ('--jslint-happy', '-j'): 1085 | js_options.jslint_happy = True 1086 | elif opt in ('--brace-style', '-b'): 1087 | js_options.brace_style = arg 1088 | elif opt in ('--stdin', '-i'): 1089 | file = '-' 1090 | elif opt in ('--help', '--usage', '--h'): 1091 | return usage() 1092 | 1093 | if file == None: 1094 | return usage() 1095 | else: 1096 | if outfile == 'stdout': 1097 | print(beautify_file(file, js_options)).encode('utf-8') 1098 | else: 1099 | f = open(outfile, 'w') 1100 | f.write(beautify_file(file, js_options) + '\n') 1101 | f.close() 1102 | 1103 | 1104 | if __name__ == "__main__": 1105 | main() 1106 | 1107 | 1108 | -------------------------------------------------------------------------------- /Text Filters/Markdown.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # 4 | # Markdown -- A text-to-HTML conversion tool for web writers 5 | # 6 | # Copyright (c) 2004 John Gruber 7 | # 8 | # 9 | 10 | 11 | package Markdown; 12 | require 5.006_000; 13 | use strict; 14 | use warnings; 15 | 16 | use Digest::MD5 qw(md5_hex); 17 | use vars qw($VERSION); 18 | $VERSION = '1.0.1'; 19 | # Tue 14 Dec 2004 20 | 21 | ## Disabled; causes problems under Perl 5.6.1: 22 | # use utf8; 23 | # binmode( STDOUT, ":utf8" ); # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html 24 | 25 | 26 | # 27 | # Global default settings: 28 | # 29 | my $g_empty_element_suffix = " />"; # Change to ">" for HTML output 30 | my $g_tab_width = 4; 31 | 32 | 33 | # 34 | # Globals: 35 | # 36 | 37 | # Regex to match balanced [brackets]. See Friedl's 38 | # "Mastering Regular Expressions", 2nd Ed., pp. 328-331. 39 | my $g_nested_brackets; 40 | $g_nested_brackets = qr{ 41 | (?> # Atomic matching 42 | [^\[\]]+ # Anything other than brackets 43 | | 44 | \[ 45 | (??{ $g_nested_brackets }) # Recursive set of nested brackets 46 | \] 47 | )* 48 | }x; 49 | 50 | 51 | # Table of hash values for escaped characters: 52 | my %g_escape_table; 53 | foreach my $char (split //, '\\`*_{}[]()>#+-.!') { 54 | $g_escape_table{$char} = md5_hex($char); 55 | } 56 | 57 | 58 | # Global hashes, used by various utility routines 59 | my %g_urls; 60 | my %g_titles; 61 | my %g_html_blocks; 62 | 63 | # Used to track when we're inside an ordered or unordered list 64 | # (see _ProcessListItems() for details): 65 | my $g_list_level = 0; 66 | 67 | 68 | #### Blosxom plug-in interface ########################################## 69 | 70 | # Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine 71 | # which posts Markdown should process, using a "meta-markup: markdown" 72 | # header. If it's set to 0 (the default), Markdown will process all 73 | # entries. 74 | my $g_blosxom_use_meta = 0; 75 | 76 | sub start { 1; } 77 | sub story { 78 | my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_; 79 | 80 | if ( (! $g_blosxom_use_meta) or 81 | (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i)) 82 | ){ 83 | $$body_ref = Markdown($$body_ref); 84 | } 85 | 1; 86 | } 87 | 88 | 89 | #### Movable Type plug-in interface ##################################### 90 | eval {require MT}; # Test to see if we're running in MT. 91 | unless ($@) { 92 | require MT; 93 | import MT; 94 | require MT::Template::Context; 95 | import MT::Template::Context; 96 | 97 | eval {require MT::Plugin}; # Test to see if we're running >= MT 3.0. 98 | unless ($@) { 99 | require MT::Plugin; 100 | import MT::Plugin; 101 | my $plugin = new MT::Plugin({ 102 | name => "Markdown", 103 | description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)", 104 | doc_link => 'http://daringfireball.net/projects/markdown/' 105 | }); 106 | MT->add_plugin( $plugin ); 107 | } 108 | 109 | MT::Template::Context->add_container_tag(MarkdownOptions => sub { 110 | my $ctx = shift; 111 | my $args = shift; 112 | my $builder = $ctx->stash('builder'); 113 | my $tokens = $ctx->stash('tokens'); 114 | 115 | if (defined ($args->{'output'}) ) { 116 | $ctx->stash('markdown_output', lc $args->{'output'}); 117 | } 118 | 119 | defined (my $str = $builder->build($ctx, $tokens) ) 120 | or return $ctx->error($builder->errstr); 121 | $str; # return value 122 | }); 123 | 124 | MT->add_text_filter('markdown' => { 125 | label => 'Markdown', 126 | docs => 'http://daringfireball.net/projects/markdown/', 127 | on_format => sub { 128 | my $text = shift; 129 | my $ctx = shift; 130 | my $raw = 0; 131 | if (defined $ctx) { 132 | my $output = $ctx->stash('markdown_output'); 133 | if (defined $output && $output =~ m/^html/i) { 134 | $g_empty_element_suffix = ">"; 135 | $ctx->stash('markdown_output', ''); 136 | } 137 | elsif (defined $output && $output eq 'raw') { 138 | $raw = 1; 139 | $ctx->stash('markdown_output', ''); 140 | } 141 | else { 142 | $raw = 0; 143 | $g_empty_element_suffix = " />"; 144 | } 145 | } 146 | $text = $raw ? $text : Markdown($text); 147 | $text; 148 | }, 149 | }); 150 | 151 | # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter: 152 | my $smartypants; 153 | 154 | { 155 | no warnings "once"; 156 | $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'}; 157 | } 158 | 159 | if ($smartypants) { 160 | MT->add_text_filter('markdown_with_smartypants' => { 161 | label => 'Markdown With SmartyPants', 162 | docs => 'http://daringfireball.net/projects/markdown/', 163 | on_format => sub { 164 | my $text = shift; 165 | my $ctx = shift; 166 | if (defined $ctx) { 167 | my $output = $ctx->stash('markdown_output'); 168 | if (defined $output && $output eq 'html') { 169 | $g_empty_element_suffix = ">"; 170 | } 171 | else { 172 | $g_empty_element_suffix = " />"; 173 | } 174 | } 175 | $text = Markdown($text); 176 | $text = $smartypants->($text, '1'); 177 | }, 178 | }); 179 | } 180 | } 181 | else { 182 | #### BBEdit/command-line text filter interface ########################## 183 | # Needs to be hidden from MT (and Blosxom when running in static mode). 184 | 185 | # We're only using $blosxom::version once; tell Perl not to warn us: 186 | no warnings 'once'; 187 | unless ( defined($blosxom::version) ) { 188 | use warnings; 189 | 190 | #### Check for command-line switches: ################# 191 | my %cli_opts; 192 | use Getopt::Long; 193 | Getopt::Long::Configure('pass_through'); 194 | GetOptions(\%cli_opts, 195 | 'version', 196 | 'shortversion', 197 | 'html4tags', 198 | ); 199 | if ($cli_opts{'version'}) { # Version info 200 | print "\nThis is Markdown, version $VERSION.\n"; 201 | print "Copyright 2004 John Gruber\n"; 202 | print "http://daringfireball.net/projects/markdown/\n\n"; 203 | exit 0; 204 | } 205 | if ($cli_opts{'shortversion'}) { # Just the version number string. 206 | print $VERSION; 207 | exit 0; 208 | } 209 | if ($cli_opts{'html4tags'}) { # Use HTML tag style instead of XHTML 210 | $g_empty_element_suffix = ">"; 211 | } 212 | 213 | 214 | #### Process incoming text: ########################### 215 | my $text; 216 | { 217 | local $/; # Slurp the whole file 218 | $text = <>; 219 | } 220 | print Markdown($text); 221 | } 222 | } 223 | 224 | 225 | 226 | sub Markdown { 227 | # 228 | # Main function. The order in which other subs are called here is 229 | # essential. Link and image substitutions need to happen before 230 | # _EscapeSpecialChars(), so that any *'s or _'s in the 231 | # and tags get encoded. 232 | # 233 | my $text = shift; 234 | 235 | # Clear the global hashes. If we don't clear these, you get conflicts 236 | # from other articles when generating a page which contains more than 237 | # one article (e.g. an index page that shows the N most recent 238 | # articles): 239 | %g_urls = (); 240 | %g_titles = (); 241 | %g_html_blocks = (); 242 | 243 | 244 | # Standardize line endings: 245 | $text =~ s{\r\n}{\n}g; # DOS to Unix 246 | $text =~ s{\r}{\n}g; # Mac to Unix 247 | 248 | # Make sure $text ends with a couple of newlines: 249 | $text .= "\n\n"; 250 | 251 | # Convert all tabs to spaces. 252 | $text = _Detab($text); 253 | 254 | # Strip any lines consisting only of spaces and tabs. 255 | # This makes subsequent regexen easier to write, because we can 256 | # match consecutive blank lines with /\n+/ instead of something 257 | # contorted like /[ \t]*\n+/ . 258 | $text =~ s/^[ \t]+$//mg; 259 | 260 | # Turn block-level HTML blocks into hash entries 261 | $text = _HashHTMLBlocks($text); 262 | 263 | # Strip link definitions, store in hashes. 264 | $text = _StripLinkDefinitions($text); 265 | 266 | $text = _RunBlockGamut($text); 267 | 268 | $text = _UnescapeSpecialChars($text); 269 | 270 | return $text . "\n"; 271 | } 272 | 273 | 274 | sub _StripLinkDefinitions { 275 | # 276 | # Strips link definitions from text, stores the URLs and titles in 277 | # hash references. 278 | # 279 | my $text = shift; 280 | my $less_than_tab = $g_tab_width - 1; 281 | 282 | # Link defs are in the form: ^[id]: url "optional title" 283 | while ($text =~ s{ 284 | ^[ ]{0,$less_than_tab}\[(.+)\]: # id = $1 285 | [ \t]* 286 | \n? # maybe *one* newline 287 | [ \t]* 288 | ? # url = $2 289 | [ \t]* 290 | \n? # maybe one newline 291 | [ \t]* 292 | (?: 293 | (?<=\s) # lookbehind for whitespace 294 | ["(] 295 | (.+?) # title = $3 296 | [")] 297 | [ \t]* 298 | )? # title is optional 299 | (?:\n+|\Z) 300 | } 301 | {}mx) { 302 | $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 ); # Link IDs are case-insensitive 303 | if ($3) { 304 | $g_titles{lc $1} = $3; 305 | $g_titles{lc $1} =~ s/"/"/g; 306 | } 307 | } 308 | 309 | return $text; 310 | } 311 | 312 | 313 | sub _HashHTMLBlocks { 314 | my $text = shift; 315 | my $less_than_tab = $g_tab_width - 1; 316 | 317 | # Hashify HTML blocks: 318 | # We only want to do this for block-level HTML tags, such as headers, 319 | # lists, and tables. That's because we still want to wrap

s around 320 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors, 321 | # phrase emphasis, and spans. The list of tags we're looking for is 322 | # hard-coded: 323 | my $block_tags_a = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del/; 324 | my $block_tags_b = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math/; 325 | 326 | # First, look for nested blocks, e.g.: 327 | #

328 | #
329 | # tags for inner block must be indented. 330 | #
331 | #
332 | # 333 | # The outermost tags must start at the left margin for this to match, and 334 | # the inner nested divs must be indented. 335 | # We need to do this before the next, more liberal match, because the next 336 | # match will start at the first `
` and stop at the first `
`. 337 | $text =~ s{ 338 | ( # save in $1 339 | ^ # start of line (with /m) 340 | <($block_tags_a) # start tag = $2 341 | \b # word break 342 | (.*\n)*? # any number of lines, minimally matching 343 | # the matching end tag 344 | [ \t]* # trailing spaces/tabs 345 | (?=\n+|\Z) # followed by a newline or end of document 346 | ) 347 | }{ 348 | my $key = md5_hex($1); 349 | $g_html_blocks{$key} = $1; 350 | "\n\n" . $key . "\n\n"; 351 | }egmx; 352 | 353 | 354 | # 355 | # Now match more liberally, simply from `\n` to `\n` 356 | # 357 | $text =~ s{ 358 | ( # save in $1 359 | ^ # start of line (with /m) 360 | <($block_tags_b) # start tag = $2 361 | \b # word break 362 | (.*\n)*? # any number of lines, minimally matching 363 | .* # the matching end tag 364 | [ \t]* # trailing spaces/tabs 365 | (?=\n+|\Z) # followed by a newline or end of document 366 | ) 367 | }{ 368 | my $key = md5_hex($1); 369 | $g_html_blocks{$key} = $1; 370 | "\n\n" . $key . "\n\n"; 371 | }egmx; 372 | # Special case just for
. It was easier to make a special case than 373 | # to make the other regex more complicated. 374 | $text =~ s{ 375 | (?: 376 | (?<=\n\n) # Starting after a blank line 377 | | # or 378 | \A\n? # the beginning of the doc 379 | ) 380 | ( # save in $1 381 | [ ]{0,$less_than_tab} 382 | <(hr) # start tag = $2 383 | \b # word break 384 | ([^<>])*? # 385 | /?> # the matching end tag 386 | [ \t]* 387 | (?=\n{2,}|\Z) # followed by a blank line or end of document 388 | ) 389 | }{ 390 | my $key = md5_hex($1); 391 | $g_html_blocks{$key} = $1; 392 | "\n\n" . $key . "\n\n"; 393 | }egx; 394 | 395 | # Special case for standalone HTML comments: 396 | $text =~ s{ 397 | (?: 398 | (?<=\n\n) # Starting after a blank line 399 | | # or 400 | \A\n? # the beginning of the doc 401 | ) 402 | ( # save in $1 403 | [ ]{0,$less_than_tab} 404 | (?s: 405 | 408 | ) 409 | [ \t]* 410 | (?=\n{2,}|\Z) # followed by a blank line or end of document 411 | ) 412 | }{ 413 | my $key = md5_hex($1); 414 | $g_html_blocks{$key} = $1; 415 | "\n\n" . $key . "\n\n"; 416 | }egx; 417 | 418 | 419 | return $text; 420 | } 421 | 422 | 423 | sub _RunBlockGamut { 424 | # 425 | # These are all the transformations that form block-level 426 | # tags like paragraphs, headers, and list items. 427 | # 428 | my $text = shift; 429 | 430 | $text = _DoHeaders($text); 431 | 432 | # Do Horizontal Rules: 433 | $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n tags around block-level tags. 447 | $text = _HashHTMLBlocks($text); 448 | 449 | $text = _FormParagraphs($text); 450 | 451 | return $text; 452 | } 453 | 454 | 455 | sub _RunSpanGamut { 456 | # 457 | # These are all the transformations that occur *within* block-level 458 | # tags like paragraphs, headers, and list items. 459 | # 460 | my $text = shift; 461 | 462 | $text = _DoCodeSpans($text); 463 | 464 | $text = _EscapeSpecialChars($text); 465 | 466 | # Process anchor and image tags. Images must come first, 467 | # because ![foo][f] looks like an anchor. 468 | $text = _DoImages($text); 469 | $text = _DoAnchors($text); 470 | 471 | # Make links out of things like `` 472 | # Must come after _DoAnchors(), because you can use < and > 473 | # delimiters in inline links like [this](). 474 | $text = _DoAutoLinks($text); 475 | 476 | $text = _EncodeAmpsAndAngles($text); 477 | 478 | $text = _DoItalicsAndBold($text); 479 | 480 | # Do hard breaks: 481 | $text =~ s/ {2,}\n/ or tags. 493 | # my $tags_to_skip = qr!<(/?)(?:pre|code|kbd|script|math)[\s>]!; 494 | 495 | foreach my $cur_token (@$tokens) { 496 | if ($cur_token->[0] eq "tag") { 497 | # Within tags, encode * and _ so they don't conflict 498 | # with their use in Markdown for italics and strong. 499 | # We're replacing each such character with its 500 | # corresponding MD5 checksum value; this is likely 501 | # overkill, but it should prevent us from colliding 502 | # with the escape values by accident. 503 | $cur_token->[1] =~ s! \* !$g_escape_table{'*'}!gx; 504 | $cur_token->[1] =~ s! _ !$g_escape_table{'_'}!gx; 505 | $text .= $cur_token->[1]; 506 | } else { 507 | my $t = $cur_token->[1]; 508 | $t = _EncodeBackslashEscapes($t); 509 | $text .= $t; 510 | } 511 | } 512 | return $text; 513 | } 514 | 515 | 516 | sub _DoAnchors { 517 | # 518 | # Turn Markdown link shortcuts into XHTML
tags. 519 | # 520 | my $text = shift; 521 | 522 | # 523 | # First, handle reference-style links: [link text] [id] 524 | # 525 | $text =~ s{ 526 | ( # wrap whole match in $1 527 | \[ 528 | ($g_nested_brackets) # link text = $2 529 | \] 530 | 531 | [ ]? # one optional space 532 | (?:\n[ ]*)? # one optional newline followed by spaces 533 | 534 | \[ 535 | (.*?) # id = $3 536 | \] 537 | ) 538 | }{ 539 | my $result; 540 | my $whole_match = $1; 541 | my $link_text = $2; 542 | my $link_id = lc $3; 543 | 544 | if ($link_id eq "") { 545 | $link_id = lc $link_text; # for shortcut links like [this][]. 546 | } 547 | 548 | if (defined $g_urls{$link_id}) { 549 | my $url = $g_urls{$link_id}; 550 | $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid 551 | $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. 552 | $result = "? # href = $3 578 | [ \t]* 579 | ( # $4 580 | (['"]) # quote char = $5 581 | (.*?) # Title = $6 582 | \5 # matching quote 583 | )? # title is optional 584 | \) 585 | ) 586 | }{ 587 | my $result; 588 | my $whole_match = $1; 589 | my $link_text = $2; 590 | my $url = $3; 591 | my $title = $6; 592 | 593 | $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid 594 | $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. 595 | $result = " tags. 616 | # 617 | my $text = shift; 618 | 619 | # 620 | # First, handle reference-style labeled images: ![alt text][id] 621 | # 622 | $text =~ s{ 623 | ( # wrap whole match in $1 624 | !\[ 625 | (.*?) # alt text = $2 626 | \] 627 | 628 | [ ]? # one optional space 629 | (?:\n[ ]*)? # one optional newline followed by spaces 630 | 631 | \[ 632 | (.*?) # id = $3 633 | \] 634 | 635 | ) 636 | }{ 637 | my $result; 638 | my $whole_match = $1; 639 | my $alt_text = $2; 640 | my $link_id = lc $3; 641 | 642 | if ($link_id eq "") { 643 | $link_id = lc $alt_text; # for shortcut links like ![this][]. 644 | } 645 | 646 | $alt_text =~ s/"/"/g; 647 | if (defined $g_urls{$link_id}) { 648 | my $url = $g_urls{$link_id}; 649 | $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid 650 | $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. 651 | $result = "\"$alt_text\"";? # src url = $3 680 | [ \t]* 681 | ( # $4 682 | (['"]) # quote char = $5 683 | (.*?) # title = $6 684 | \5 # matching quote 685 | [ \t]* 686 | )? # title is optional 687 | \) 688 | ) 689 | }{ 690 | my $result; 691 | my $whole_match = $1; 692 | my $alt_text = $2; 693 | my $url = $3; 694 | my $title = ''; 695 | if (defined($6)) { 696 | $title = $6; 697 | } 698 | 699 | $alt_text =~ s/"/"/g; 700 | $title =~ s/"/"/g; 701 | $url =~ s! \* !$g_escape_table{'*'}!gx; # We've got to encode these to avoid 702 | $url =~ s! _ !$g_escape_table{'_'}!gx; # conflicting with italics/bold. 703 | $result = "\"$alt_text\"";" . _RunSpanGamut($1) . "\n\n"; 730 | }egmx; 731 | 732 | $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{ 733 | "

" . _RunSpanGamut($1) . "

\n\n"; 734 | }egmx; 735 | 736 | 737 | # atx-style headers: 738 | # # Header 1 739 | # ## Header 2 740 | # ## Header 2 with closing hashes ## 741 | # ... 742 | # ###### Header 6 743 | # 744 | $text =~ s{ 745 | ^(\#{1,6}) # $1 = string of #'s 746 | [ \t]* 747 | (.+?) # $2 = Header text 748 | [ \t]* 749 | \#* # optional closing #'s (not counted) 750 | \n+ 751 | }{ 752 | my $h_level = length($1); 753 | "" . _RunSpanGamut($2) . "\n\n"; 754 | }egmx; 755 | 756 | return $text; 757 | } 758 | 759 | 760 | sub _DoLists { 761 | # 762 | # Form HTML ordered (numbered) and unordered (bulleted) lists. 763 | # 764 | my $text = shift; 765 | my $less_than_tab = $g_tab_width - 1; 766 | 767 | # Re-usable patterns to match list item bullets and number markers: 768 | my $marker_ul = qr/[*+-]/; 769 | my $marker_ol = qr/\d+[.]/; 770 | my $marker_any = qr/(?:$marker_ul|$marker_ol)/; 771 | 772 | # Re-usable pattern to match any entirel ul or ol list: 773 | my $whole_list = qr{ 774 | ( # $1 = whole list 775 | ( # $2 776 | [ ]{0,$less_than_tab} 777 | (${marker_any}) # $3 = first list item marker 778 | [ \t]+ 779 | ) 780 | (?s:.+?) 781 | ( # $4 782 | \z 783 | | 784 | \n{2,} 785 | (?=\S) 786 | (?! # Negative lookahead for another list item marker 787 | [ \t]* 788 | ${marker_any}[ \t]+ 789 | ) 790 | ) 791 | ) 792 | }mx; 793 | 794 | # We use a different prefix before nested lists than top-level lists. 795 | # See extended comment in _ProcessListItems(). 796 | # 797 | # Note: There's a bit of duplication here. My original implementation 798 | # created a scalar regex pattern as the conditional result of the test on 799 | # $g_list_level, and then only ran the $text =~ s{...}{...}egmx 800 | # substitution once, using the scalar as the pattern. This worked, 801 | # everywhere except when running under MT on my hosting account at Pair 802 | # Networks. There, this caused all rebuilds to be killed by the reaper (or 803 | # perhaps they crashed, but that seems incredibly unlikely given that the 804 | # same script on the same server ran fine *except* under MT. I've spent 805 | # more time trying to figure out why this is happening than I'd like to 806 | # admit. My only guess, backed up by the fact that this workaround works, 807 | # is that Perl optimizes the substition when it can figure out that the 808 | # pattern will never change, and when this optimization isn't on, we run 809 | # afoul of the reaper. Thus, the slightly redundant code to that uses two 810 | # static s/// patterns rather than one conditional pattern. 811 | 812 | if ($g_list_level) { 813 | $text =~ s{ 814 | ^ 815 | $whole_list 816 | }{ 817 | my $list = $1; 818 | my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol"; 819 | # Turn double returns into triple returns, so that we can make a 820 | # paragraph for the last item in a list, if necessary: 821 | $list =~ s/\n{2,}/\n\n\n/g; 822 | my $result = _ProcessListItems($list, $marker_any); 823 | $result = "<$list_type>\n" . $result . "\n"; 824 | $result; 825 | }egmx; 826 | } 827 | else { 828 | $text =~ s{ 829 | (?:(?<=\n\n)|\A\n?) 830 | $whole_list 831 | }{ 832 | my $list = $1; 833 | my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol"; 834 | # Turn double returns into triple returns, so that we can make a 835 | # paragraph for the last item in a list, if necessary: 836 | $list =~ s/\n{2,}/\n\n\n/g; 837 | my $result = _ProcessListItems($list, $marker_any); 838 | $result = "<$list_type>\n" . $result . "\n"; 839 | $result; 840 | }egmx; 841 | } 842 | 843 | 844 | return $text; 845 | } 846 | 847 | 848 | sub _ProcessListItems { 849 | # 850 | # Process the contents of a single ordered or unordered list, splitting it 851 | # into individual list items. 852 | # 853 | 854 | my $list_str = shift; 855 | my $marker_any = shift; 856 | 857 | 858 | # The $g_list_level global keeps track of when we're inside a list. 859 | # Each time we enter a list, we increment it; when we leave a list, 860 | # we decrement. If it's zero, we're not in a list anymore. 861 | # 862 | # We do this because when we're not inside a list, we want to treat 863 | # something like this: 864 | # 865 | # I recommend upgrading to version 866 | # 8. Oops, now this line is treated 867 | # as a sub-list. 868 | # 869 | # As a single paragraph, despite the fact that the second line starts 870 | # with a digit-period-space sequence. 871 | # 872 | # Whereas when we're inside a list (or sub-list), that line will be 873 | # treated as the start of a sub-list. What a kludge, huh? This is 874 | # an aspect of Markdown's syntax that's hard to parse perfectly 875 | # without resorting to mind-reading. Perhaps the solution is to 876 | # change the syntax rules such that sub-lists must start with a 877 | # starting cardinal number; e.g. "1." or "a.". 878 | 879 | $g_list_level++; 880 | 881 | # trim trailing blank lines: 882 | $list_str =~ s/\n{2,}\z/\n/; 883 | 884 | 885 | $list_str =~ s{ 886 | (\n)? # leading line = $1 887 | (^[ \t]*) # leading whitespace = $2 888 | ($marker_any) [ \t]+ # list marker = $3 889 | ((?s:.+?) # list item text = $4 890 | (\n{1,2})) 891 | (?= \n* (\z | \2 ($marker_any) [ \t]+)) 892 | }{ 893 | my $item = $4; 894 | my $leading_line = $1; 895 | my $leading_space = $2; 896 | 897 | if ($leading_line or ($item =~ m/\n{2,}/)) { 898 | $item = _RunBlockGamut(_Outdent($item)); 899 | } 900 | else { 901 | # Recursion for sub-lists: 902 | $item = _DoLists(_Outdent($item)); 903 | chomp $item; 904 | $item = _RunSpanGamut($item); 905 | } 906 | 907 | "
  • " . $item . "
  • \n"; 908 | }egmx; 909 | 910 | $g_list_level--; 911 | return $list_str; 912 | } 913 | 914 | 915 | 916 | sub _DoCodeBlocks { 917 | # 918 | # Process Markdown `
    ` blocks.
     919 | #	
     920 | 
     921 | 	my $text = shift;
     922 | 
     923 | 	$text =~ s{
     924 | 			(?:\n\n|\A)
     925 | 			(	            # $1 = the code block -- one or more lines, starting with a space/tab
     926 | 			  (?:
     927 | 			    (?:[ ]{$g_tab_width} | \t)  # Lines must start with a tab or a tab-width of spaces
     928 | 			    .*\n+
     929 | 			  )+
     930 | 			)
     931 | 			((?=^[ ]{0,$g_tab_width}\S)|\Z)	# Lookahead for non-space at line-start, or end of doc
     932 | 		}{
     933 | 			my $codeblock = $1;
     934 | 			my $result; # return value
     935 | 
     936 | 			$codeblock = _EncodeCode(_Outdent($codeblock));
     937 | 			$codeblock = _Detab($codeblock);
     938 | 			$codeblock =~ s/\A\n+//; # trim leading newlines
     939 | 			$codeblock =~ s/\s+\z//; # trim trailing whitespace
     940 | 
     941 | 			$result = "\n\n
    " . $codeblock . "\n
    \n\n"; 942 | 943 | $result; 944 | }egmx; 945 | 946 | return $text; 947 | } 948 | 949 | 950 | sub _DoCodeSpans { 951 | # 952 | # * Backtick quotes are used for spans. 953 | # 954 | # * You can use multiple backticks as the delimiters if you want to 955 | # include literal backticks in the code span. So, this input: 956 | # 957 | # Just type ``foo `bar` baz`` at the prompt. 958 | # 959 | # Will translate to: 960 | # 961 | #

    Just type foo `bar` baz at the prompt.

    962 | # 963 | # There's no arbitrary limit to the number of backticks you 964 | # can use as delimters. If you need three consecutive backticks 965 | # in your code, use four for delimiters, etc. 966 | # 967 | # * You can use spaces to get literal backticks at the edges: 968 | # 969 | # ... type `` `bar` `` ... 970 | # 971 | # Turns to: 972 | # 973 | # ... type `bar` ... 974 | # 975 | 976 | my $text = shift; 977 | 978 | $text =~ s@ 979 | (`+) # $1 = Opening run of ` 980 | (.+?) # $2 = The code block 981 | (?$c
    "; 990 | @egsx; 991 | 992 | return $text; 993 | } 994 | 995 | 996 | sub _EncodeCode { 997 | # 998 | # Encode/escape certain characters inside Markdown code runs. 999 | # The point is that in code, these characters are literals, 1000 | # and lose their special Markdown meanings. 1001 | # 1002 | local $_ = shift; 1003 | 1004 | # Encode all ampersands; HTML entities are not 1005 | # entities within a Markdown code span. 1006 | s/&/&/g; 1007 | 1008 | # Encode $'s, but only if we're running under Blosxom. 1009 | # (Blosxom interpolates Perl variables in article bodies.) 1010 | { 1011 | no warnings 'once'; 1012 | if (defined($blosxom::version)) { 1013 | s/\$/$/g; 1014 | } 1015 | } 1016 | 1017 | 1018 | # Do the angle bracket song and dance: 1019 | s! < !<!gx; 1020 | s! > !>!gx; 1021 | 1022 | # Now, escape characters that are magic in Markdown: 1023 | s! \* !$g_escape_table{'*'}!gx; 1024 | s! _ !$g_escape_table{'_'}!gx; 1025 | s! { !$g_escape_table{'{'}!gx; 1026 | s! } !$g_escape_table{'}'}!gx; 1027 | s! \[ !$g_escape_table{'['}!gx; 1028 | s! \] !$g_escape_table{']'}!gx; 1029 | s! \\ !$g_escape_table{'\\'}!gx; 1030 | 1031 | return $_; 1032 | } 1033 | 1034 | 1035 | sub _DoItalicsAndBold { 1036 | my $text = shift; 1037 | 1038 | # must go first: 1039 | $text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 } 1040 | {$2}gsx; 1041 | 1042 | $text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 } 1043 | {$2}gsx; 1044 | 1045 | return $text; 1046 | } 1047 | 1048 | 1049 | sub _DoBlockQuotes { 1050 | my $text = shift; 1051 | 1052 | $text =~ s{ 1053 | ( # Wrap whole match in $1 1054 | ( 1055 | ^[ \t]*>[ \t]? # '>' at the start of a line 1056 | .+\n # rest of the first line 1057 | (.+\n)* # subsequent consecutive lines 1058 | \n* # blanks 1059 | )+ 1060 | ) 1061 | }{ 1062 | my $bq = $1; 1063 | $bq =~ s/^[ \t]*>[ \t]?//gm; # trim one level of quoting 1064 | $bq =~ s/^[ \t]+$//mg; # trim whitespace-only lines 1065 | $bq = _RunBlockGamut($bq); # recurse 1066 | 1067 | $bq =~ s/^/ /g; 1068 | # These leading spaces screw with
     content, so we need to fix that:
    1069 | 			$bq =~ s{
    1070 | 					(\s*
    .+?
    ) 1071 | }{ 1072 | my $pre = $1; 1073 | $pre =~ s/^ //mg; 1074 | $pre; 1075 | }egsx; 1076 | 1077 | "
    \n$bq\n
    \n\n"; 1078 | }egmx; 1079 | 1080 | 1081 | return $text; 1082 | } 1083 | 1084 | 1085 | sub _FormParagraphs { 1086 | # 1087 | # Params: 1088 | # $text - string to process with html

    tags 1089 | # 1090 | my $text = shift; 1091 | 1092 | # Strip leading and trailing lines: 1093 | $text =~ s/\A\n+//; 1094 | $text =~ s/\n+\z//; 1095 | 1096 | my @grafs = split(/\n{2,}/, $text); 1097 | 1098 | # 1099 | # Wrap

    tags. 1100 | # 1101 | foreach (@grafs) { 1102 | unless (defined( $g_html_blocks{$_} )) { 1103 | $_ = _RunSpanGamut($_); 1104 | s/^([ \t]*)/

    /; 1105 | $_ .= "

    "; 1106 | } 1107 | } 1108 | 1109 | # 1110 | # Unhashify HTML blocks 1111 | # 1112 | foreach (@grafs) { 1113 | if (defined( $g_html_blocks{$_} )) { 1114 | $_ = $g_html_blocks{$_}; 1115 | } 1116 | } 1117 | 1118 | return join "\n\n", @grafs; 1119 | } 1120 | 1121 | 1122 | sub _EncodeAmpsAndAngles { 1123 | # Smart processing for ampersands and angle brackets that need to be encoded. 1124 | 1125 | my $text = shift; 1126 | 1127 | # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 1128 | # http://bumppo.net/projects/amputator/ 1129 | $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&/g; 1130 | 1131 | # Encode naked <'s 1132 | $text =~ s{<(?![a-z/?\$!])}{<}gi; 1133 | 1134 | return $text; 1135 | } 1136 | 1137 | 1138 | sub _EncodeBackslashEscapes { 1139 | # 1140 | # Parameter: String. 1141 | # Returns: The string, with after processing the following backslash 1142 | # escape sequences. 1143 | # 1144 | local $_ = shift; 1145 | 1146 | s! \\\\ !$g_escape_table{'\\'}!gx; # Must process escaped backslashes first. 1147 | s! \\` !$g_escape_table{'`'}!gx; 1148 | s! \\\* !$g_escape_table{'*'}!gx; 1149 | s! \\_ !$g_escape_table{'_'}!gx; 1150 | s! \\\{ !$g_escape_table{'{'}!gx; 1151 | s! \\\} !$g_escape_table{'}'}!gx; 1152 | s! \\\[ !$g_escape_table{'['}!gx; 1153 | s! \\\] !$g_escape_table{']'}!gx; 1154 | s! \\\( !$g_escape_table{'('}!gx; 1155 | s! \\\) !$g_escape_table{')'}!gx; 1156 | s! \\> !$g_escape_table{'>'}!gx; 1157 | s! \\\# !$g_escape_table{'#'}!gx; 1158 | s! \\\+ !$g_escape_table{'+'}!gx; 1159 | s! \\\- !$g_escape_table{'-'}!gx; 1160 | s! \\\. !$g_escape_table{'.'}!gx; 1161 | s{ \\! }{$g_escape_table{'!'}}gx; 1162 | 1163 | return $_; 1164 | } 1165 | 1166 | 1167 | sub _DoAutoLinks { 1168 | my $text = shift; 1169 | 1170 | $text =~ s{<((https?|ftp):[^'">\s]+)>}{
    $1}gi; 1171 | 1172 | # Email addresses: 1173 | $text =~ s{ 1174 | < 1175 | (?:mailto:)? 1176 | ( 1177 | [-.\w]+ 1178 | \@ 1179 | [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ 1180 | ) 1181 | > 1182 | }{ 1183 | _EncodeEmailAddress( _UnescapeSpecialChars($1) ); 1184 | }egix; 1185 | 1186 | return $text; 1187 | } 1188 | 1189 | 1190 | sub _EncodeEmailAddress { 1191 | # 1192 | # Input: an email address, e.g. "foo@example.com" 1193 | # 1194 | # Output: the email address as a mailto link, with each character 1195 | # of the address encoded as either a decimal or hex entity, in 1196 | # the hopes of foiling most address harvesting spam bots. E.g.: 1197 | # 1198 | # foo 1200 | # @example.com 1201 | # 1202 | # Based on a filter by Matthew Wickline, posted to the BBEdit-Talk 1203 | # mailing list: 1204 | # 1205 | 1206 | my $addr = shift; 1207 | 1208 | srand; 1209 | my @encode = ( 1210 | sub { '&#' . ord(shift) . ';' }, 1211 | sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' }, 1212 | sub { shift }, 1213 | ); 1214 | 1215 | $addr = "mailto:" . $addr; 1216 | 1217 | $addr =~ s{(.)}{ 1218 | my $char = $1; 1219 | if ( $char eq '@' ) { 1220 | # this *must* be encoded. I insist. 1221 | $char = $encode[int rand 1]->($char); 1222 | } elsif ( $char ne ':' ) { 1223 | # leave ':' alone (to spot mailto: later) 1224 | my $r = rand; 1225 | # roughly 10% raw, 45% hex, 45% dec 1226 | $char = ( 1227 | $r > .9 ? $encode[2]->($char) : 1228 | $r < .45 ? $encode[1]->($char) : 1229 | $encode[0]->($char) 1230 | ); 1231 | } 1232 | $char; 1233 | }gex; 1234 | 1235 | $addr = qq{$addr}; 1236 | $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part 1237 | 1238 | return $addr; 1239 | } 1240 | 1241 | 1242 | sub _UnescapeSpecialChars { 1243 | # 1244 | # Swap back in all the special characters we've hidden. 1245 | # 1246 | my $text = shift; 1247 | 1248 | while( my($char, $hash) = each(%g_escape_table) ) { 1249 | $text =~ s/$hash/$char/g; 1250 | } 1251 | return $text; 1252 | } 1253 | 1254 | 1255 | sub _TokenizeHTML { 1256 | # 1257 | # Parameter: String containing HTML markup. 1258 | # Returns: Reference to an array of the tokens comprising the input 1259 | # string. Each token is either a tag (possibly with nested, 1260 | # tags contained therein, such as , or a 1261 | # run of text between tags. Each element of the array is a 1262 | # two-element array; the first is either 'tag' or 'text'; 1263 | # the second is the actual value. 1264 | # 1265 | # 1266 | # Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin. 1267 | # 1268 | # 1269 | 1270 | my $str = shift; 1271 | my $pos = 0; 1272 | my $len = length $str; 1273 | my @tokens; 1274 | 1275 | my $depth = 6; 1276 | my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x $depth); 1277 | my $match = qr/(?s: ) | # comment 1278 | (?s: <\? .*? \?> ) | # processing instruction 1279 | $nested_tags/ix; # nested tags 1280 | 1281 | while ($str =~ m/($match)/g) { 1282 | my $whole_tag = $1; 1283 | my $sec_start = pos $str; 1284 | my $tag_start = $sec_start - length $whole_tag; 1285 | if ($pos < $tag_start) { 1286 | push @tokens, ['text', substr($str, $pos, $tag_start - $pos)]; 1287 | } 1288 | push @tokens, ['tag', $whole_tag]; 1289 | $pos = pos $str; 1290 | } 1291 | push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len; 1292 | \@tokens; 1293 | } 1294 | 1295 | 1296 | sub _Outdent { 1297 | # 1298 | # Remove one level of line-leading tabs or spaces 1299 | # 1300 | my $text = shift; 1301 | 1302 | $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm; 1303 | return $text; 1304 | } 1305 | 1306 | 1307 | sub _Detab { 1308 | # 1309 | # Cribbed from a post by Bart Lateur: 1310 | # 1311 | # 1312 | my $text = shift; 1313 | 1314 | $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge; 1315 | return $text; 1316 | } 1317 | 1318 | 1319 | 1; 1320 | 1321 | __END__ 1322 | 1323 | 1324 | =pod 1325 | 1326 | =head1 NAME 1327 | 1328 | B 1329 | 1330 | 1331 | =head1 SYNOPSIS 1332 | 1333 | B [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ] 1334 | [ I ... ] 1335 | 1336 | 1337 | =head1 DESCRIPTION 1338 | 1339 | Markdown is a text-to-HTML filter; it translates an easy-to-read / 1340 | easy-to-write structured text format into HTML. Markdown's text format 1341 | is most similar to that of plain text email, and supports features such 1342 | as headers, *emphasis*, code blocks, blockquotes, and links. 1343 | 1344 | Markdown's syntax is designed not as a generic markup language, but 1345 | specifically to serve as a front-end to (X)HTML. You can use span-level 1346 | HTML tags anywhere in a Markdown document, and you can use block level 1347 | HTML tags (like
    and as well). 1348 | 1349 | For more information about Markdown's syntax, see: 1350 | 1351 | http://daringfireball.net/projects/markdown/ 1352 | 1353 | 1354 | =head1 OPTIONS 1355 | 1356 | Use "--" to end switch parsing. For example, to open a file named "-z", use: 1357 | 1358 | Markdown.pl -- -z 1359 | 1360 | =over 4 1361 | 1362 | 1363 | =item B<--html4tags> 1364 | 1365 | Use HTML 4 style for empty element tags, e.g.: 1366 | 1367 |
    1368 | 1369 | instead of Markdown's default XHTML style tags, e.g.: 1370 | 1371 |
    1372 | 1373 | 1374 | =item B<-v>, B<--version> 1375 | 1376 | Display Markdown's version number and copyright information. 1377 | 1378 | 1379 | =item B<-s>, B<--shortversion> 1380 | 1381 | Display the short-form version number. 1382 | 1383 | 1384 | =back 1385 | 1386 | 1387 | 1388 | =head1 BUGS 1389 | 1390 | To file bug reports or feature requests (other than topics listed in the 1391 | Caveats section above) please send email to: 1392 | 1393 | support@daringfireball.net 1394 | 1395 | Please include with your report: (1) the example input; (2) the output 1396 | you expected; (3) the output Markdown actually produced. 1397 | 1398 | 1399 | =head1 VERSION HISTORY 1400 | 1401 | See the readme file for detailed release notes for this version. 1402 | 1403 | 1.0.1 - 14 Dec 2004 1404 | 1405 | 1.0 - 28 Aug 2004 1406 | 1407 | 1408 | =head1 AUTHOR 1409 | 1410 | John Gruber 1411 | http://daringfireball.net 1412 | 1413 | PHP port and other contributions by Michel Fortin 1414 | http://michelf.com 1415 | 1416 | 1417 | =head1 COPYRIGHT AND LICENSE 1418 | 1419 | Copyright (c) 2003-2004 John Gruber 1420 | 1421 | All rights reserved. 1422 | 1423 | Redistribution and use in source and binary forms, with or without 1424 | modification, are permitted provided that the following conditions are 1425 | met: 1426 | 1427 | * Redistributions of source code must retain the above copyright notice, 1428 | this list of conditions and the following disclaimer. 1429 | 1430 | * Redistributions in binary form must reproduce the above copyright 1431 | notice, this list of conditions and the following disclaimer in the 1432 | documentation and/or other materials provided with the distribution. 1433 | 1434 | * Neither the name "Markdown" nor the names of its contributors may 1435 | be used to endorse or promote products derived from this software 1436 | without specific prior written permission. 1437 | 1438 | This software is provided by the copyright holders and contributors "as 1439 | is" and any express or implied warranties, including, but not limited 1440 | to, the implied warranties of merchantability and fitness for a 1441 | particular purpose are disclaimed. In no event shall the copyright owner 1442 | or contributors be liable for any direct, indirect, incidental, special, 1443 | exemplary, or consequential damages (including, but not limited to, 1444 | procurement of substitute goods or services; loss of use, data, or 1445 | profits; or business interruption) however caused and on any theory of 1446 | liability, whether in contract, strict liability, or tort (including 1447 | negligence or otherwise) arising in any way out of the use of this 1448 | software, even if advised of the possibility of such damage. 1449 | 1450 | =cut 1451 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Create all BBEdit support directories if they don't already exist 5 | # Run this after cloning BBEdit-ApplicationSupport to 6 | # ~/Library/Application Support/BBEdit 7 | # 8 | 9 | mkdir -p Attachment Scripts 10 | mkdir -p Language Modules 11 | mkdir -p Scripts 12 | mkdir -p Unix Support 13 | mkdir -p Clippings 14 | mkdir -p Menu Scripts 15 | mkdir -p Startup Items 16 | mkdir -p Completion Data 17 | mkdir -p Plug-Ins 18 | mkdir -p Stationery 19 | mkdir -p HTML Templates 20 | mkdir -p Text Factories 21 | mkdir -p Clippings/Universal Items 22 | --------------------------------------------------------------------------------