diff --git a/doc/Rakefile b/doc/Rakefile index bf610259..93b113be 100644 --- a/doc/Rakefile +++ b/doc/Rakefile @@ -1,11 +1,6 @@ verbose(true) -DOCSET_PATHS = %w{ - /Library/Developer/Documentation/DocSets/com.apple.adc.documentation.AppleiOS4_3.iOSLibrary.docset/Contents/Resources/Documents/documentation/UIKit/Reference - /Library/Developer/Documentation/DocSets/com.apple.adc.documentation.AppleiOS4_3.iOSLibrary.docset/Contents/Resources/Documents/documentation/Cocoa/Reference -} - -class DocGenerator +class DocsetGenerator require 'rubygems' require 'nokogiri' require 'fileutils' @@ -140,7 +135,7 @@ class DocGenerator end end - def initialize(paths) + def initialize(outpath, paths) @input_paths = [] paths.each do |path| if File.directory?(path) @@ -149,6 +144,7 @@ class DocGenerator @input_paths << path end end + @outpath = outpath end def run @@ -163,17 +159,255 @@ class DocGenerator end sh "yard doc #{rb_files_dir}" - sh "mv doc html" + sh "mv doc \"#{@outpath}\"" end end +require 'redcloth' +class GuideGenerator < RedCloth + def initialize(src, dest) + base = File.basename(src, '.textile') + @title = base.gsub(/([a-z])([A-Z])/, '\1 \2') + @dest = dest + super(fix_anchors(File.read(src))) + end + + def run + File.open(@dest, 'w') do |io| + io.puts html_header + io.puts toc + io.puts '
+$ sudo motion update ++ +This command will grab the latest version of RubyMotion from the network and install it. You must be connected to the Internet to perform that command. + +You can always see the version number of the version of RubyMotion installed on your computer. + +
+$ motion -v +1.0 ++ +h2. Runtime + +The RubyMotion runtime implements the Ruby language functionality required during the execution of an application. The object model, builtin classes and memory management system are part of the runtime. + +Althrough similars in appearance, the RubyMotion runtime has a completely different implementation than "CRuby":http://www.ruby-lang.org/en/, the mainstream implementation. We will cover the main differences in the following sections. + +The key feature of the RubyMotion runtime is its tight integration with iOS, which makes applications very efficient. + +RubyMotion follows the Ruby 1.9 language specifications. + +h3. Object Model + +The object model of RubyMotion is based on "Objective-C":http://en.wikipedia.org/wiki/Objective-C, the underlying language runtime of the iOS SDK. Objective-C is an object-oriented flavor of C that has been, like Ruby, heavily influenced by the "Smalltalk":http://en.wikipedia.org/wiki/Smalltalk language. + +Sharing a common ancestor, the object models of Ruby and Objective-C are conceptually similar. For instance, both languages have the notion of open classes, single inheritance, and single dynamic message dispatch. + +RubyMotion implements the Ruby object model by using the "Objective-C runtime":http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html, the library that powers the Objective-C language, and indirectly, the iOS SDK APIs. + +In RubyMotion, Ruby classes, methods and objects are respectively Objective-C classes, methods and objects. And reciprocally, Objective-C classes, methods and objects are available in Ruby as if they were native. + +By sharing the same object model infrastructure, Objective-C and RubyMotion APIs can be interchangeable at no additional performance expense. + +h3. Builtin Classes + +The builtin classes of RubyMotion are based on the "Foundation framework":http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/ObjC_classic/_index.html, the base layer of iOS. + +The Foundation framework is an Objective-C framework, but due to the fact that RubyMotion is based on the Objective-C runtime, the classes that it defines can be naturally re-used in RubyMotion. + +Foundation exports the root object class +NSObject+, which is also used in RubyMotion as the root class of all classes. +Object+ is an alias to +NSObject+. + +
+class Hello; end +Hello.ancestors # => [Hello, NSObject, Kernel] ++ +Foundation also provides a set of primitive object classes, which are used as the base of Ruby builtin classes. + +|_<. Ruby Class |_<. Subclass of | +| +String+ | +NSMutableString+ (mutable version of +NSString+) | +| +Array+ | +NSMutableArray+ (mutable version of +NSArray+) | +| +Hash+ | +NSMutableDictionary+ (mutable version of +NSDictionary+) | +| +Numeric+ | +NSNumber+ | +| +Time+ | +NSDate+ | + +A consequence of hosting the Ruby builtin classes on Foundation is that instances respond to more messages. For instance, the +NSString+ class defines the +-uppercaseString+ method. Since the +String+ class is a subclass of +NSString+, strings created in Ruby also respond to that method. + +
+'hello'.uppercaseString # => 'HELLO ++ +Respectively, the Ruby interface of these builtin classes is implemented on their Foundation counterparts. As an example, +Array+'s +each+ method is implemented on +NSArray+. This allows primitive types to always respond to the same interface, regardless of where they come from. +each+ will always work on all arrays. + +
+def iterate(ary)
+ ary.each { |x| p x }
+end
+
+iterate [42]
+iterate NSArray.arrayWithObject(42)
+
+
+But the main purpose of this design is to allow the exchange of primitive types between Objective-C and Ruby at no cost, since they don't have to be converted. This is important as most of the types going to be exchanged in a typical application will likely be builtin types. As an example, a +String+ object created in Ruby can have its memory address passed as the argument of an Objective-C method that expects an +NSString+.
+
+h4. Mutability
+
+The Foundation framework ships a set of classes that have both mutable and immutable variants. Mutable objects can be modified, while immutable objects can't.
+
+Immutable types in RubyMotion behave like frozen objects. As in traditional Ruby, the +freeze+ message can be sent on collection types to prevent them from being modified later on.
+
+As an example, changing the content of an +NSString+ is prohibited, and an exception will be raised by the system if doing so. However, it is possible to change the content of an +NSMutableString+ instance.
+
++NSString.new.strip! # raises RuntimeError: can't modify frozen/immutable string +NSMutableString.new.strip! # works ++ +Strings created in RubyMotion inherit from +NSMutableString+, so they can be modified. The same goes for arrays and hashes. + +However, the developer must be careful that it is very common in iOS SDK APIs to return immutable types. In these cases, the +dup+ or +mutableCopy+ messages can be sent to the object in order to get a mutable version of it, that can be modified. + +h3. Memory Management + +RubyMotion provides automatic memory management; the developer does not need to reclaim unused objects. + +Since memory is limited on iOS hardware, the developer must be careful about not creating large object graphs. + +RubyMotion implements a form of garbage collection called reference counting. An object has an initial reference count of zero, is incremented when a reference to it is created, and decremented when a reference is destroyed. When the count reaches zero, the object's memory is reclaimed by the collector. + +Object cycles, when two or more objects refer to each other, are handled by the garbage collector. + +RubyMotion's memory management system is designed to simplify the development process. Unlike traditional Objective-C programming, object references are automatically created and destroyed by the system. Similarly, because object cycles will be garbage-collected, the developer does not need to keep in mind the entire object graph of his project and use weak references. + +h4. Objective-C Objects + +Objects created by Objective-C APIs are automatically managed by RubyMotion. There is no need to send the +retain+, +release+ or +autorelease+ messages to them. + +
+def dates + puts NSDate.alloc.init + puts NSDate.date +end + +dates # both NSDate objects created above will be garbage-collected. ++ +h4. CoreFoundation Objects + +Objects created by CoreFoundation-style APIs must be explicitely destroyed by the developer, by calling the +CFRelease()+ function. RubyMotion will not garbage-collect them otherwise. + +
+attributed_string = CFAttributedStringCreate(nil, 'Hello World', {})
+do_something(attributed_string)
+
+# At this point, the CFAttributedString object is leaking, we need to manually destroy it.
+CFRelease(attributed_string)
+attributed_string = nil
+
+
+h3. Concurrency
+
+The ability to run code concurrently became critical as multicore processors appeared on iOS devices. RubyMotion has been designed around this purpose.
+
+RubyMotion has the concept of virtual machine objects, which wrap the state of a thread of execution. A piece of code is running through a virtual machine.
+
+Virtual machines don't have locks, and there can be multiple virtual machines running at the same time, concurrently.
+
+Unlike some Ruby implementations, race conditions are possible in RubyMotion, since there is no Global Interpreter Lock (GIL) to prohibit threads from running concurrently. The developer must be careful to secure concurrent access to shared resources.
+
+Different options are available to write concurrent code in RubyMotion.
+
+h4. Threads and Mutexes
+
+The +Thread+ class spawns a new POSIX thread that will run concurrently with other threads. The +Mutex+ class wraps a POSIX mutex, and can be used to isolate concurrent access to a shared resource.
+
+h4. Grand Central Dispatch
+
+RubyMotion wraps the "Grand Central Dispatch":http://developer.apple.com/library/ios/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html (GCD) concurrency library under the +Dispatch+ module. It is possible to execute both synchronously and asynchronously blocks of code under concurrent or serial queues.
+
+Albeit more complicated to comprehend than regular threads, sometimes GCD offers a more elegant way to run code concurrently. GCD maintains for the developer a pool of threads and its APIs are architectured to avoid the need to use mutexes.
+
+== h2. Using the iOS SDK==
+== h3. Objective-C APIs==
+== h3. C APIs==
+== h3. Pointers==
+== h3. Blocks==
+
+h2. Project Management
+
+h3. Creation
+
+RubyMotion projects are created by passing the +create+ command to +/usr/bin/motion+. RubyMotion projects are directories.
+
++$ motion create Hello +$ cd Hello +$ ls +Rakefile app resources ++ +The following table explains the anatomy of a project directory. + +|_<. File/Directory |_<. Purpose | +|+Rakefile+ | This file contains the configuration of the project, as well as a default set of tasks. It can be edited to change the configuration or add new tasks. | +| +app/+ | This directory contains the Ruby code of the project. In a new project, a +main.rb+ file is created automatically. | +| +resources/+ | This directory contains the resources files of the project, such as images or sounds. In a new project, this directory is empty. | + +h3. Configuration + +The +rake config+ task will dump the project configuration. Each configuration variable has a sensible default value that can be manually overriden in the +Rakefile+ file. + +|_<. Variable |_<. Discussion | +|+name+ | Project name, as a +String+. The default value is the name passed to +motion create+. | +|+version+ | Project version, as a +String+. The default value is +'1.0'+. | +|+delegate_class+ | Name of the application delegate class, as a +String+. The default value is +'AppDelegate'+ and the class is defined in +app/main.rb+.| +|+files+ | Project files, as an +Array+. The default value is the result of the following expression: +Dir.glob('./app/**/*.rb')+ (every +.rb+ file in the +app+ directory). | +|+frameworks+ | iOS frameworks to link against, as an +Array+. The default value is +['UIKit', 'Foundation', 'CoreGraphics']+. | +|+build_dir+ | Directory for build products, as a +String+. The directory will be created by the build system if it does not exist yet. The default value is +'build'+.| +|+resources_dir+ | Directory for resources files, as a +String+. The default value is +'resources'+. | +|+icons+ | List of resource files to use for icons, as an +Array+. The files must conform to the "HIG guidelines":http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/IconsImages/IconsImages.html. The default value is +[]+, an empty array. | +|+device_family+ | Family of devices to support. Possible values can be: +iphone+, +ipad+ or +[:iphone, :ipad]+ (for a universal application). The default value is +:iphone+.| +|+interface_orientations+ | Supported interface orientations. Value must be an +Array+ of one or more of the following symbols: +:portrait+, +:landscape_left+, :+landscape_right+:, and +:portrait_upside_down+. The default value is +[:portrait, :landscape_left, :landscape_right]+. | +|+platforms_dir+ | Platforms directory where to find SDKs, as a +String+. The default value is +'/Developer/Platforms'+. | +|+sdk_version+ | Version number of SDK to target, as a +String+. The default value is the version number of the most recent SDK in +platforms_dir+. Example: +'5.0'+| +|+codesign_certificate+ | The name of the certificate to use for codesigning, as a +String+. The default value is the first iPhone Developer certificate found in keychain. Example: +'iPhone Developer: Darth Vader (A3LKZY369Q)'+.| +|+provisioning_profile+ | Path to the provisioning profile to use for deployment, as a +String+. The default value is the first +.mobileprovision+ file found in +~/Library/MobileDevice/Provisioning+. | + +Custom values for the configuration settings can be added by tweaking the +Motion::App.setup+ block in the +Rakefile+ file. + +As an example, let's take the configuration block of a fictional video player application for the iPad. The device family setting has to change from its default value, iPhone, to iPad. Also, the application makes use of an additional framework, AVFoundation, for audio-video functionality. + +
+Motion::App.setup do |app| + app.name = 'Awesome Video Player' + app.device_family = :ipad + app.frameworks << 'AVFoundation' +end ++ +h3. Build + +The +rake build+ task builds the project into the temporary +build+ directory. Two different versions of the project will be built, one to run in the iOS simulator (on the Mac itself) and one to run on the iOS device. + +The following steps are performed: + +# It compiles each Ruby source code file into optimized machine code, translating the Ruby syntax tree into an intermediate representation language (using "LLVM":http://llvm.org/), then assembly. The compiler will generate code for either the Intel 32-bit (+i386+) or ARM (+armv6+, +armv7+) instruction sets and ABIs depending on the target. +# It links the machine code with the RubyMotion runtime statically to form an executable. The linker also includes metadata for the C APIs that your project uses. +# It creates an +.app+ bundle and copies the executable there. The +Info.plist+ file is generated based on the project configuration. Each resource file in the +resources+ directory is copied in the bundle. +# It codesigns the bundle based on the certificate and provisioning profile specified in the project configuration. + +Normally the user does not need to explicitly build the project, as the +build+ task is a dependency of the other tasks. + +h3. Simulation + +The +rake simulator+ task builds the project for the iOS simulator, and runs the application in the simulator. + +The default +rake+ task is a shortcut to +rake simulator+. + +h3. Archiving + +The +rake archive+ task builds the project for the iOS device, and generates in the +build+ directory an +.ipa+ archive suitable for ad-hoc distribution or submissions to the App Store. + +
+$ rake archive +$ file build/Hello.ipa +build/Hello.ipa: Zip archive data, at least v1.0 to extract ++ +h3. Deployment + +The +rake deploy+ task uploads an archive version of the application to an iOS device. + +There must be one iOS device connected via USB to the Mac. The deployment task will attempt to upload the application to the first discovered iOS device on the USB channel. + +The process will fail in the following cases: +* No iOS device was found on USB. +* The project builds on a version of iOS greater than the version of iOS running on the device. +* The project doesn't use the appropriate certificate and provisioning profile linked to the device. +* There is a USB connection issue when talking to the device. + +Otherwise, the process returns successfully and the application is then available on the device springboard. + +h2. Library + +TBD. diff --git a/doc/redcloth.rb b/doc/redcloth.rb new file mode 100644 index 00000000..cdc16759 --- /dev/null +++ b/doc/redcloth.rb @@ -0,0 +1,1131 @@ +# vim:ts=4:sw=4: +# = RedCloth - Textile and Markdown Hybrid for Ruby +# +# Homepage:: http://whytheluckystiff.net/ruby/redcloth/ +# Author:: why the lucky stiff (http://whytheluckystiff.net/) +# Copyright:: (cc) 2004 why the lucky stiff (and his puppet organizations.) +# License:: BSD +# +# (see http://hobix.com/textile/ for a Textile Reference.) +# +# Based on (and also inspired by) both: +# +# PyTextile: http://diveintomark.org/projects/textile/textile.py.txt +# Textism for PHP: http://www.textism.com/tools/textile/ +# +# + +# = RedCloth +# +# RedCloth is a Ruby library for converting Textile and/or Markdown +# into HTML. You can use either format, intermingled or separately. +# You can also extend RedCloth to honor your own custom text stylings. +# +# RedCloth users are encouraged to use Textile if they are generating +# HTML and to use Markdown if others will be viewing the plain text. +# +# == What is Textile? +# +# Textile is a simple formatting style for text +# documents, loosely based on some HTML conventions. +# +# == Sample Textile Text +# +# h2. This is a title +# +# h3. This is a subhead +# +# This is a bit of paragraph. +# +# bq. This is a blockquote. +# +# = Writing Textile +# +# A Textile document consists of paragraphs. Paragraphs +# can be specially formatted by adding a small instruction +# to the beginning of the paragraph. +# +# h[n]. Header of size [n]. +# bq. Blockquote. +# # Numeric list. +# * Bulleted list. +# +# == Quick Phrase Modifiers +# +# Quick phrase modifiers are also included, to allow formatting +# of small portions of text within a paragraph. +# +# \_emphasis\_ +# \_\_italicized\_\_ +# \*strong\* +# \*\*bold\*\* +# ??citation?? +# -deleted text- +# +inserted text+ +# ^superscript^ +# ~subscript~ +# @code@ +# %(classname)span% +# +# ==notextile== (leave text alone) +# +# == Links +# +# To make a hypertext link, put the link text in "quotation +# marks" followed immediately by a colon and the URL of the link. +# +# Optional: text in (parentheses) following the link text, +# but before the closing quotation mark, will become a Title +# attribute for the link, visible as a tool tip when a cursor is above it. +# +# Example: +# +# "This is a link (This is a title) ":http://www.textism.com +# +# Will become: +# +# This is a link +# +# == Images +# +# To insert an image, put the URL for the image inside exclamation marks. +# +# Optional: text that immediately follows the URL in (parentheses) will +# be used as the Alt text for the image. Images on the web should always +# have descriptive Alt text for the benefit of readers using non-graphical +# browsers. +# +# Optional: place a colon followed by a URL immediately after the +# closing ! to make the image into a link. +# +# Example: +# +# !http://www.textism.com/common/textist.gif(Textist)! +# +# Will become: +# +#
+#
+# With a link:
+#
+# !/common/textist.gif(Textist)!:http://textism.com
+#
+# Will become:
+#
+#
+#
+# == Defining Acronyms
+#
+# HTML allows authors to define acronyms via the tag. The definition appears as a
+# tool tip when a cursor hovers over the acronym. A crucial aid to clear writing,
+# this should be used at least once for each acronym in documents where they appear.
+#
+# To quickly define an acronym in Textile, place the full text in (parentheses)
+# immediately following the acronym.
+#
+# Example:
+#
+# ACLU(American Civil Liberties Union)
+#
+# Will become:
+#
+# ACLU
+#
+# == Adding Tables
+#
+# In Textile, simple tables can be added by seperating each column by
+# a pipe.
+#
+# |a|simple|table|row|
+# |And|Another|table|row|
+#
+# Attributes are defined by style definitions in parentheses.
+#
+# table(border:1px solid black).
+# (background:#ddd;color:red). |{}| | | |
+#
+# == Using RedCloth
+#
+# RedCloth is simply an extension of the String class, which can handle
+# Textile formatting. Use it like a String and output HTML with its
+# RedCloth#to_html method.
+#
+# doc = RedCloth.new "
+#
+# h2. Test document
+#
+# Just a simple test."
+#
+# puts doc.to_html
+#
+# By default, RedCloth uses both Textile and Markdown formatting, with
+# Textile formatting taking precedence. If you want to turn off Markdown
+# formatting, to boost speed and limit the processor:
+#
+# class RedCloth::Textile.new( str )
+
+class RedCloth < String
+
+ VERSION = '3.0.4'
+ DEFAULT_RULES = [:textile, :markdown]
+
+ #
+ # Two accessor for setting security restrictions.
+ #
+ # This is a nice thing if you're using RedCloth for
+ # formatting in public places (e.g. Wikis) where you
+ # don't want users to abuse HTML for bad things.
+ #
+ # If +:filter_html+ is set, HTML which wasn't
+ # created by the Textile processor will be escaped.
+ #
+ # If +:filter_styles+ is set, it will also disable
+ # the style markup specifier. ('{color: red}')
+ #
+ attr_accessor :filter_html, :filter_styles
+
+ #
+ # Accessor for toggling hard breaks.
+ #
+ # If +:hard_breaks+ is set, single newlines will
+ # be converted to HTML break tags. This is the
+ # default behavior for traditional RedCloth.
+ #
+ attr_accessor :hard_breaks
+
+ # Accessor for toggling lite mode.
+ #
+ # In lite mode, block-level rules are ignored. This means
+ # that tables, paragraphs, lists, and such aren't available.
+ # Only the inline markup for bold, italics, entities and so on.
+ #
+ # r = RedCloth.new( "And then? She *fell*!", [:lite_mode] )
+ # r.to_html
+ # #=> "And then? She fell!"
+ #
+ attr_accessor :lite_mode
+
+ #
+ # Accessor for toggling span caps.
+ #
+ # Textile places `span' tags around capitalized
+ # words by default, but this wreaks havoc on Wikis.
+ # If +:no_span_caps+ is set, this will be
+ # suppressed.
+ #
+ attr_accessor :no_span_caps
+
+ #
+ # Establishes the markup predence. Available rules include:
+ #
+ # == Textile Rules
+ #
+ # The following textile rules can be set individually. Or add the complete
+ # set of rules with the single :textile rule, which supplies the rule set in
+ # the following precedence:
+ #
+ # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/)
+ # block_textile_table:: Textile table block structures
+ # block_textile_lists:: Textile list structures
+ # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.)
+ # inline_textile_image:: Textile inline images
+ # inline_textile_link:: Textile inline links
+ # inline_textile_span:: Textile inline spans
+ # glyphs_textile:: Textile entities (such as em-dashes and smart quotes)
+ #
+ # == Markdown
+ #
+ # refs_markdown:: Markdown references (for example: [hobix]: http://hobix.com/)
+ # block_markdown_setext:: Markdown setext headers
+ # block_markdown_atx:: Markdown atx headers
+ # block_markdown_rule:: Markdown horizontal rules
+ # block_markdown_bq:: Markdown blockquotes
+ # block_markdown_lists:: Markdown lists
+ # inline_markdown_link:: Markdown links
+ attr_accessor :rules
+
+ # Returns a new RedCloth object, based on _string_ and
+ # enforcing all the included _restrictions_.
+ #
+ # r = RedCloth.new( "h1. A bold man", [:filter_html] )
+ # r.to_html
+ # #=>"#{ code }#{ after }" )
+ end
+ end
+
+ def lT( text )
+ text =~ /\#$/ ? 'o' : 'u'
+ end
+
+ def hard_break( text )
+ text.gsub!( /(.)\n(?!\Z| *([#*=]+(\s|$)|[{|]))/, "\\1#{ blk }"
+ else
+ blk = "\t#{ blk }
" + end + end + # hard_break blk + blk + "\n#{ code_blk }" + end + end + + end.join( "\n\n" ) ) + end + + def textile_bq( tag, atts, cite, content ) + cite, cite_title = check_refs( cite ) + cite = " cite=\"#{ cite }\"" if cite + atts = shelve( atts ) if atts + "\t\n\t\t" + end + + def textile_p( tag, atts, cite, content ) + atts = shelve( atts ) if atts + "\t<#{ tag }#{ atts }>#{ content }#{ tag }>" + end + + alias textile_h1 textile_p + alias textile_h2 textile_p + alias textile_h3 textile_p + alias textile_h4 textile_p + alias textile_h5 textile_p + alias textile_h6 textile_p + + def textile_fn_( tag, num, atts, cite, content ) + atts << " id=\"fn#{ num }\"" + content = "#{ num } #{ content }" + atts = shelve( atts ) if atts + "\t#{ content }
\n\t
#{ content }
" + end + + BLOCK_RE = /^(([a-z]+)(\d*))(#{A}#{C})\.(?::(\S+))? (.*)$/m + + def block_textile_prefix( text ) + if text =~ BLOCK_RE + tag,tagpre,num,atts,cite,content = $~[1..6] + atts = pba( atts ) + + # pass to prefix handler + if respond_to? "textile_#{ tag }", true + text.gsub!( $&, method( "textile_#{ tag }" ).call( tag, atts, cite, content ) ) + elsif respond_to? "textile_#{ tagpre }_", true + text.gsub!( $&, method( "textile_#{ tagpre }_" ).call( tagpre, num, atts, cite, content ) ) + end + end + end + + SETEXT_RE = /\A(.+?)\n([=-])[=-]* *$/m + def block_markdown_setext( text ) + if text =~ SETEXT_RE + tag = if $2 == "="; "h1"; else; "h2"; end + blk, cont = "<#{ tag }>#{ $1 }#{ tag }>", $' + blocks cont + text.replace( blk + cont ) + end + end + + ATX_RE = /\A(\#{1,6}) # $1 = string of #'s + [ ]* + (.+?) # $2 = Header text + [ ]* + \#* # optional closing #'s (not counted) + $/x + def block_markdown_atx( text ) + if text =~ ATX_RE + tag = "h#{ $1.length }" + blk, cont = "<#{ tag }>#{ $2 }#{ tag }>\n\n", $' + blocks cont + text.replace( blk + cont ) + end + end + + MARKDOWN_BQ_RE = /\A(^ *> ?.+$(.+\n)*\n*)+/m + + def block_markdown_bq( text ) + text.gsub!( MARKDOWN_BQ_RE ) do |blk| + blk.gsub!( /^ *> ?/, '' ) + flush_left blk + blocks blk + blk.gsub!( /^(\S)/, "\t\\1" ) + "\n#{ blk }\n\n\n" + end + end + + MARKDOWN_RULE_RE = /^(#{ + ['*', '-', '_'].collect { |ch| '( ?' + Regexp::quote( ch ) + ' ?){3,}' }.join( '|' ) + })$/ + + def block_markdown_rule( text ) + text.gsub!( MARKDOWN_RULE_RE ) do |blk| + "
|.|^) # start of line?
+ \! # opening
+ (\<|\=|\>)? # optional alignment atts
+ (#{C}) # optional style,class atts
+ (?:\. )? # optional dot-space
+ ([^\s(!]+?) # presume this is the src
+ \s? # optional space
+ (?:\(((?:[^\(\)]|\([^\)]+\))+?)\))? # optional title
+ \! # closing
+ (?::#{ HYPERLINK })? # optional href
+ /x
+
+ def inline_textile_image( text )
+ text.gsub!( IMAGE_RE ) do |m|
+ stln,algn,atts,url,title,href,href_a1,href_a2 = $~[1..8]
+ atts = pba( atts )
+ atts = " src=\"#{ url }\"#{ atts }"
+ atts << " title=\"#{ title }\"" if title
+ atts << " alt=\"#{ title }\""
+ # size = @getimagesize($url);
+ # if($size) $atts.= " $size[3]";
+
+ href, alt_title = check_refs( href ) if href
+ url, url_title = check_refs( url )
+
+ out = ''
+ out << "" if href
+ out << ""
+ out << "#{ href_a1 }#{ href_a2 }" if href
+
+ if algn
+ algn = h_align( algn )
+ if stln == "
" + out = "
#{ out }" + else + out = "#{ stln }
, etc.
+ if $1
+ if line =~ OFFTAG_OPEN
+ codepre += 1
+ elsif line =~ OFFTAG_CLOSE
+ codepre -= 1
+ codepre = 0 if codepre < 0
+ end
+ elsif codepre.zero?
+ glyphs_textile( line, level + 1 )
+ else
+ htmlesc( line, :NoQuotes )
+ end
+ # p [level, codepre, line]
+
+ line
+ end
+ end
+ end
+
+ def rip_offtags( text )
+ if text =~ /<.*>/
+ ## strip and encode content
+ codepre, used_offtags = 0, {}
+ text.gsub!( OFFTAG_MATCH ) do |line|
+ if $3
+ offtag, aftertag = $4, $5
+ codepre += 1
+ used_offtags[offtag] = true
+ if codepre - used_offtags.length > 0
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
+ @pre_list.last << line
+ line = ""
+ else
+ htmlesc( aftertag, :NoQuotes ) if aftertag and not used_offtags['notextile']
+ line = ""
+ @pre_list << "#{ $3 }#{ aftertag }"
+ end
+ elsif $1 and codepre > 0
+ if codepre - used_offtags.length > 0
+ htmlesc( line, :NoQuotes ) unless used_offtags['notextile']
+ @pre_list.last << line
+ line = ""
+ end
+ codepre -= 1 unless codepre.zero?
+ used_offtags = {} if codepre.zero?
+ end
+ line
+ end
+ end
+ text
+ end
+
+ def smooth_offtags( text )
+ unless @pre_list.empty?
+ ## replace content
+ text.gsub!( // ) { @pre_list[$1.to_i] }
+ end
+ end
+
+ def inline( text )
+ [/^inline_/, /^glyphs_/].each do |meth_re|
+ @rules.each do |rule_name|
+ method( rule_name ).call( text ) if rule_name.to_s.match( meth_re )
+ end
+ end
+ end
+
+ def h_align( text )
+ H_ALGN_VALS[text]
+ end
+
+ def v_align( text )
+ V_ALGN_VALS[text]
+ end
+
+ def textile_popup_help( name, windowW, windowH )
+ ' ' + name + '
'
+ end
+
+ # HTML cleansing stuff
+ BASIC_TAGS = {
+ 'a' => ['href', 'title'],
+ 'img' => ['src', 'alt', 'title'],
+ 'br' => [],
+ 'i' => nil,
+ 'u' => nil,
+ 'b' => nil,
+ 'pre' => nil,
+ 'kbd' => nil,
+ 'code' => ['lang'],
+ 'cite' => nil,
+ 'strong' => nil,
+ 'em' => nil,
+ 'ins' => nil,
+ 'sup' => nil,
+ 'sub' => nil,
+ 'del' => nil,
+ 'table' => nil,
+ 'tr' => nil,
+ 'td' => ['colspan', 'rowspan'],
+ 'th' => nil,
+ 'ol' => nil,
+ 'ul' => nil,
+ 'li' => nil,
+ 'p' => nil,
+ 'h1' => nil,
+ 'h2' => nil,
+ 'h3' => nil,
+ 'h4' => nil,
+ 'h5' => nil,
+ 'h6' => nil,
+ 'blockquote' => ['cite']
+ }
+
+ def clean_html( text, tags = BASIC_TAGS )
+ text.gsub!( /]*)>/ ) do
+ raw = $~
+ tag = raw[2].downcase
+ if tags.has_key? tag
+ pcs = [tag]
+ tags[tag].each do |prop|
+ ['"', "'", ''].each do |q|
+ q2 = ( q != '' ? q : '\s' )
+ if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
+ attrv = $1
+ next if prop == 'src' and attrv =~ %r{^(?!http)\w+:}
+ pcs << "#{prop}=\"#{$1.gsub('"', '\\"')}\""
+ break
+ end
+ end
+ end if tags[tag]
+ "<#{raw[1]}#{pcs.join " "}>"
+ else
+ " "
+ end
+ end
+ end
+end
+
+