typo 3.99.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (742) hide show
  1. data/.DS_Store +0 -0
  2. data/CHANGELOG +718 -0
  3. data/MAINTAINERS +30 -0
  4. data/MIT-LICENSE +21 -0
  5. data/README +92 -0
  6. data/Rakefile +10 -0
  7. data/app/apis/blogger_service.rb +94 -0
  8. data/app/apis/meta_weblog_service.rb +197 -0
  9. data/app/apis/movable_type_service.rb +150 -0
  10. data/app/apis/typo_web_service.rb +26 -0
  11. data/app/controllers/accounts_controller.rb +52 -0
  12. data/app/controllers/admin/base_controller.rb +21 -0
  13. data/app/controllers/admin/blacklist_controller.rb +50 -0
  14. data/app/controllers/admin/cache_controller.rb +31 -0
  15. data/app/controllers/admin/categories_controller.rb +64 -0
  16. data/app/controllers/admin/comments_controller.rb +57 -0
  17. data/app/controllers/admin/content_controller.rb +147 -0
  18. data/app/controllers/admin/general_controller.rb +42 -0
  19. data/app/controllers/admin/pages_controller.rb +50 -0
  20. data/app/controllers/admin/resources_controller.rb +98 -0
  21. data/app/controllers/admin/sidebar_controller.rb +74 -0
  22. data/app/controllers/admin/textfilters_controller.rb +104 -0
  23. data/app/controllers/admin/themes_controller.rb +20 -0
  24. data/app/controllers/admin/trackbacks_controller.rb +54 -0
  25. data/app/controllers/admin/users_controller.rb +41 -0
  26. data/app/controllers/application.rb +66 -0
  27. data/app/controllers/articles_controller.rb +218 -0
  28. data/app/controllers/backend_controller.rb +11 -0
  29. data/app/controllers/content_controller.rb +98 -0
  30. data/app/controllers/live_controller.rb +10 -0
  31. data/app/controllers/redirect_controller.rb +16 -0
  32. data/app/controllers/sidebar_controller.rb +50 -0
  33. data/app/controllers/textfilter_controller.rb +22 -0
  34. data/app/controllers/theme_controller.rb +52 -0
  35. data/app/controllers/xml_controller.rb +111 -0
  36. data/app/helpers/accounts_helper.rb +2 -0
  37. data/app/helpers/admin/base_helper.rb +124 -0
  38. data/app/helpers/admin/blacklist_helper.rb +2 -0
  39. data/app/helpers/admin/cache_helper.rb +2 -0
  40. data/app/helpers/admin/comments_helper.rb +2 -0
  41. data/app/helpers/admin/content_helper.rb +2 -0
  42. data/app/helpers/admin/general_helper.rb +2 -0
  43. data/app/helpers/admin/pages_helper.rb +2 -0
  44. data/app/helpers/admin/resources_helper.rb +2 -0
  45. data/app/helpers/admin/sidebar_helper.rb +2 -0
  46. data/app/helpers/admin/textfilters_helper.rb +2 -0
  47. data/app/helpers/admin/themes_helper.rb +2 -0
  48. data/app/helpers/admin/trackbacks_helper.rb +2 -0
  49. data/app/helpers/admin/users_helper.rb +2 -0
  50. data/app/helpers/application_helper.rb +92 -0
  51. data/app/helpers/articles_helper.rb +163 -0
  52. data/app/helpers/backend_helper.rb +2 -0
  53. data/app/helpers/content_helper.rb +3 -0
  54. data/app/helpers/mail_helper.rb +6 -0
  55. data/app/helpers/redirect_helper.rb +2 -0
  56. data/app/helpers/sidebar_helper.rb +22 -0
  57. data/app/helpers/sidebars/plugin_helper.rb +2 -0
  58. data/app/helpers/text_filter_plugin_helper.rb +2 -0
  59. data/app/helpers/textfilter_helper.rb +2 -0
  60. data/app/helpers/theme_helper.rb +23 -0
  61. data/app/helpers/xml_helper.rb +21 -0
  62. data/app/models/aggregations/audioscrobbler.rb +54 -0
  63. data/app/models/aggregations/backpack.rb +57 -0
  64. data/app/models/aggregations/delicious.rb +57 -0
  65. data/app/models/aggregations/flickr.rb +89 -0
  66. data/app/models/aggregations/fortythree.rb +62 -0
  67. data/app/models/aggregations/magnolia.rb +81 -0
  68. data/app/models/aggregations/tada.rb +75 -0
  69. data/app/models/aggregations/technorati.rb +51 -0
  70. data/app/models/aggregations/upcoming.rb +74 -0
  71. data/app/models/article.rb +211 -0
  72. data/app/models/blacklist_pattern.rb +22 -0
  73. data/app/models/blog.rb +162 -0
  74. data/app/models/blog_sweeper.rb +23 -0
  75. data/app/models/category.rb +62 -0
  76. data/app/models/comment.rb +76 -0
  77. data/app/models/config_manager.rb +80 -0
  78. data/app/models/content.rb +222 -0
  79. data/app/models/content_observer.rb +5 -0
  80. data/app/models/content_state/base.rb +51 -0
  81. data/app/models/content_state/draft.rb +40 -0
  82. data/app/models/content_state/factory.rb +16 -0
  83. data/app/models/content_state/just_published.rb +47 -0
  84. data/app/models/content_state/new.rb +45 -0
  85. data/app/models/content_state/publication_pending.rb +43 -0
  86. data/app/models/content_state/published.rb +35 -0
  87. data/app/models/email_notifier.rb +9 -0
  88. data/app/models/notification_mailer.rb +34 -0
  89. data/app/models/page.rb +20 -0
  90. data/app/models/page_cache.rb +29 -0
  91. data/app/models/ping.rb +129 -0
  92. data/app/models/redirect.rb +3 -0
  93. data/app/models/resource.rb +76 -0
  94. data/app/models/sidebar.rb +32 -0
  95. data/app/models/simple_cache.rb +37 -0
  96. data/app/models/tag.rb +63 -0
  97. data/app/models/text_filter.rb +111 -0
  98. data/app/models/theme.rb +44 -0
  99. data/app/models/trackback.rb +51 -0
  100. data/app/models/trigger.rb +31 -0
  101. data/app/models/user.rb +113 -0
  102. data/app/models/web_notifier.rb +7 -0
  103. data/app/views/accounts/login.rhtml +24 -0
  104. data/app/views/accounts/logout.rhtml +12 -0
  105. data/app/views/accounts/signup.rhtml +26 -0
  106. data/app/views/accounts/welcome.rhtml +13 -0
  107. data/app/views/admin/base/_recent_comments.rhtml +10 -0
  108. data/app/views/admin/base/_recent_trackbacks.rhtml +10 -0
  109. data/app/views/admin/blacklist/_blacklist_patterns.rhtml +16 -0
  110. data/app/views/admin/blacklist/_form.rhtml +15 -0
  111. data/app/views/admin/blacklist/_quick_post.rhtml +16 -0
  112. data/app/views/admin/blacklist/destroy.rhtml +10 -0
  113. data/app/views/admin/blacklist/edit.rhtml +14 -0
  114. data/app/views/admin/blacklist/list.rhtml +13 -0
  115. data/app/views/admin/cache/list.rhtml +8 -0
  116. data/app/views/admin/categories/_categories.rhtml +14 -0
  117. data/app/views/admin/categories/_form.rhtml +7 -0
  118. data/app/views/admin/categories/_quick_post.rhtml +11 -0
  119. data/app/views/admin/categories/destroy.rhtml +10 -0
  120. data/app/views/admin/categories/edit.rhtml +13 -0
  121. data/app/views/admin/categories/list.rhtml +15 -0
  122. data/app/views/admin/categories/reorder.rhtml +9 -0
  123. data/app/views/admin/categories/show.rhtml +18 -0
  124. data/app/views/admin/comments/_form.rhtml +19 -0
  125. data/app/views/admin/comments/comments.rhtml +4 -0
  126. data/app/views/admin/comments/destroy.rhtml +11 -0
  127. data/app/views/admin/comments/edit.rhtml +18 -0
  128. data/app/views/admin/comments/list.rhtml +31 -0
  129. data/app/views/admin/comments/new.rhtml +15 -0
  130. data/app/views/admin/comments/show.rhtml +10 -0
  131. data/app/views/admin/content/_articles.rhtml +20 -0
  132. data/app/views/admin/content/_attachment.rhtml +17 -0
  133. data/app/views/admin/content/_form.rhtml +51 -0
  134. data/app/views/admin/content/_pages.rhtml +3 -0
  135. data/app/views/admin/content/_quick_post.rhtml +20 -0
  136. data/app/views/admin/content/_show_categories.rhtml +9 -0
  137. data/app/views/admin/content/_show_resources.rhtml +9 -0
  138. data/app/views/admin/content/destroy.rhtml +11 -0
  139. data/app/views/admin/content/edit.rhtml +28 -0
  140. data/app/views/admin/content/list.rhtml +20 -0
  141. data/app/views/admin/content/new.rhtml +27 -0
  142. data/app/views/admin/content/preview.rhtml +3 -0
  143. data/app/views/admin/content/show.rhtml +25 -0
  144. data/app/views/admin/general/index.rhtml +216 -0
  145. data/app/views/admin/general/update_database.rhtml +44 -0
  146. data/app/views/admin/pages/_form.rhtml +19 -0
  147. data/app/views/admin/pages/_pages.rhtml +18 -0
  148. data/app/views/admin/pages/_quick_post.rhtml +21 -0
  149. data/app/views/admin/pages/destroy.rhtml +11 -0
  150. data/app/views/admin/pages/edit.rhtml +28 -0
  151. data/app/views/admin/pages/list.rhtml +13 -0
  152. data/app/views/admin/pages/new.rhtml +24 -0
  153. data/app/views/admin/pages/preview.rhtml +1 -0
  154. data/app/views/admin/pages/show.rhtml +14 -0
  155. data/app/views/admin/resources/_itunes_category_add.rhtml +11 -0
  156. data/app/views/admin/resources/_itunes_category_edit.rhtml +32 -0
  157. data/app/views/admin/resources/_metadata_add.rhtml +22 -0
  158. data/app/views/admin/resources/_metadata_edit.rhtml +22 -0
  159. data/app/views/admin/resources/_mime_edit.rhtml +8 -0
  160. data/app/views/admin/resources/_pages.rhtml +3 -0
  161. data/app/views/admin/resources/_resources.rhtml +40 -0
  162. data/app/views/admin/resources/destroy.rhtml +11 -0
  163. data/app/views/admin/resources/list.rhtml +9 -0
  164. data/app/views/admin/resources/new.rhtml +12 -0
  165. data/app/views/admin/sidebar/_active.rhtml +14 -0
  166. data/app/views/admin/sidebar/_actives.rhtml +9 -0
  167. data/app/views/admin/sidebar/_available.rhtml +5 -0
  168. data/app/views/admin/sidebar/_availables.rhtml +5 -0
  169. data/app/views/admin/sidebar/_publish.rhtml +2 -0
  170. data/app/views/admin/sidebar/_target.rhtml +1 -0
  171. data/app/views/admin/sidebar/index.rhtml +34 -0
  172. data/app/views/admin/sidebar/publish.rjs +6 -0
  173. data/app/views/admin/sidebar/remove.rjs +5 -0
  174. data/app/views/admin/sidebar/set_active.rjs +10 -0
  175. data/app/views/admin/textfilters/_form.rhtml +45 -0
  176. data/app/views/admin/textfilters/_macros.rhtml +16 -0
  177. data/app/views/admin/textfilters/_textfilters.rhtml +20 -0
  178. data/app/views/admin/textfilters/destroy.rhtml +10 -0
  179. data/app/views/admin/textfilters/edit.rhtml +17 -0
  180. data/app/views/admin/textfilters/list.rhtml +15 -0
  181. data/app/views/admin/textfilters/macro_help.rhtml +3 -0
  182. data/app/views/admin/textfilters/new.rhtml +15 -0
  183. data/app/views/admin/textfilters/preview.rhtml +3 -0
  184. data/app/views/admin/textfilters/show.rhtml +34 -0
  185. data/app/views/admin/textfilters/show_help.rhtml +8 -0
  186. data/app/views/admin/themes/index.rhtml +12 -0
  187. data/app/views/admin/trackbacks/_form.rhtml +19 -0
  188. data/app/views/admin/trackbacks/destroy.rhtml +6 -0
  189. data/app/views/admin/trackbacks/edit.rhtml +14 -0
  190. data/app/views/admin/trackbacks/list.rhtml +31 -0
  191. data/app/views/admin/trackbacks/new.rhtml +15 -0
  192. data/app/views/admin/trackbacks/show.rhtml +14 -0
  193. data/app/views/admin/users/_form.rhtml +48 -0
  194. data/app/views/admin/users/_user.rhtml +9 -0
  195. data/app/views/admin/users/destroy.rhtml +14 -0
  196. data/app/views/admin/users/edit.rhtml +14 -0
  197. data/app/views/admin/users/list.rhtml +9 -0
  198. data/app/views/admin/users/new.rhtml +15 -0
  199. data/app/views/admin/users/show.rhtml +18 -0
  200. data/app/views/articles/_article.rhtml +4 -0
  201. data/app/views/articles/_comment.rhtml +7 -0
  202. data/app/views/articles/_comment_box.rhtml +43 -0
  203. data/app/views/articles/_comment_error.rhtml +1 -0
  204. data/app/views/articles/_trackback.rhtml +7 -0
  205. data/app/views/articles/archives.rhtml +22 -0
  206. data/app/views/articles/comment_preview.rhtml +3 -0
  207. data/app/views/articles/error.rhtml +3 -0
  208. data/app/views/articles/groupings.rhtml +14 -0
  209. data/app/views/articles/index.rhtml +11 -0
  210. data/app/views/articles/read.rhtml +70 -0
  211. data/app/views/articles/trackback.rxml +5 -0
  212. data/app/views/articles/view_page.rhtml +3 -0
  213. data/app/views/layouts/accounts.rhtml +22 -0
  214. data/app/views/layouts/administration.rhtml +75 -0
  215. data/app/views/layouts/sidebar.rhtml +40 -0
  216. data/app/views/live/search.rhtml +10 -0
  217. data/app/views/notification_mailer/_mail_footer.rhtml +7 -0
  218. data/app/views/notification_mailer/_mail_header.rhtml +1 -0
  219. data/app/views/notification_mailer/article.rhtml +6 -0
  220. data/app/views/notification_mailer/comment.rhtml +11 -0
  221. data/app/views/notification_mailer/trackback.rhtml +3 -0
  222. data/app/views/settings/done.rhtml +2 -0
  223. data/app/views/settings/install.rhtml +12 -0
  224. data/app/views/shared/_search.rhtml +10 -0
  225. data/app/views/sidebar/_row.rhtml +1 -0
  226. data/app/views/sidebar/_sidebar.rhtml +5 -0
  227. data/app/views/sidebar/display_plugins.rhtml +5 -0
  228. data/app/views/sidebar/show.rhtml +1 -0
  229. data/app/views/theme/static_view_test.rhtml +1 -0
  230. data/app/views/xml/_atom10_item_article.rxml +39 -0
  231. data/app/views/xml/_atom10_item_comment.rxml +13 -0
  232. data/app/views/xml/_atom10_item_trackback.rxml +16 -0
  233. data/app/views/xml/_itunes_item_resource.rxml +30 -0
  234. data/app/views/xml/_rss20_item_article.rxml +35 -0
  235. data/app/views/xml/_rss20_item_comment.rxml +7 -0
  236. data/app/views/xml/_rss20_item_trackback.rxml +7 -0
  237. data/app/views/xml/atom10_feed.rxml +18 -0
  238. data/app/views/xml/itunes_feed.rxml +29 -0
  239. data/app/views/xml/rsd.rxml +19 -0
  240. data/app/views/xml/rss20_feed.rxml +18 -0
  241. data/bin/typo +12 -0
  242. data/cache/META/DATA/ACTION_PARAM/localhost.3000/articles/index/.cache +540 -0
  243. data/cache/META/DATA/ACTION_PARAM/localhost.3000/articles/permalink/day=09&month=09&title=i-need-a-new-keyboard-and-mouse&year=2005.cache +414 -0
  244. data/cache/META/META/ACTION_PARAM/localhost.3000/articles/index/.cache +3 -0
  245. data/cache/META/META/ACTION_PARAM/localhost.3000/articles/permalink/day=09&month=09&title=i-need-a-new-keyboard-and-mouse&year=2005.cache +3 -0
  246. data/components/plugins/sidebars/aimpresence/content.rhtml +4 -0
  247. data/components/plugins/sidebars/aimpresence_controller.rb +10 -0
  248. data/components/plugins/sidebars/amazon/content.rhtml +4 -0
  249. data/components/plugins/sidebars/amazon_controller.rb +17 -0
  250. data/components/plugins/sidebars/archives/content.rhtml +11 -0
  251. data/components/plugins/sidebars/archives_controller.rb +24 -0
  252. data/components/plugins/sidebars/audioscrobbler/content.rhtml +10 -0
  253. data/components/plugins/sidebars/audioscrobbler_controller.rb +10 -0
  254. data/components/plugins/sidebars/backpack/content.rhtml +12 -0
  255. data/components/plugins/sidebars/backpack_controller.rb +17 -0
  256. data/components/plugins/sidebars/category/content.rhtml +10 -0
  257. data/components/plugins/sidebars/category_controller.rb +13 -0
  258. data/components/plugins/sidebars/delicious/content.rhtml +20 -0
  259. data/components/plugins/sidebars/delicious_controller.rb +29 -0
  260. data/components/plugins/sidebars/flickr/content.rhtml +15 -0
  261. data/components/plugins/sidebars/flickr_controller.rb +16 -0
  262. data/components/plugins/sidebars/fortythree/content.rhtml +8 -0
  263. data/components/plugins/sidebars/fortythree_controller.rb +12 -0
  264. data/components/plugins/sidebars/fortythreeplaces/content.rhtml +8 -0
  265. data/components/plugins/sidebars/fortythreeplaces_controller.rb +12 -0
  266. data/components/plugins/sidebars/magnolia/content.rhtml +12 -0
  267. data/components/plugins/sidebars/magnolia_controller.rb +17 -0
  268. data/components/plugins/sidebars/recent_comments/content.rhtml +12 -0
  269. data/components/plugins/sidebars/recent_comments_controller.rb +23 -0
  270. data/components/plugins/sidebars/static/content.rhtml +2 -0
  271. data/components/plugins/sidebars/static_controller.rb +23 -0
  272. data/components/plugins/sidebars/tada/content.rhtml +12 -0
  273. data/components/plugins/sidebars/tada_controller.rb +12 -0
  274. data/components/plugins/sidebars/tag/content.rhtml +8 -0
  275. data/components/plugins/sidebars/tag_controller.rb +18 -0
  276. data/components/plugins/sidebars/technorati/content.rhtml +8 -0
  277. data/components/plugins/sidebars/technorati_controller.rb +12 -0
  278. data/components/plugins/sidebars/upcoming/content.rhtml +8 -0
  279. data/components/plugins/sidebars/upcoming_controller.rb +11 -0
  280. data/components/plugins/sidebars/xbox/content.rhtml +2 -0
  281. data/components/plugins/sidebars/xbox_controller.rb +6 -0
  282. data/components/plugins/sidebars/xml/content.rhtml +12 -0
  283. data/components/plugins/sidebars/xml_controller.rb +11 -0
  284. data/components/plugins/textfilters/amazon_controller.rb +39 -0
  285. data/components/plugins/textfilters/code_controller.rb +59 -0
  286. data/components/plugins/textfilters/flickr_controller.rb +75 -0
  287. data/components/plugins/textfilters/htmlfilter_controller.rb +8 -0
  288. data/components/plugins/textfilters/lightbox_controller.rb +122 -0
  289. data/components/plugins/textfilters/macropost_controller.rb +14 -0
  290. data/components/plugins/textfilters/macropre_controller.rb +14 -0
  291. data/components/plugins/textfilters/markdown_controller.rb +32 -0
  292. data/components/plugins/textfilters/none_controller.rb +8 -0
  293. data/components/plugins/textfilters/smartypants_controller.rb +8 -0
  294. data/components/plugins/textfilters/sparkline_controller.rb +87 -0
  295. data/components/plugins/textfilters/textile_controller.rb +8 -0
  296. data/components/sidebars/README +36 -0
  297. data/config/boot.rb +44 -0
  298. data/config/database.yml +17 -0
  299. data/config/database.yml-pgsql +17 -0
  300. data/config/database.yml.example +18 -0
  301. data/config/environment.rb +145 -0
  302. data/config/environments/development.rb +17 -0
  303. data/config/environments/production.rb +19 -0
  304. data/config/environments/test.rb +26 -0
  305. data/config/iTunes.yml +72 -0
  306. data/config/lighttpd.conf +46 -0
  307. data/config/mail.yml +8 -0
  308. data/config/mail.yml.example +8 -0
  309. data/config/routes.rb +92 -0
  310. data/db/DB +0 -0
  311. data/db/DB.test +0 -0
  312. data/db/converters/README +14 -0
  313. data/db/converters/feed.rb +68 -0
  314. data/db/converters/mt-import.rb +72 -0
  315. data/db/converters/mt3.rb +172 -0
  316. data/db/converters/rss.rb +67 -0
  317. data/db/converters/s9y.rb +182 -0
  318. data/db/converters/textpattern.rb +137 -0
  319. data/db/converters/wordpress.rb +187 -0
  320. data/db/development_structure.sql +691 -0
  321. data/db/migrate/001_initial_schema.rb +127 -0
  322. data/db/migrate/002_add_user_email.rb +17 -0
  323. data/db/migrate/003_add_article_user_id.rb +20 -0
  324. data/db/migrate/004_add_sidebars.rb +31 -0
  325. data/db/migrate/005_add_cache_table.rb +20 -0
  326. data/db/migrate/006_add_pages.rb +17 -0
  327. data/db/migrate/007_add_permalink.rb +36 -0
  328. data/db/migrate/008_add_page_title.rb +9 -0
  329. data/db/migrate/009_add_article_guid.rb +10 -0
  330. data/db/migrate/010_add_tags.rb +19 -0
  331. data/db/migrate/011_add_article_id.rb +17 -0
  332. data/db/migrate/012_enlarge_settings.rb +11 -0
  333. data/db/migrate/013_add_textfilters.rb +33 -0
  334. data/db/migrate/014_move_text_filter_to_text_filter_id.rb +43 -0
  335. data/db/migrate/015_convert_mysql_to_innodb.rb +19 -0
  336. data/db/migrate/016_fix_is_primary_postgres.rb +19 -0
  337. data/db/migrate/017_add_comment_user_id.rb +23 -0
  338. data/db/migrate/018_add_guids.rb +13 -0
  339. data/db/migrate/019_add_whiteboards_to_content.rb +15 -0
  340. data/db/migrate/020_superclass_articles.rb +167 -0
  341. data/db/migrate/021_superclass_comments.rb +94 -0
  342. data/db/migrate/022_superclass_trackbacks.rb +71 -0
  343. data/db/migrate/023_superclass_pages.rb +64 -0
  344. data/db/migrate/024_cleanup_contents.rb +54 -0
  345. data/db/migrate/025_add_itunes_metadata.rb +29 -0
  346. data/db/migrate/026_add_redirect_table.rb +13 -0
  347. data/db/migrate/027_set_comment_published_flag.rb +19 -0
  348. data/db/migrate/028_rename_redirect_to.rb +12 -0
  349. data/db/migrate/029_add_user_notification.rb +29 -0
  350. data/db/migrate/030_index_sessions.rb +9 -0
  351. data/db/migrate/031_add_notifications_table.rb +14 -0
  352. data/db/migrate/032_add_jabber_notification.rb +11 -0
  353. data/db/migrate/033_add_count_caching.rb +34 -0
  354. data/db/migrate/034_boolify_published.rb +36 -0
  355. data/db/migrate/035_boolify_content_allow_foo.rb +36 -0
  356. data/db/migrate/036_add_tag_display_name.rb +53 -0
  357. data/db/migrate/037_enlarge_ip_field.rb +9 -0
  358. data/db/migrate/038_add_blog_object.rb +52 -0
  359. data/db/migrate/039_serialize_blog_attributes.rb +160 -0
  360. data/db/migrate/040_attach_content_to_blog.rb +28 -0
  361. data/db/migrate/041_fixup_default_sidebars.rb +18 -0
  362. data/db/migrate/042_remove_sidebar_staged_config.rb +22 -0
  363. data/db/migrate/043_create_triggers.rb +14 -0
  364. data/db/migrate/044_add_published_at_to_content.rb +18 -0
  365. data/db/migrate/045_fix_contents_published_default.rb +9 -0
  366. data/db/migrate/046_fixup_forthcoming_publications.rb +40 -0
  367. data/db/schema.mysql-v3.sql +218 -0
  368. data/db/schema.mysql.sql +218 -0
  369. data/db/schema.postgresql.sql +218 -0
  370. data/db/schema.rb +169 -0
  371. data/db/schema.sqlite.sql +218 -0
  372. data/db/schema.sqlserver.sql +232 -0
  373. data/db/schema_version +1 -0
  374. data/db/scripts/fix_permalinks.rb +5 -0
  375. data/db/updates/update.168.to.200.mysql.sql +33 -0
  376. data/db/updates/update.168.to.200.psql.sql +30 -0
  377. data/doc/Installer.txt +55 -0
  378. data/doc/README_FOR_APP +2 -0
  379. data/installer/rails-installer.rb +488 -0
  380. data/installer/rails_installer_defaults.yml +4 -0
  381. data/installer/typo-installer.rb +35 -0
  382. data/lib/backpack_api.rb +202 -0
  383. data/lib/bare_migration.rb +142 -0
  384. data/lib/email_notify.rb +32 -0
  385. data/lib/format.rb +19 -0
  386. data/lib/generators/sidebar/sidebar_generator.rb +13 -0
  387. data/lib/generators/sidebar/templates/components/plugins/sidebars/controller_template.rb +20 -0
  388. data/lib/generators/sidebar/templates/components/plugins/sidebars/views/content_template.rhtml +4 -0
  389. data/lib/jabber_notify.rb +32 -0
  390. data/lib/login_system.rb +85 -0
  391. data/lib/migrator.rb +28 -0
  392. data/lib/rails_patch/active_record.rb +36 -0
  393. data/lib/rails_patch/components.rb +14 -0
  394. data/lib/sidebars/component_plugin.rb +5 -0
  395. data/lib/sidebars/plugin.rb +241 -0
  396. data/lib/spam_protection.rb +126 -0
  397. data/lib/tasks/release.rake +171 -0
  398. data/lib/tasks/schemas.rake +6 -0
  399. data/lib/tasks/sweep_cache.rake +6 -0
  400. data/lib/text_filter_plugin.rb +97 -0
  401. data/lib/transforms.rb +26 -0
  402. data/lib/typo_guid.rb +10 -0
  403. data/lib/typo_plugins.rb +19 -0
  404. data/lib/typo_version.rb +1 -0
  405. data/lib/xmlrpc_fix.rb +13 -0
  406. data/public/.htaccess +34 -0
  407. data/public/404.html +8 -0
  408. data/public/500.html +8 -0
  409. data/public/dispatch.cgi +10 -0
  410. data/public/dispatch.fcgi +24 -0
  411. data/public/dispatch.rb +10 -0
  412. data/public/favicon.ico +0 -0
  413. data/public/images/admin/dot-vertical.gif +0 -0
  414. data/public/images/admin/h-tile.gif +0 -0
  415. data/public/images/admin/hdr-tile.gif +0 -0
  416. data/public/images/admin/toggle.gif +0 -0
  417. data/public/images/bg.jpg +0 -0
  418. data/public/images/bgcolor.jpg +0 -0
  419. data/public/images/body_bg.png +0 -0
  420. data/public/images/branding_bg.png +0 -0
  421. data/public/images/branding_logo.png +0 -0
  422. data/public/images/checked.gif +0 -0
  423. data/public/images/content_bg.png +0 -0
  424. data/public/images/delete.png +0 -0
  425. data/public/images/footer.jpg +0 -0
  426. data/public/images/footer.png +0 -0
  427. data/public/images/global_bg.png +0 -0
  428. data/public/images/global_logo.png +0 -0
  429. data/public/images/go.png +0 -0
  430. data/public/images/header.jpg +0 -0
  431. data/public/images/loading.gif +0 -0
  432. data/public/images/not-checked copy.gif +0 -0
  433. data/public/images/not-checked.gif +0 -0
  434. data/public/images/overlay.png +0 -0
  435. data/public/images/powered.gif +0 -0
  436. data/public/images/section_bg.png +0 -0
  437. data/public/images/sections_bg.png +0 -0
  438. data/public/images/sections_hilite.png +0 -0
  439. data/public/images/sections_hover.png +0 -0
  440. data/public/images/spinner-blue.gif +0 -0
  441. data/public/images/spinner.gif +0 -0
  442. data/public/images/spot-header.gif +0 -0
  443. data/public/images/spot-title.gif +0 -0
  444. data/public/images/x-ed.gif +0 -0
  445. data/public/javascripts/application.js +2 -0
  446. data/public/javascripts/controls.js +815 -0
  447. data/public/javascripts/cookies.js +60 -0
  448. data/public/javascripts/dragdrop.js +724 -0
  449. data/public/javascripts/effects.js +953 -0
  450. data/public/javascripts/lightbox.js +348 -0
  451. data/public/javascripts/prototype.js +1985 -0
  452. data/public/javascripts/scriptaculous.js +47 -0
  453. data/public/javascripts/slider.js +258 -0
  454. data/public/javascripts/typo.js +85 -0
  455. data/public/robots.txt +2 -0
  456. data/public/stylesheets/administration.css +585 -0
  457. data/public/stylesheets/lightbox.css +22 -0
  458. data/public/stylesheets/rss.css +54 -0
  459. data/public/stylesheets/user-styles.css +0 -0
  460. data/script/about +3 -0
  461. data/script/benchmarker +19 -0
  462. data/script/breakpointer +3 -0
  463. data/script/console +3 -0
  464. data/script/destroy +3 -0
  465. data/script/generate +3 -0
  466. data/script/lighttpd +2 -0
  467. data/script/logreport +79 -0
  468. data/script/migrate +31 -0
  469. data/script/performance/benchmarker +3 -0
  470. data/script/performance/profiler +3 -0
  471. data/script/plugin +3 -0
  472. data/script/process/reaper +3 -0
  473. data/script/process/spawner +3 -0
  474. data/script/process/spinner +3 -0
  475. data/script/profiler +34 -0
  476. data/script/runner +3 -0
  477. data/script/server +3 -0
  478. data/script/spacecheck +40 -0
  479. data/test/fixtures/articles_categories.yml +31 -0
  480. data/test/fixtures/articles_tags.yml +19 -0
  481. data/test/fixtures/blacklist_patterns.yml +10 -0
  482. data/test/fixtures/blogs.yml +74 -0
  483. data/test/fixtures/categories.yml +35 -0
  484. data/test/fixtures/contents.yml +297 -0
  485. data/test/fixtures/notification_mailer/article +3 -0
  486. data/test/fixtures/notification_mailer/comment +3 -0
  487. data/test/fixtures/notification_mailer/trackback +3 -0
  488. data/test/fixtures/notifications.yml +0 -0
  489. data/test/fixtures/page_caches.yml +7 -0
  490. data/test/fixtures/redirects.yml +13 -0
  491. data/test/fixtures/resources.yml +24 -0
  492. data/test/fixtures/sidebars.yml +5 -0
  493. data/test/fixtures/tags.yml +13 -0
  494. data/test/fixtures/text_filters.yml +42 -0
  495. data/test/fixtures/triggers.yml +1 -0
  496. data/test/fixtures/users.yml +56 -0
  497. data/test/functional/accounts_controller_test.rb +82 -0
  498. data/test/functional/admin/article_preview_test.rb +67 -0
  499. data/test/functional/admin/blacklist_controller_test.rb +63 -0
  500. data/test/functional/admin/categories_controller_test.rb +112 -0
  501. data/test/functional/admin/comments_controller_test.rb +78 -0
  502. data/test/functional/admin/content_controller_test.rb +234 -0
  503. data/test/functional/admin/general_controller_test.rb +26 -0
  504. data/test/functional/admin/pages_controller_test.rb +103 -0
  505. data/test/functional/admin/resources_controller_test.rb +49 -0
  506. data/test/functional/admin/textfilters_controller_test.rb +29 -0
  507. data/test/functional/admin/themes_controller_test.rb +34 -0
  508. data/test/functional/admin/trackbacks_controller_test.rb +76 -0
  509. data/test/functional/admin/users_controller_test.rb +72 -0
  510. data/test/functional/articles_controller_test.rb +525 -0
  511. data/test/functional/backend_controller_test.rb +273 -0
  512. data/test/functional/redirect_controller_test.rb +40 -0
  513. data/test/functional/textfilter_controller_test.rb +275 -0
  514. data/test/functional/theme_controller_test.rb +45 -0
  515. data/test/functional/xml_controller_test.rb +353 -0
  516. data/test/mocks/test/dns_mock.rb +13 -0
  517. data/test/mocks/test/flickr_mock.rb +30 -0
  518. data/test/mocks/test/http_mock.rb +38 -0
  519. data/test/mocks/test/theme_mock.rb +5 -0
  520. data/test/mocks/test/xmlrpc_mock.rb +27 -0
  521. data/test/mocks/themes/123-numbers-in-path/about.markdown +0 -0
  522. data/test/mocks/themes/CamelCaseDirectory/about.markdown +0 -0
  523. data/test/mocks/themes/azure/about.markdown +5 -0
  524. data/test/mocks/themes/azure/layouts/default.rhtml +50 -0
  525. data/test/mocks/themes/azure/preview.png +0 -0
  526. data/test/mocks/themes/azure/views/theme/static_view_test.rhtml +1 -0
  527. data/test/mocks/themes/i-have-special-chars/about.markdown +0 -0
  528. data/test/test_helper.rb +95 -0
  529. data/test/unit/article_test.rb +245 -0
  530. data/test/unit/assumptions_tests.rb +54 -0
  531. data/test/unit/audioscrobbler_test.rb +132 -0
  532. data/test/unit/blacklist_pattern_test.rb +14 -0
  533. data/test/unit/blog_test.rb +83 -0
  534. data/test/unit/category_test.rb +37 -0
  535. data/test/unit/comment_test.rb +117 -0
  536. data/test/unit/configuration_test.rb +21 -0
  537. data/test/unit/content_state/factory_test.rb +84 -0
  538. data/test/unit/delicious_test.rb +96 -0
  539. data/test/unit/flickr_test.rb +134 -0
  540. data/test/unit/fortythree_test.rb +66 -0
  541. data/test/unit/magnolia_test.rb +87 -0
  542. data/test/unit/metafragment_test.rb +63 -0
  543. data/test/unit/notification_mailer_test.rb +55 -0
  544. data/test/unit/observer_test.rb +40 -0
  545. data/test/unit/page_cache_test.rb +34 -0
  546. data/test/unit/page_test.rb +25 -0
  547. data/test/unit/ping_test.rb +111 -0
  548. data/test/unit/redirect_test.rb +19 -0
  549. data/test/unit/resource_test.rb +71 -0
  550. data/test/unit/sidebar_test.rb +14 -0
  551. data/test/unit/tag_test.rb +66 -0
  552. data/test/unit/text_filter_test.rb +126 -0
  553. data/test/unit/theme_test.rb +44 -0
  554. data/test/unit/trackback_test.rb +45 -0
  555. data/test/unit/trigger_test.rb +40 -0
  556. data/test/unit/user_test.rb +111 -0
  557. data/themes/azure/about.markdown +5 -0
  558. data/themes/azure/images/bg-tile.gif +0 -0
  559. data/themes/azure/images/bracket.gif +0 -0
  560. data/themes/azure/images/bullet.gif +0 -0
  561. data/themes/azure/images/hdr.gif +0 -0
  562. data/themes/azure/images/q-close.gif +0 -0
  563. data/themes/azure/images/q-open.gif +0 -0
  564. data/themes/azure/images/sbar-tile.gif +0 -0
  565. data/themes/azure/images/spinner.gif +0 -0
  566. data/themes/azure/images/xlink.gif +0 -0
  567. data/themes/azure/layouts/default.rhtml +50 -0
  568. data/themes/azure/preview.png +0 -0
  569. data/themes/azure/stylesheets/azure.css +436 -0
  570. data/themes/azure/stylesheets/print.css +30 -0
  571. data/themes/azure/views/theme/static_view_test.rhtml +1 -0
  572. data/vendor/akismet/Akismet.rb +140 -0
  573. data/vendor/bluecloth/CHANGES +366 -0
  574. data/vendor/bluecloth/LICENSE +340 -0
  575. data/vendor/bluecloth/README +99 -0
  576. data/vendor/bluecloth/bin/bluecloth +83 -0
  577. data/vendor/bluecloth/install.rb +150 -0
  578. data/vendor/bluecloth/lib/bluecloth.rb +1144 -0
  579. data/vendor/bluecloth/test.rb +117 -0
  580. data/vendor/bluecloth/tests/00_Class.tests.rb +71 -0
  581. data/vendor/bluecloth/tests/05_Markdown.tests.rb +1527 -0
  582. data/vendor/bluecloth/tests/10_Bug.tests.rb +57 -0
  583. data/vendor/bluecloth/tests/15_Contrib.tests.rb +132 -0
  584. data/vendor/bluecloth/tests/bctestcase.rb +274 -0
  585. data/vendor/bluecloth/tests/data/antsugar.txt +34 -0
  586. data/vendor/bluecloth/tests/data/ml-announce.txt +17 -0
  587. data/vendor/bluecloth/tests/data/re-overflow.txt +67 -0
  588. data/vendor/bluecloth/tests/data/re-overflow2.txt +281 -0
  589. data/vendor/bluecloth/utils.rb +553 -0
  590. data/vendor/flickr/Rakefile +36 -0
  591. data/vendor/flickr/flickr.rb +490 -0
  592. data/vendor/flickr/index.html +129 -0
  593. data/vendor/flickr/test_flickr.rb +173 -0
  594. data/vendor/jabber4r/CHANGES +8 -0
  595. data/vendor/jabber4r/LICENSE.txt +12 -0
  596. data/vendor/jabber4r/README +180 -0
  597. data/vendor/jabber4r/Rakefile.rb +143 -0
  598. data/vendor/jabber4r/lib/jabber4r/jabber4r.rb +22 -0
  599. data/vendor/jabber4r/lib/jabber4r/jid.rb +93 -0
  600. data/vendor/jabber4r/lib/jabber4r/protocol.rb +1384 -0
  601. data/vendor/jabber4r/lib/jabber4r/rexml_1.8_patch.rb +16 -0
  602. data/vendor/jabber4r/lib/jabber4r/roster.rb +322 -0
  603. data/vendor/jabber4r/lib/jabber4r/session.rb +615 -0
  604. data/vendor/jabber4r/lib/jabber4r/vcard.rb +42 -0
  605. data/vendor/plugins/expiring_action_cache/init.rb +2 -0
  606. data/vendor/plugins/expiring_action_cache/lib/actionparamcache.rb +113 -0
  607. data/vendor/plugins/expiring_action_cache/lib/metafragment.rb +46 -0
  608. data/vendor/plugins/upload_progress/CHANGELOG +3 -0
  609. data/vendor/plugins/upload_progress/MIT-LICENSE +20 -0
  610. data/vendor/plugins/upload_progress/README +45 -0
  611. data/vendor/plugins/upload_progress/Rakefile +23 -0
  612. data/vendor/plugins/upload_progress/init.rb +7 -0
  613. data/vendor/plugins/upload_progress/lib/multipart_progress.rb +169 -0
  614. data/vendor/plugins/upload_progress/lib/progress.rb +145 -0
  615. data/vendor/plugins/upload_progress/lib/upload_progress.rb +303 -0
  616. data/vendor/plugins/upload_progress/lib/upload_progress_helper.rb +424 -0
  617. data/vendor/plugins/upload_progress/public/stylesheets/upload_progress.css +21 -0
  618. data/vendor/plugins/upload_progress/test/multipart_progress_testx.rb +364 -0
  619. data/vendor/plugins/upload_progress/test/upload_progress_helper_testx.rb +134 -0
  620. data/vendor/plugins/upload_progress/test/upload_progress_testx.rb +88 -0
  621. data/vendor/redcloth/RedCloth.gemspec +52 -0
  622. data/vendor/redcloth/bin/redcloth +3 -0
  623. data/vendor/redcloth/doc/CHANGELOG +160 -0
  624. data/vendor/redcloth/doc/COPYING +25 -0
  625. data/vendor/redcloth/doc/README +106 -0
  626. data/vendor/redcloth/doc/REFERENCE +216 -0
  627. data/vendor/redcloth/doc/make.rb +359 -0
  628. data/vendor/redcloth/install.rb +1032 -0
  629. data/vendor/redcloth/lib/redcloth.rb +1130 -0
  630. data/vendor/redcloth/run-tests.rb +28 -0
  631. data/vendor/redcloth/setup.rb +1376 -0
  632. data/vendor/redcloth/tests/code.yml +105 -0
  633. data/vendor/redcloth/tests/hard_breaks.yml +26 -0
  634. data/vendor/redcloth/tests/images.yml +171 -0
  635. data/vendor/redcloth/tests/instiki.yml +39 -0
  636. data/vendor/redcloth/tests/links.yml +155 -0
  637. data/vendor/redcloth/tests/lists.yml +77 -0
  638. data/vendor/redcloth/tests/markdown.yml +218 -0
  639. data/vendor/redcloth/tests/poignant.yml +64 -0
  640. data/vendor/redcloth/tests/table.yml +198 -0
  641. data/vendor/redcloth/tests/textism.yml +406 -0
  642. data/vendor/ruby-mp3info/lib/mp3info.rb +720 -0
  643. data/vendor/rubypants/README +114 -0
  644. data/vendor/rubypants/Rakefile +55 -0
  645. data/vendor/rubypants/html/classes/RubyPants.html +924 -0
  646. data/vendor/rubypants/html/created.rid +1 -0
  647. data/vendor/rubypants/html/files/README.html +248 -0
  648. data/vendor/rubypants/html/files/rubypants_rb.html +125 -0
  649. data/vendor/rubypants/html/fr_class_index.html +27 -0
  650. data/vendor/rubypants/html/fr_file_index.html +28 -0
  651. data/vendor/rubypants/html/fr_method_index.html +38 -0
  652. data/vendor/rubypants/html/index.html +24 -0
  653. data/vendor/rubypants/html/rdoc-style.css +172 -0
  654. data/vendor/rubypants/install.rb +9 -0
  655. data/vendor/rubypants/rubypants.rb +490 -0
  656. data/vendor/rubypants/test_rubypants.rb +162 -0
  657. data/vendor/sparklines/README.txt +47 -0
  658. data/vendor/sparklines/USAGE +14 -0
  659. data/vendor/sparklines/lib/sparklines.rb +435 -0
  660. data/vendor/sparklines/samples/sparklinestest.rb +26 -0
  661. data/vendor/sparklines/sparklines_generator.rb +9 -0
  662. data/vendor/sparklines/templates/sparklines_controller.rb +30 -0
  663. data/vendor/sparklines/templates/sparklines_helper.rb +30 -0
  664. data/vendor/syntax/LICENSE +27 -0
  665. data/vendor/syntax/NEWS +16 -0
  666. data/vendor/syntax/README +38 -0
  667. data/vendor/syntax/Rakefile +194 -0
  668. data/vendor/syntax/api/classes/Syntax.html +173 -0
  669. data/vendor/syntax/api/classes/Syntax/Convertors.html +91 -0
  670. data/vendor/syntax/api/classes/Syntax/Convertors/Abstract.html +159 -0
  671. data/vendor/syntax/api/classes/Syntax/Convertors/HTML.html +149 -0
  672. data/vendor/syntax/api/classes/Syntax/Default.html +123 -0
  673. data/vendor/syntax/api/classes/Syntax/Ruby.html +319 -0
  674. data/vendor/syntax/api/classes/Syntax/Token.html +151 -0
  675. data/vendor/syntax/api/classes/Syntax/Tokenizer.html +324 -0
  676. data/vendor/syntax/api/classes/Syntax/Version.html +109 -0
  677. data/vendor/syntax/api/classes/Syntax/XML.html +205 -0
  678. data/vendor/syntax/api/classes/Syntax/YAML.html +189 -0
  679. data/vendor/syntax/api/created.rid +1 -0
  680. data/vendor/syntax/api/files/README.html +155 -0
  681. data/vendor/syntax/api/files/lib/syntax/common_rb.html +96 -0
  682. data/vendor/syntax/api/files/lib/syntax/convertors/abstract_rb.html +96 -0
  683. data/vendor/syntax/api/files/lib/syntax/convertors/html_rb.html +96 -0
  684. data/vendor/syntax/api/files/lib/syntax/lang/ruby_rb.html +96 -0
  685. data/vendor/syntax/api/files/lib/syntax/lang/xml_rb.html +96 -0
  686. data/vendor/syntax/api/files/lib/syntax/lang/yaml_rb.html +96 -0
  687. data/vendor/syntax/api/files/lib/syntax/version_rb.html +92 -0
  688. data/vendor/syntax/api/files/lib/syntax_rb.html +96 -0
  689. data/vendor/syntax/api/fr_class_index.html +56 -0
  690. data/vendor/syntax/api/fr_file_index.html +54 -0
  691. data/vendor/syntax/api/fr_method_index.html +65 -0
  692. data/vendor/syntax/api/index.html +26 -0
  693. data/vendor/syntax/api/rdoc-style.css +175 -0
  694. data/vendor/syntax/doc/manual-html/chapter-1.html +198 -0
  695. data/vendor/syntax/doc/manual-html/chapter-2.html +262 -0
  696. data/vendor/syntax/doc/manual-html/chapter-3.html +266 -0
  697. data/vendor/syntax/doc/manual-html/chapter-4.html +267 -0
  698. data/vendor/syntax/doc/manual-html/index.html +152 -0
  699. data/vendor/syntax/doc/manual-html/stylesheets/manual.css +270 -0
  700. data/vendor/syntax/doc/manual-html/stylesheets/ruby.css +17 -0
  701. data/vendor/syntax/doc/manual/chapter.erb +38 -0
  702. data/vendor/syntax/doc/manual/example.erb +18 -0
  703. data/vendor/syntax/doc/manual/index.erb +29 -0
  704. data/vendor/syntax/doc/manual/manual.rb +311 -0
  705. data/vendor/syntax/doc/manual/manual.yml +43 -0
  706. data/vendor/syntax/doc/manual/page.erb +87 -0
  707. data/vendor/syntax/doc/manual/parts/0000.txt +5 -0
  708. data/vendor/syntax/doc/manual/parts/0001.txt +16 -0
  709. data/vendor/syntax/doc/manual/parts/0002.txt +24 -0
  710. data/vendor/syntax/doc/manual/parts/0003.txt +6 -0
  711. data/vendor/syntax/doc/manual/parts/0004.txt +32 -0
  712. data/vendor/syntax/doc/manual/parts/0005.txt +18 -0
  713. data/vendor/syntax/doc/manual/parts/0006.txt +62 -0
  714. data/vendor/syntax/doc/manual/parts/0007.txt +3 -0
  715. data/vendor/syntax/doc/manual/parts/0008.txt +5 -0
  716. data/vendor/syntax/doc/manual/parts/0009.txt +29 -0
  717. data/vendor/syntax/doc/manual/parts/0010.txt +21 -0
  718. data/vendor/syntax/doc/manual/stylesheets/manual.css +270 -0
  719. data/vendor/syntax/doc/manual/stylesheets/ruby.css +17 -0
  720. data/vendor/syntax/doc/manual/tutorial.erb +30 -0
  721. data/vendor/syntax/lib/syntax.rb +38 -0
  722. data/vendor/syntax/lib/syntax/common.rb +163 -0
  723. data/vendor/syntax/lib/syntax/convertors/abstract.rb +27 -0
  724. data/vendor/syntax/lib/syntax/convertors/html.rb +51 -0
  725. data/vendor/syntax/lib/syntax/lang/ruby.rb +317 -0
  726. data/vendor/syntax/lib/syntax/lang/xml.rb +108 -0
  727. data/vendor/syntax/lib/syntax/lang/yaml.rb +105 -0
  728. data/vendor/syntax/lib/syntax/version.rb +9 -0
  729. data/vendor/syntax/setup.rb +1331 -0
  730. data/vendor/syntax/syntax.gemspec +24 -0
  731. data/vendor/syntax/test/ALL-TESTS.rb +5 -0
  732. data/vendor/syntax/test/syntax/tc_ruby.rb +871 -0
  733. data/vendor/syntax/test/syntax/tc_xml.rb +202 -0
  734. data/vendor/syntax/test/syntax/tc_yaml.rb +228 -0
  735. data/vendor/syntax/test/syntax/tokenizer_testcase.rb +40 -0
  736. data/vendor/syntax/test/tc_syntax.rb +22 -0
  737. data/vendor/uuidtools/CHANGELOG +4 -0
  738. data/vendor/uuidtools/README +13 -0
  739. data/vendor/uuidtools/install.rb +30 -0
  740. data/vendor/uuidtools/lib/uuidtools.rb +555 -0
  741. data/vendor/uuidtools/rakefile +112 -0
  742. metadata +1022 -0
@@ -0,0 +1,406 @@
1
+ ---
2
+ in: h1. Header 1
3
+ out: <h1>Header 1</h1>
4
+ ---
5
+ in: h2. Header 2
6
+ out: <h2>Header 2</h2>
7
+ ---
8
+ in: h3. Header 3
9
+ out: <h3>Header 3</h3>
10
+ ---
11
+ in: |-
12
+ Any old text.
13
+
14
+ bq. A block quotation.
15
+
16
+ Any old text.
17
+
18
+ out: |-
19
+ <p>Any old text.</p>
20
+
21
+ <blockquote>
22
+ <p>A block quotation.</p>
23
+ </blockquote>
24
+
25
+ <p>Any old text.</p>
26
+
27
+ ---
28
+ in: This is covered elsewhere[1].
29
+ out: <p>This is covered elsewhere<sup><a href="#fn1">1</a></sup>.</p>
30
+ ---
31
+ in: fn1. Down here, in fact.
32
+ out: <p id="fn1"><sup>1</sup> Down here, in fact.</p>
33
+ ---
34
+ in: |-
35
+ # A first item
36
+ # A second item
37
+ # A third item
38
+ # A fourth item
39
+ out: |-
40
+ <ol>
41
+ <li>A first item</li>
42
+ <li>A second item</li>
43
+ <li>A third item</li>
44
+ <li>A fourth item</li>
45
+ </ol>
46
+ ---
47
+ in: |-
48
+ * A first item
49
+ * A second item
50
+ * A third item
51
+ * A fourth item
52
+
53
+ out: |-
54
+ <ul>
55
+ <li>A first item</li>
56
+ <li>A second item</li>
57
+ <li>A third item</li>
58
+ <li>A fourth item</li>
59
+ </ul>
60
+
61
+ ---
62
+ in: _a phrase_
63
+ out: <p><em>a phrase</em></p>
64
+ ---
65
+ in: __a phrase__
66
+ out: <p><i>a phrase</i></p>
67
+ ---
68
+ in: '*a phrase*'
69
+ out: <p><strong>a phrase</strong></p>
70
+ ---
71
+ in: '**a phrase**'
72
+ out: <p><b>a phrase</b></p>
73
+ ---
74
+ in: '*(a)* a'
75
+ out: <p><strong>(a)</strong> a</p>
76
+ ---
77
+ in: '*(a)* *'
78
+ out: <p><strong>(a)</strong> *</p>
79
+ ---
80
+ in: Nabokov's ??Pnin??
81
+ out: <p>Nabokov&#8217;s <cite>Pnin</cite></p>
82
+ ---
83
+ in: -a phrase-
84
+ out: <p><del>a phrase</del></p>
85
+ ---
86
+ in: +a phrase+
87
+ out: <p><ins>a phrase</ins></p>
88
+ ---
89
+ in: ^a phrase^
90
+ out: <p><sup>a phrase</sup></p>
91
+ ---
92
+ in: ~a phrase~
93
+ out: <p><sub>a phrase</sub></p>
94
+ # ---
95
+ # in: %(caps)SPAN%
96
+ # out: <p><span class="caps">SPAN</span>
97
+ ---
98
+ in: %{color:red}red%
99
+ out: <p><span style="color:red;">red</span></p>
100
+ ---
101
+ in: %[fr]rouge%
102
+ out: <p><span lang="fr">rouge</span></p>
103
+ ---
104
+ in: _(big)red_
105
+ out: <p><em class="big">red</em></p>
106
+ ---
107
+ in: p(bob). A paragraph
108
+ out: <p class="bob">A paragraph</p>
109
+ ---
110
+ in: p{color:#ddd}. A paragraph
111
+ out: <p style="color:#ddd;">A paragraph</p>
112
+ ---
113
+ in: p[fr]. A paragraph
114
+ out: <p lang="fr">A paragraph</p>
115
+ ---
116
+ in: h2()>. right-aligned header2, indented 1em both side
117
+ out: <h2 style="padding-left:1em;padding-right:1em;text-align:right;">right-aligned header2, indented 1em both side</h2>
118
+ ---
119
+ in: h3=. centered header
120
+ out: <h3 style="text-align:center;">centered header</h3>
121
+ ---
122
+ in: '!>/image.gif! right-aligned image'
123
+ out: <p style="float:right"><img src="/image.gif" alt="" /> right-aligned image</p>
124
+ ---
125
+ in: p[no]{color:red}. A Norse of a different colour.
126
+ out: <p style="color:red;" lang="no">A Norse of a different colour.</p>
127
+ ---
128
+ in: |-
129
+ |This|is|a|simple|table|
130
+ |This|is|a|simple|row|
131
+ out: |-
132
+ <table>
133
+ <tr>
134
+ <td>This</td>
135
+ <td>is</td>
136
+ <td>a</td>
137
+ <td>simple</td>
138
+ <td>table</td>
139
+ </tr>
140
+ <tr>
141
+ <td>This</td>
142
+ <td>is</td>
143
+ <td>a</td>
144
+ <td>simple</td>
145
+ <td>row</td>
146
+ </tr>
147
+ </table>
148
+ ---
149
+ in: |-
150
+ table{border:1px solid black}.
151
+ |This|is|a|row|
152
+ |This|is|a|row|
153
+ out: |-
154
+ <table style="border:1px solid black;">
155
+ <tr>
156
+ <td>This</td>
157
+ <td>is</td>
158
+ <td>a</td>
159
+ <td>row</td>
160
+ </tr>
161
+ <tr>
162
+ <td>This</td>
163
+ <td>is</td>
164
+ <td>a</td>
165
+ <td>row</td>
166
+ </tr>
167
+ </table>
168
+ ---
169
+ in: '{background:#ddd}. |This|is|a|row|'
170
+ out: |-
171
+ <table>
172
+ <tr style="background:#ddd;">
173
+ <td>This</td>
174
+ <td>is</td>
175
+ <td>a</td>
176
+ <td>row</td>
177
+ </tr>
178
+ </table>
179
+ ---
180
+ in: |-
181
+ |{background:#ddd}. Cell with gray background|
182
+ |\2. Cell spanning 2 columns|
183
+ |/3. Cell spanning 3 rows|
184
+ |>. Right-aligned cell|
185
+ out: |-
186
+ <table>
187
+ <tr>
188
+ <td style="background:#ddd;">Cell with gray background</td>
189
+ </tr>
190
+ <tr>
191
+ <td colspan="2">Cell spanning 2 columns</td>
192
+ </tr>
193
+ <tr>
194
+ <td rowspan="3">Cell spanning 3 rows</td>
195
+ </tr>
196
+ <tr>
197
+ <td style="text-align:right;">Right-aligned cell</td>
198
+ </tr>
199
+ </table>
200
+ # ---
201
+ # in: |-
202
+ # This is a "link":bob to Bob's website.
203
+ #
204
+ # [bob]http://itsbob.com/index.html
205
+ ---
206
+ in: ACLU(American Civil Liberties Union)
207
+ out: <p><acronym title="American Civil Liberties Union">ACLU</acronym></p>
208
+ ---
209
+ in: |-
210
+ h2{color:green}. This is a title
211
+
212
+ h3. This is a subhead
213
+
214
+ p{color:red}. This is some text of dubious character. Isn't the use of "quotes" just lazy writing -- and theft of 'intellectual property' besides? I think the time has come to see a block quote.
215
+
216
+ bq[fr]. This is a block quote. I'll admit it's not the most exciting block quote ever devised.
217
+
218
+ Simple list:
219
+
220
+ #{color:blue} one
221
+ # two
222
+ # three
223
+
224
+ Multi-level list:
225
+
226
+ # one
227
+ ## aye
228
+ ## bee
229
+ ## see
230
+ # two
231
+ ## x
232
+ ## y
233
+ # three
234
+
235
+ Mixed list:
236
+
237
+ * Point one
238
+ * Point two
239
+ ## Step 1
240
+ ## Step 2
241
+ ## Step 3
242
+ * Point three
243
+ ** Sub point 1
244
+ ** Sub point 2
245
+
246
+
247
+ Well, that went well. How about we insert an <a href="/" title="watch out">old-fashioned hypertext link</a>? Will the quote marks in the tags get messed up? No!
248
+
249
+ "This is a link (optional title)":http://www.textism.com
250
+
251
+ table{border:1px solid black}.
252
+ |_. this|_. is|_. a|_. header|
253
+ <{background:gray}. |\2. this is|{background:red;width:200px}. a|^<>{height:200px}. row|
254
+ |this|<>{padding:10px}. is|^. another|(bob#bob). row|
255
+
256
+ An image:
257
+
258
+ !/common/textist.gif(optional alt text)!
259
+
260
+ # Librarians rule
261
+ # Yes they do
262
+ # But you knew that
263
+
264
+ Some more text of dubious character. Here is a noisome string of CAPITAL letters. Here is something we want to _emphasize_.
265
+ That was a linebreak. And something to indicate *strength*. Of course I could use <em>my own HTML tags</em> if I <strong>felt</strong> like it.
266
+
267
+ h3. Coding
268
+
269
+ This <code>is some code, "isn't it"</code>. Watch those quote marks! Now for some preformatted text:
270
+
271
+ <pre>
272
+ <code>
273
+ $text = str_replace("<p>%::%</p>","",$text);
274
+ $text = str_replace("%::%</p>","",$text);
275
+ $text = str_replace("%::%","",$text);
276
+
277
+ </code>
278
+ </pre>
279
+
280
+ This isn't code.
281
+
282
+
283
+ So you see, my friends:
284
+
285
+ * The time is now
286
+ * The time is not later
287
+ * The time is not yesterday
288
+ * We must act
289
+
290
+ out: |-
291
+ <h2 style="color:green;">This is a title</h2>
292
+
293
+ <h3>This is a subhead</h3>
294
+
295
+ <p style="color:red;">This is some text of dubious character. Isn&#8217;t the use of &#8220;quotes&#8221; just lazy writing&#8212;and theft of &#8216;intellectual property&#8217; besides? I think the time has come to see a block quote.</p>
296
+
297
+ <blockquote>
298
+ <p lang="fr">This is a block quote. I&#8217;ll admit it&#8217;s not the most exciting block quote ever devised.</p>
299
+ </blockquote>
300
+
301
+ <p>Simple list:</p>
302
+ <ol style="color:blue;">
303
+ <li>one</li>
304
+ <li>two</li>
305
+ <li>three</li>
306
+ </ol>
307
+
308
+ <p>Multi-level list:</p>
309
+ <ol>
310
+ <li>one
311
+ <ol>
312
+ <li>aye</li>
313
+ <li>bee</li>
314
+ <li>see</li>
315
+ </ol>
316
+ </li>
317
+ <li>two
318
+ <ol>
319
+ <li>x</li>
320
+ <li>y</li>
321
+ </ol>
322
+ </li>
323
+ <li>three</li>
324
+ </ol>
325
+
326
+ <p>Mixed list:</p>
327
+ <ul>
328
+ <li>Point one</li>
329
+ <li>Point two
330
+ <ol>
331
+ <li>Step 1</li>
332
+ <li>Step 2</li>
333
+ <li>Step 3</li>
334
+ </ol>
335
+ </li>
336
+ <li>Point three
337
+ <ul>
338
+ <li>Sub point 1</li>
339
+ <li>Sub point 2</li>
340
+ </ul></li>
341
+ </ul>
342
+
343
+ <p>Well, that went well. How about we insert an <a href="/" title="watch out">old-fashioned hypertext link</a>? Will the quote marks in the tags get messed up? No!</p>
344
+
345
+ <p><a href="http://www.textism.com" title="optional title">This is a link</a></p>
346
+
347
+ <table style="border:1px solid black;">
348
+ <tr>
349
+ <th>this</th>
350
+ <th>is</th>
351
+ <th>a</th>
352
+ <th>header</th>
353
+ </tr>
354
+ <tr style="background:gray;text-align:left;">
355
+ <td colspan="2">this is</td>
356
+ <td style="background:red;width:200px;">a</td>
357
+ <td style="vertical-align:top;height:200px;text-align:justify;">row</td>
358
+ </tr>
359
+ <tr>
360
+ <td>this</td>
361
+ <td style="padding:10px;text-align:justify;">is</td>
362
+ <td style="vertical-align:top;">another</td>
363
+ <td class="bob" id="bob">row</td>
364
+ </tr>
365
+ </table>
366
+
367
+
368
+
369
+ <p>An image:</p>
370
+
371
+ <p><img src="/common/textist.gif" title="optional alt text" alt="optional alt text" /></p>
372
+ <ol>
373
+ <li>Librarians rule</li>
374
+ <li>Yes they do</li>
375
+ <li>But you knew that</li>
376
+ </ol>
377
+
378
+ <p>Some more text of dubious character. Here is a noisome string of <span class="caps">CAPITAL</span> letters. Here is something we want to <em>emphasize</em>.
379
+ That was a linebreak. And something to indicate <strong>strength</strong>. Of course I could use <em>my own <span class="caps">HTML</span> tags</em> if I <strong>felt</strong> like it.</p>
380
+
381
+ <h3>Coding</h3>
382
+
383
+ <p>This <code>is some code, "isn't it"</code>. Watch those quote marks! Now for some preformatted text:</p>
384
+
385
+
386
+ <pre>
387
+ <code>
388
+ $text = str_replace("&lt;p&gt;%::%&lt;/p&gt;","",$text);
389
+ $text = str_replace("%::%&lt;/p&gt;","",$text);
390
+ $text = str_replace("%::%","",$text);
391
+
392
+ </code>
393
+ </pre>
394
+ <p>This isn&#8217;t code.</p>
395
+
396
+ <p>So you see, my friends:</p>
397
+ <ul>
398
+ <li>The time is now</li>
399
+ <li>The time is not later</li>
400
+ <li>The time is not yesterday</li>
401
+ <li>We must act</li>
402
+ </ul>
403
+
404
+ ---
405
+ in: '"test":http://foo.com/b---ar'
406
+ out: <p><a href="http://foo.com/b---ar">test</a></p>
@@ -0,0 +1,720 @@
1
+ # $Id: mp3info.rb,v 1.5 2005/04/26 13:41:41 moumar Exp $
2
+ # = Description
3
+ #
4
+ # ruby-mp3info gives you access to low level informations on mp3 files
5
+ # (bitrate, length, samplerate, etc...). It can read, write, remove id3v1 tag
6
+ # and read id3v2. It is written in pure ruby.
7
+ #
8
+ #
9
+ # = Download
10
+ #
11
+ # get tar.gz at
12
+ # http://rubyforge.org/projects/ruby-mp3info/
13
+ #
14
+ #
15
+ # = Installation
16
+ #
17
+ # $ ruby install.rb config
18
+ # $ ruby install.rb setup
19
+ # # ruby install.rb install
20
+ #
21
+ # or
22
+ #
23
+ # # gem install ruby-mp3info
24
+ #
25
+ #
26
+ # = Example
27
+ #
28
+ # require "mp3info"
29
+ #
30
+ # mp3info = Mp3Info.new("myfile.mp3")
31
+ # puts mp3info
32
+ #
33
+ #
34
+ # = Testing
35
+ #
36
+ # Test::Unit library is used for tests. see http://testunit.talbott.ws/
37
+ #
38
+ # $ ruby test.rb
39
+ #
40
+ #
41
+ # = ToDo
42
+ #
43
+ # * adding write support for ID3v2 tags
44
+ # * adding a test for id3v2
45
+ # * encoder detection
46
+ #
47
+ #
48
+ # = Changelog
49
+ #
50
+ # [0.4 26/04/2005]
51
+ #
52
+ # * fixes in vbr mode
53
+ # * removed extract_info_from_head() function
54
+ # * now try several times to find a good header frame before giving up
55
+ # * correct handling of unicode in v2 tags. Require standard "iconv" library if such tags are used
56
+ # * FIXED if a tag appears more than one time, create an array with every value found for this tag
57
+ #
58
+ #
59
+ # [0.3 04/05/2004]
60
+ #
61
+ # * massive changes of most of the code to make it easier to read & hopefully run faster
62
+ # * ID2TAGS hash is just informative now, no use of it in the code. id3v2 tag fields are read in directly
63
+ # * added support for id3 v2.2 and v2.4 (0.2.1 only supported v2.3)
64
+ # * much improved vbr duration guessing
65
+ # * made Mp3Info#to_s output to be prettier
66
+ # * moved hastag1? and hastag2? to be class booleans instead of functions (now named hastag1 and hastag2)
67
+ # * fixed a bug on computing "error_protection" attribute
68
+ # * new attribute "tag", which is a sort of "universal" tag, regardless of the tag version, 1 or 2, with the same keys as @tag1
69
+ # * new method hastag?, which test the presence of any tag
70
+ #
71
+ #
72
+ # [0.2.1 04/09/2003]
73
+ #
74
+ # * filename attribute added
75
+ # * mp3 files are opened read-only now [Alan Davies <alan__DOT_davies__AT__thomson.com>]
76
+ # * Mp3Info#initialize: bugfixes [Alan Davies <alan__DOT_davies__AT__thomson.com>]
77
+ # * put NULLs in year field in id3v1 tags instead of zeros [Alan Davies <alan__DOT_davies__AT__thomson.com>]
78
+ # * Mp3Info#gettag1: remove null at end of strings [Alan Davies <alan__DOT_davies__AT__thomson.com>]
79
+ # * Mp3Info#extract_infos_from_head(): some brackets missed [Alan Davies <alan__DOT_davies__AT__thomson.com>]
80
+ #
81
+ #
82
+ # [0.2 18/08/2003]
83
+ #
84
+ # * writing, reading and removing of id3v1 tags
85
+ # * reading of id3v2 tags
86
+ # * test suite improved
87
+ # * to_s method added
88
+ # * length attribute is a Float now
89
+ #
90
+ #
91
+ # [0.1 17/03/2003]
92
+ #
93
+ # * Initial version
94
+ #
95
+ #
96
+ # License:: Ruby
97
+ # Author:: Guillaume Pierronnet (mailto:moumar_AT__rubyforge_DOT_org)
98
+ # Website:: http://ruby-mp3info.rubyforge.org/
99
+
100
+ # Raised on any kind of error related to ruby-mp3info
101
+ class Mp3InfoError < StandardError ; end
102
+
103
+ class Mp3InfoInternalError < StandardError #:nodoc:
104
+ end
105
+
106
+ class Numeric
107
+ ### returns the selected bit range (b, a) as a number
108
+ ### NOTE: b > a if not, returns 0
109
+ def bits(b, a)
110
+ t = 0
111
+ b.downto(a) { |i| t += t + self[i] }
112
+ t
113
+ end
114
+ end
115
+
116
+ class Hash
117
+ ### lets you specify hash["key"] as hash.key
118
+ ### this came from CodingInRuby on RubyGarden
119
+ ### http://www.rubygarden.org/ruby?CodingInRuby
120
+ def method_missing(meth,*args)
121
+ if /=$/=~(meth=meth.id2name) then
122
+ self[meth[0...-1]] = (args.length<2 ? args[0] : args)
123
+ else
124
+ self[meth]
125
+ end
126
+ end
127
+ end
128
+
129
+ class File
130
+ def get32bits
131
+ (getc << 24) + (getc << 16) + (getc << 8) + getc
132
+ end
133
+ def get_syncsafe
134
+ (getc << 21) + (getc << 14) + (getc << 7) + getc
135
+ end
136
+ end
137
+
138
+ class Mp3Info
139
+
140
+ VERSION = "0.4"
141
+
142
+ LAYER = [ nil, 3, 2, 1]
143
+ BITRATE = [
144
+ [
145
+ [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448],
146
+ [32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384],
147
+ [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320] ],
148
+ [
149
+ [32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256],
150
+ [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],
151
+ [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
152
+ ]
153
+ ]
154
+ SAMPLERATE = [
155
+ [ 44100, 48000, 32000 ],
156
+ [ 22050, 24000, 16000 ]
157
+ ]
158
+ CHANNEL_MODE = [ "Stereo", "JStereo", "Dual Channel", "Single Channel"]
159
+
160
+ GENRES = [
161
+ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk",
162
+ "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies",
163
+ "Other", "Pop", "R&B", "Rap", "Reggae", "Rock",
164
+ "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks",
165
+ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk",
166
+ "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House",
167
+ "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", "Bass",
168
+ "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
169
+ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk",
170
+ "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta",
171
+ "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
172
+ "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
173
+ "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical",
174
+ "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing",
175
+ "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde",
176
+ "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
177
+ "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
178
+ "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
179
+ "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba",
180
+ "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet",
181
+ "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall",
182
+ "Goa", "Drum & Bass", "Club House", "Hardcore", "Terror",
183
+ "Indie", "BritPop", "NegerPunk", "Polsk Punk", "Beat",
184
+ "Christian Gangsta", "Heavy Metal", "Black Metal", "Crossover", "Contemporary C",
185
+ "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop",
186
+ "SynthPop" ]
187
+
188
+
189
+ ID2TAGS = {
190
+ "AENC" => "Audio encryption",
191
+ "APIC" => "Attached picture",
192
+ "COMM" => "Comments",
193
+ "COMR" => "Commercial frame",
194
+ "ENCR" => "Encryption method registration",
195
+ "EQUA" => "Equalization",
196
+ "ETCO" => "Event timing codes",
197
+ "GEOB" => "General encapsulated object",
198
+ "GRID" => "Group identification registration",
199
+ "IPLS" => "Involved people list",
200
+ "LINK" => "Linked information",
201
+ "MCDI" => "Music CD identifier",
202
+ "MLLT" => "MPEG location lookup table",
203
+ "OWNE" => "Ownership frame",
204
+ "PRIV" => "Private frame",
205
+ "PCNT" => "Play counter",
206
+ "POPM" => "Popularimeter",
207
+ "POSS" => "Position synchronisation frame",
208
+ "RBUF" => "Recommended buffer size",
209
+ "RVAD" => "Relative volume adjustment",
210
+ "RVRB" => "Reverb",
211
+ "SYLT" => "Synchronized lyric/text",
212
+ "SYTC" => "Synchronized tempo codes",
213
+ "TALB" => "Album/Movie/Show title",
214
+ "TBPM" => "BPM (beats per minute)",
215
+ "TCOM" => "Composer",
216
+ "TCON" => "Content type",
217
+ "TCOP" => "Copyright message",
218
+ "TDAT" => "Date",
219
+ "TDLY" => "Playlist delay",
220
+ "TENC" => "Encoded by",
221
+ "TEXT" => "Lyricist/Text writer",
222
+ "TFLT" => "File type",
223
+ "TIME" => "Time",
224
+ "TIT1" => "Content group description",
225
+ "TIT2" => "Title/songname/content description",
226
+ "TIT3" => "Subtitle/Description refinement",
227
+ "TKEY" => "Initial key",
228
+ "TLAN" => "Language(s)",
229
+ "TLEN" => "Length",
230
+ "TMED" => "Media type",
231
+ "TOAL" => "Original album/movie/show title",
232
+ "TOFN" => "Original filename",
233
+ "TOLY" => "Original lyricist(s)/text writer(s)",
234
+ "TOPE" => "Original artist(s)/performer(s)",
235
+ "TORY" => "Original release year",
236
+ "TOWN" => "File owner/licensee",
237
+ "TPE1" => "Lead performer(s)/Soloist(s)",
238
+ "TPE2" => "Band/orchestra/accompaniment",
239
+ "TPE3" => "Conductor/performer refinement",
240
+ "TPE4" => "Interpreted, remixed, or otherwise modified by",
241
+ "TPOS" => "Part of a set",
242
+ "TPUB" => "Publisher",
243
+ "TRCK" => "Track number/Position in set",
244
+ "TRDA" => "Recording dates",
245
+ "TRSN" => "Internet radio station name",
246
+ "TRSO" => "Internet radio station owner",
247
+ "TSIZ" => "Size",
248
+ "TSRC" => "ISRC (international standard recording code)",
249
+ "TSSE" => "Software/Hardware and settings used for encoding",
250
+ "TYER" => "Year",
251
+ "TXXX" => "User defined text information frame",
252
+ "UFID" => "Unique file identifier",
253
+ "USER" => "Terms of use",
254
+ "USLT" => "Unsychronized lyric/text transcription",
255
+ "WCOM" => "Commercial information",
256
+ "WCOP" => "Copyright/Legal information",
257
+ "WOAF" => "Official audio file webpage",
258
+ "WOAR" => "Official artist/performer webpage",
259
+ "WOAS" => "Official audio source webpage",
260
+ "WORS" => "Official internet radio station homepage",
261
+ "WPAY" => "Payment",
262
+ "WPUB" => "Publishers official webpage",
263
+ "WXXX" => "User defined URL link frame"
264
+ }
265
+
266
+ TAGSIZE = 128
267
+ #MAX_FRAME_COUNT = 6 #number of frame to read for encoder detection
268
+
269
+ # mpeg version = 1 or 2
270
+ attr_reader(:mpeg_version)
271
+
272
+ # layer = 1, 2, or 3
273
+ attr_reader(:layer)
274
+
275
+ # bitrate in kbps
276
+ attr_reader(:bitrate)
277
+
278
+ # samplerate in Hz
279
+ attr_reader(:samplerate)
280
+
281
+ # channel mode => "Stereo", "JStereo", "Dual Channel" or "Single Channel"
282
+ attr_reader(:channel_mode)
283
+
284
+ # variable bitrate => true or false
285
+ attr_reader(:vbr)
286
+
287
+ # length in seconds as a Float
288
+ attr_reader(:length)
289
+
290
+ # error protection => true or false
291
+ attr_reader(:error_protection)
292
+
293
+ #a sort of "universal" tag, regardless of the tag version, 1 or 2, with the same keys as @tag1
294
+ attr_reader(:tag)
295
+
296
+ # id3v1 tag has a Hash. You can modify it, it will be written when calling
297
+ # "close" method.
298
+ attr_accessor(:tag1)
299
+
300
+ # id3v2 tag as a Hash
301
+ attr_reader(:tag2)
302
+
303
+ # the original filename
304
+ attr_reader(:filename)
305
+
306
+ # Moved hastag1? and hastag2? to be booleans
307
+ attr_reader(:hastag1, :hastag2)
308
+
309
+ # Test the presence of an id3v1 tag in file +filename+
310
+ def self.hastag1?(filename)
311
+ File.open(filename) { |f|
312
+ f.seek(-TAGSIZE, File::SEEK_END)
313
+ f.read(3) == "TAG"
314
+ }
315
+ end
316
+
317
+ # Test the presence of an id3v2 tag in file +filename+
318
+ def self.hastag2?(filename)
319
+ File.open(filename) { |f|
320
+ f.read(3) == "ID3"
321
+ }
322
+ end
323
+
324
+
325
+ # Remove id3v1 tag from +filename+
326
+ def self.removetag1(filename)
327
+ if self.hastag1?(filename)
328
+ newsize = File.size(filename) - TAGSIZE
329
+ File.open(filename, "r+") { |f| f.truncate(newsize) }
330
+ end
331
+ end
332
+
333
+ # Instantiate a new Mp3Info object with name +filename+
334
+ def initialize(filename)
335
+ $stderr.puts("#{self.class}::new() does not take block; use #{self.class}::open() instead") if block_given?
336
+ raise(Mp3InfoError, "empty file") unless File.stat(filename).size? #FIXME
337
+ @filename = filename
338
+ @hastag1, @hastag2 = false
339
+ @tag = Hash.new
340
+ @tag1 = Hash.new
341
+ @tag2 = Hash.new
342
+
343
+ @file = File.new(filename, "rb")
344
+ parse_tags
345
+ @tag_orig = @tag1.dup
346
+
347
+ #creation of a sort of "universal" tag, regardless of the tag version
348
+ if hastag2?
349
+ h = {
350
+ "title" => "TIT2",
351
+ "artist" => "TPE1",
352
+ "album" => "TALB",
353
+ "year" => "TYER",
354
+ "tracknum" => "TRCK",
355
+ "comments" => "COMM",
356
+ "genre" => 255,
357
+ "genre_s" => "TCON"
358
+ }
359
+
360
+ h.each { |k, v| @tag[k] = @tag2[v] }
361
+
362
+ elsif hastag1?
363
+ @tag = @tag1.dup
364
+ end
365
+
366
+
367
+ ### extracts MPEG info from MPEG header and stores it in the hash @mpeg
368
+ ### head (fixnum) = valid 4 byte MPEG header
369
+
370
+ found = false
371
+
372
+ 5.times do
373
+ head = find_next_frame()
374
+ @mpeg_version = [2, 1][head[19]]
375
+ @layer = LAYER[head.bits(18,17)]
376
+ next if @layer.nil?
377
+ @bitrate = BITRATE[@mpeg_version-1][@layer-1][head.bits(15,12)-1]
378
+ @error_protection = head[16] == 0 ? true : false
379
+ @samplerate = SAMPLERATE[@mpeg_version-1][head.bits(11,10)]
380
+ @padding = (head[9] == 1 ? true : false)
381
+ @channel_mode = CHANNEL_MODE[@channel_num = head.bits(7,6)]
382
+ @copyright = (head[3] == 1 ? true : false)
383
+ @original = (head[2] == 1 ? true : false)
384
+ @vbr = false
385
+ found = true
386
+ break
387
+ end
388
+
389
+ raise(Mp3InfoError, "Cannot find good frame") unless found
390
+
391
+
392
+ seek = @mpeg_version == 1 ?
393
+ (@channel_num == 3 ? 17 : 32) :
394
+ (@channel_num == 3 ? 9 : 17)
395
+
396
+ @file.seek(seek, IO::SEEK_CUR)
397
+
398
+ vbr_head = @file.read(4)
399
+ if vbr_head == "Xing"
400
+ flags = @file.get32bits
401
+ @streamsize = @frames = 0
402
+ flags[1] == 1 and @frames = @file.get32bits
403
+ flags[2] == 1 and @streamsize = @file.get32bits
404
+ # currently this just skips the TOC entries if they're found
405
+ @file.seek(100, IO::SEEK_CUR) if flags[0] == 1
406
+ @vbr_quality = @file.get32bits if flags[3] == 1
407
+ @length = (26/1000.0)*@frames
408
+ @bitrate = (((@streamsize/@frames)*@samplerate)/144) >> 10
409
+ @vbr = true
410
+ else
411
+ # for cbr, calculate duration with the given bitrate
412
+ @streamsize = @file.stat.size - (@hastag1 ? TAGSIZE : 0) - (@hastag2 ? @tag2["length"] : 0)
413
+ @length = ((@streamsize << 3)/1000.0)/@bitrate
414
+ if @tag2["TLEN"]
415
+ # but if another duration is given and it isn't close (within 5%)
416
+ # assume the mp3 is vbr and go with the given duration
417
+ tlen = (@tag2["TLEN"].to_i)/1000
418
+ percent_diff = ((@length.to_i-tlen)/tlen.to_f)
419
+ if percent_diff.abs > 0.05
420
+ # without the xing header, this is the best guess without reading
421
+ # every single frame
422
+ @vbr = true
423
+ @length = @tag2["TLEN"].to_i/1000
424
+ @bitrate = (@streamsize / @bitrate) >> 10
425
+ end
426
+ end
427
+ end
428
+ end
429
+
430
+ # "block version" of Mp3Info::new()
431
+ def self.open(filename)
432
+ m = self.new(filename)
433
+ ret = nil
434
+ if block_given?
435
+ begin
436
+ ret = yield(m)
437
+ ensure
438
+ m.close
439
+ end
440
+ else
441
+ ret = m
442
+ end
443
+ ret
444
+ end
445
+
446
+ # Remove id3v1 from mp3
447
+ def removetag1
448
+ if hastag1?
449
+ newsize = @file.stat.size(filename) - TAGSIZE
450
+ @file.truncate(newsize)
451
+ @tag1.clear
452
+ end
453
+ self
454
+ end
455
+
456
+ # Has file an id3v1 or v2 tag? true or false
457
+ def hastag?
458
+ @hastag1 or @hastag2
459
+ end
460
+
461
+ # Has file an id3v1 tag? true or false
462
+ def hastag1?
463
+ @hastag1
464
+ end
465
+
466
+ # Has file an id3v2 tag? true or false
467
+ def hastag2?
468
+ @hastag2
469
+ end
470
+
471
+
472
+ # Flush pending modifications to tags and close the file
473
+ def close
474
+ return if @file.nil?
475
+ if @tag1 != @tag_orig
476
+ @tag_orig.update(@tag1)
477
+ #puts "@tag_orig: #{@tag_orig.inspect}"
478
+ @file.reopen(@filename, 'rb+')
479
+ @file.seek(-TAGSIZE, File::SEEK_END)
480
+ t = @file.read(3)
481
+ if t != 'TAG'
482
+ #append new tag
483
+ @file.seek(0, File::SEEK_END)
484
+ @file.write('TAG')
485
+ end
486
+ str = [
487
+ @tag_orig["title"]||"",
488
+ @tag_orig["artist"]||"",
489
+ @tag_orig["album"]||"",
490
+ ((@tag_orig["year"] != 0) ? ("%04d" % @tag_orig["year"]) : "\0\0\0\0"),
491
+ @tag_orig["comments"]||"",
492
+ 0,
493
+ @tag_orig["tracknum"]||0,
494
+ @tag_orig["genre"]||255
495
+ ].pack("Z30Z30Z30Z4Z28CCC")
496
+ @file.write(str)
497
+ end
498
+ @file.close
499
+ @file = nil
500
+ end
501
+
502
+ # inspect inside Mp3Info
503
+ def to_s
504
+ s = "MPEG #{@mpeg_version} Layer #{@layer} #{@vbr ? "VBR" : "CBR"} #{@bitrate} Kbps #{@channel_mode} #{@samplerate} Hz length #{@length} sec. error protection #{@error_protection} "
505
+ s << "tag1: "+@tag1.inspect+"\n" if @hastag1
506
+ s << "tag2: "+@tag2.inspect+"\n" if @hastag2
507
+ s
508
+ end
509
+
510
+
511
+ private
512
+
513
+ ### parses the id3 tags of the currently open @file
514
+ def parse_tags
515
+ return if @file.stat.size < TAGSIZE # file is too small
516
+ @file.seek(0)
517
+ f3 = @file.read(3)
518
+ gettag1 if f3 == "TAG" # v1 tag at beginning
519
+ gettag2 if f3 == "ID3" # v2 tag at beginning
520
+ unless @hastag1 # v1 tag at end
521
+ # this preserves the file pos if tag2 found, since gettag2 leaves
522
+ # the file at the best guess as to the first MPEG frame
523
+ pos = (@hastag2 ? @file.pos : 0)
524
+ # seek to where id3v1 tag should be
525
+ @file.seek(-TAGSIZE, IO::SEEK_END)
526
+ gettag1 if @file.read(3) == "TAG"
527
+ @file.seek(pos)
528
+ end
529
+ end
530
+
531
+ ### reads in id3 field strings, stripping out non-printable chars
532
+ ### len (fixnum) = number of chars in field
533
+ ### returns string
534
+ def read_id3_string(len)
535
+ #FIXME handle unicode strings
536
+ #return @file.read(len)
537
+ s = ""
538
+ len.times do
539
+ c = @file.getc
540
+ # only append printable characters
541
+ s << c if c >= 32 and c < 254
542
+ end
543
+ return s.strip
544
+ #return (s[0..2] == "eng" ? s[3..-1] : s)
545
+ end
546
+
547
+ ### gets id3v1 tag information from @file
548
+ ### assumes @file is pointing to char after "TAG" id
549
+ def gettag1
550
+ @hastag1 = true
551
+ @tag1["title"] = read_id3_string(30)
552
+ @tag1["artist"] = read_id3_string(30)
553
+ @tag1["album"] = read_id3_string(30)
554
+ year_t = read_id3_string(4).to_i
555
+ @tag1["year"] = year_t unless year_t == 0
556
+ comments = @file.read(30)
557
+ if comments[-2] == 0
558
+ @tag1["tracknum"] = comments[-1].to_i
559
+ comments.chop! #remove the last char
560
+ end
561
+ #@tag1["comments"] = comments.sub!(/\0.*$/, '')
562
+ @tag1["comments"] = comments.strip
563
+ @tag1["genre"] = @file.getc
564
+ @tag1["genre_s"] = GENRES[@tag1["genre"]] || ""
565
+ end
566
+
567
+ ### gets id3v2 tag information from @file
568
+ def gettag2
569
+ @file.seek(3)
570
+ version_maj, version_min, flags = @file.read(3).unpack("CCB4")
571
+ unsync, ext_header, experimental, footer = (0..3).collect { |i| flags[i].chr == '1' }
572
+ return unless [2, 3, 4].include?(version_maj)
573
+ @hastag2 = true
574
+ @tag2["version"] = "2.#{version_maj}.#{version_min}"
575
+ tag2_len = @file.get_syncsafe
576
+ case version_maj
577
+ when 2
578
+ read_id3v2_2_frames(tag2_len)
579
+ when 3,4
580
+ # seek past extended header if present
581
+ @file.seek(@file.get_syncsafe - 4, IO::SEEK_CUR) if ext_header
582
+ read_id3v2_3_frames(tag2_len)
583
+ end
584
+ tag2["length"] = @file.pos
585
+ # we should now have @file sitting at the first MPEG frame
586
+ end
587
+
588
+ ### runs thru @file one char at a time looking for best guess of first MPEG
589
+ ### frame, which should be first 0xff byte after id3v2 padding zero's
590
+ ### returns true
591
+ def v2_end?
592
+ until @file.getc == 0xff
593
+ end
594
+ @file.seek(-1, IO::SEEK_CUR)
595
+ true
596
+ end
597
+
598
+ ### reads id3 ver 2.3.x/2.4.x frames and adds the contents to @tag2 hash
599
+ ### tag2_len (fixnum) = length of entire id3v2 data, as reported in header
600
+ ### NOTE: the id3v2 header does not take padding zero's into consideration
601
+ def read_id3v2_3_frames(tag2_len)
602
+ v2end_found = false
603
+ until v2end_found # there are 2 ways to end the loop
604
+ name = @file.read(4)
605
+ if name[0] == 0
606
+ @file.seek(-4, IO::SEEK_CUR) # 1. find a padding zero,
607
+ v2end_found = v2_end? # so we seek to end of zeros
608
+ else
609
+ size = @file.get32bits
610
+ @file.seek(2, IO::SEEK_CUR) # skip flags
611
+ add_value_to_tag2(name, size)
612
+ # case name
613
+ # when /T[A-Z]+|COMM/
614
+ # data = read_id3_string(size-1)
615
+ # add_value_to_tag2(name, data)
616
+ # else
617
+ # @file.seek(size-1, IO::SEEK_CUR)
618
+ # end
619
+ v2end_found = true if @file.pos >= tag2_len # 2. reach length from header
620
+ end
621
+ end
622
+ end
623
+
624
+ ### reads id3 ver 2.2.x frames and adds the contents to @tag2 hash
625
+ ### tag2_len (fixnum) = length of entire id3v2 data, as reported in header
626
+ ### NOTE: the id3v2 header does not take padding zero's into consideration
627
+ def read_id3v2_2_frames(tag2_len)
628
+ v2end_found = false
629
+ until v2end_found
630
+ name = @file.read(3)
631
+ if name[0] == 0
632
+ @file.seek(-3, IO::SEEK_CUR)
633
+ v2end_found = v2_end?
634
+ else
635
+ size = (@file.getc << 16) + (@file.getc << 8) + @file.getc
636
+ add_value_to_tag2(name, size)
637
+ v2end_found = true if @file.pos >= tag2_len
638
+ end
639
+ end
640
+ end
641
+
642
+ ### Add data to tag2["name"]
643
+ ### read lang_encoding, decode data if unicode and
644
+ ### create an array if the key ever exists in the tag
645
+ def add_value_to_tag2(name, size)
646
+ lang_encoding = @file.getc # language encoding bit 0 for iso_8859_1, 1 for unicode
647
+ data = size == 0 ? "" : @file.read(size-1)
648
+
649
+ if lang_encoding == 1 and name[0] == ?T
650
+ require "iconv"
651
+
652
+ #strip byte-order bytes at the beginning of the unicode string if they exists
653
+ data[0..3] =~ /^[\xff\xfe]+$/ and data = data[2..-1]
654
+
655
+ data = Iconv.iconv("ISO-8859-1", "UNICODE", data)[0]
656
+ end
657
+
658
+ if @tag2.keys.include?(name)
659
+ unless @tag2[name].is_a?(Array)
660
+ keep = @tag2[name]
661
+ @tag2[name] = []
662
+ @tag2[name] << keep
663
+ end
664
+ @tag2[name] << data
665
+ else
666
+ @tag2[name] = data
667
+ end
668
+ end
669
+
670
+ ### reads through @file from current pos until it finds a valid MPEG header
671
+ ### returns the MPEG header as FixNum
672
+ def find_next_frame
673
+ # @file will now be sitting at the best guess for where the MPEG frame is.
674
+ # It should be at byte 0 when there's no id3v2 tag.
675
+ # It should be at the end of the id3v2 tag or the zero padding if there
676
+ # is a id3v2 tag.
677
+ start_pos = @file.pos
678
+ dummyproof = @file.stat.size - @file.pos
679
+ dummyproof.times do |i|
680
+ if @file.getc == 0xff
681
+ head = 0xff000000 + (@file.getc << 16) + (@file.getc << 8) + @file.getc
682
+ if check_head(head)
683
+ return head
684
+ else
685
+ @file.seek(-3, IO::SEEK_CUR)
686
+ end
687
+ end
688
+ end
689
+ raise Mp3InfoError
690
+ end
691
+
692
+ ### checks the given header to see if it is valid
693
+ ### head (fixnum) = 4 byte value to test for MPEG header validity
694
+ ### returns true if valid, false if not
695
+ def check_head(head)
696
+ return false if head & 0xffe00000 != 0xffe00000 # 11 bit MPEG frame sync
697
+ return false if head & 0x00060000 == 0x00060000 # 2 bit layer type
698
+ return false if head & 0x0000f000 == 0x0000f000 # 4 bit bitrate
699
+ return false if head & 0x0000f000 == 0x00000000 # free format bitstream
700
+ return false if head & 0x00000c00 == 0x00000c00 # 2 bit frequency
701
+ return false if head & 0xffff0000 == 0xfffe0000
702
+ true
703
+ end
704
+
705
+ end
706
+
707
+ if $0 == __FILE__
708
+ while filename = ARGV.shift
709
+ begin
710
+ info = Mp3Info.new(filename)
711
+ puts filename
712
+ #puts "MPEG #{info.mpeg_version} Layer #{info.layer} #{info.vbr ? "VBR" : "CBR"} #{info.bitrate} Kbps \
713
+ #{info.channel_mode} #{info.samplerate} Hz length #{info.length} sec."
714
+ puts info
715
+ rescue Mp3InfoError => e
716
+ puts "#{filename}\nERROR: #{e}"
717
+ end
718
+ puts
719
+ end
720
+ end