Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial commit

  • Loading branch information...
commit 4de14ffc339e62ffbcd026ec2bcaa09f3a0d9bca 0 parents
Kim Ahlström authored
Showing with 10,183 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +38 −0 Makefile.PL
  3. +97 −0 denshijisho.yml
  4. +107 −0 lib/Catalyst/Plugin/DenshiJisho/Encoding.pm
  5. +49 −0 lib/Catalyst/Plugin/DenshiJisho/Flavour.pm
  6. +48 −0 lib/Catalyst/Plugin/DenshiJisho/Static.pm
  7. +65 −0 lib/DenshiJisho.pm
  8. +54 −0 lib/DenshiJisho/Controller/About.pm
  9. +18 −0 lib/DenshiJisho/Controller/Commandline.pm
  10. +329 −0 lib/DenshiJisho/Controller/Kanji.pm
  11. +137 −0 lib/DenshiJisho/Controller/Kanji/Radicals.pm
  12. +106 −0 lib/DenshiJisho/Controller/Kanji/Similarity.pm
  13. +47 −0 lib/DenshiJisho/Controller/Links.pm
  14. +48 −0 lib/DenshiJisho/Controller/Lite.pm
  15. +105 −0 lib/DenshiJisho/Controller/Report.pm
  16. +173 −0 lib/DenshiJisho/Controller/Root.pm
  17. +291 −0 lib/DenshiJisho/Controller/Sentences.pm
  18. +29 −0 lib/DenshiJisho/Controller/Text.pm
  19. +88 −0 lib/DenshiJisho/Controller/Utilities.pm
  20. +119 −0 lib/DenshiJisho/Controller/Words.pm
  21. +670 −0 lib/DenshiJisho/I18N/en.pm
  22. +21 −0 lib/DenshiJisho/I18N/messages.pot
  23. +31 −0 lib/DenshiJisho/Importer.pm
  24. +298 −0 lib/DenshiJisho/Lingua.pm
  25. +42 −0 lib/DenshiJisho/Model/DJDB.pm
  26. +57 −0 lib/DenshiJisho/Model/RadicalInfo.pm
  27. +143 −0 lib/DenshiJisho/Model/Smartfm.pm
  28. +48 −0 lib/DenshiJisho/Model/Tags.pm
  29. +98 −0 lib/DenshiJisho/Model/Text.pm
  30. +311 −0 lib/DenshiJisho/Model/Words.pm
  31. +22 −0 lib/DenshiJisho/Schema/DJDB.pm
  32. +68 −0 lib/DenshiJisho/Schema/DJDB/Kanji.pm
  33. +46 −0 lib/DenshiJisho/Schema/DJDB/KanjiCodes.pm
  34. +40 −0 lib/DenshiJisho/Schema/DJDB/KanjiMeanings.pm
  35. +88 −0 lib/DenshiJisho/Schema/DJDB/KanjiRS.pm
  36. +50 −0 lib/DenshiJisho/Schema/DJDB/KanjiRadicals.pm
  37. +40 −0 lib/DenshiJisho/Schema/DJDB/KanjiReadings.pm
  38. +43 −0 lib/DenshiJisho/Schema/DJDB/Meanings.pm
  39. +43 −0 lib/DenshiJisho/Schema/DJDB/Radicals.pm
  40. +37 −0 lib/DenshiJisho/Schema/DJDB/Representations.pm
  41. +48 −0 lib/DenshiJisho/Schema/DJDB/WordTags.pm
  42. +59 −0 lib/DenshiJisho/Schema/DJDB/Words.pm
  43. +113 −0 lib/DenshiJisho/Schema/DJDB/WordsRS.pm
  44. +29 −0 lib/DenshiJisho/View/JSON.pm
  45. +110 −0 lib/DenshiJisho/View/TT.pm
  46. +214 −0 radical_info.txt
  47. +24 −0 root/flavour/iphone/home/index.tt
  48. +415 −0 root/flavour/iphone/includes/page.tt
  49. +41 −0 root/flavour/iphone/includes/querylog.tt
  50. +16 −0 root/flavour/j_mobile/home/index.tt
  51. +27 −0 root/flavour/j_mobile/includes/page.tt
  52. +41 −0 root/flavour/j_mobile/includes/querylog.tt
  53. +35 −0 root/flavour/j_mobile/kanji/details.tt
  54. +37 −0 root/flavour/j_mobile/kanji/details_data.tt
  55. +9 −0 root/flavour/j_mobile/kanji/details_data/classifications.tt
  56. +8 −0 root/flavour/j_mobile/kanji/details_data/codepoints.tt
  57. +31 −0 root/flavour/j_mobile/kanji/details_data/connections.tt
  58. +23 −0 root/flavour/j_mobile/kanji/details_data/indices.tt
  59. +24 −0 root/flavour/j_mobile/kanji/details_data/meanings.tt
  60. +6 −0 root/flavour/j_mobile/kanji/details_data/menu.tt
  61. +74 −0 root/flavour/j_mobile/kanji/details_data/readings.tt
  62. +36 −0 root/flavour/j_mobile/kanji/details_data/specs.tt
  63. +36 −0 root/flavour/j_mobile/kanji/form.tt
  64. +8 −0 root/flavour/j_mobile/kanji/index.tt
  65. +338 −0 root/flavour/j_mobile/kanji/radicals/index.tt
  66. +85 −0 root/flavour/j_mobile/kanji/result.tt
  67. +5 −0 root/flavour/j_mobile/sentences/form.tt
  68. +8 −0 root/flavour/j_mobile/sentences/index.tt
  69. +45 −0 root/flavour/j_mobile/sentences/result.tt
  70. +14 −0 root/flavour/j_mobile/words/form.tt
  71. +8 −0 root/flavour/j_mobile/words/index.tt
  72. +82 −0 root/flavour/j_mobile/words/result.tt
  73. +9 −0 root/flavour/www/404.tt
  74. +106 −0 root/flavour/www/about.tt
  75. +17 −0 root/flavour/www/donations_thanks.tt
  76. +112 −0 root/flavour/www/home/index.tt
  77. +5 −0 root/flavour/www/home/news.tt
  78. +88 −0 root/flavour/www/includes/macros.tt
  79. +16 −0 root/flavour/www/includes/menu.tt
  80. +125 −0 root/flavour/www/includes/page.tt
  81. +41 −0 root/flavour/www/includes/querylog.tt
  82. +36 −0 root/flavour/www/kanji/details.tt
  83. +127 −0 root/flavour/www/kanji/details.tt.tct
  84. +57 −0 root/flavour/www/kanji/details_data.tt
  85. +877 −0 root/flavour/www/kanji/details_data.tt.tct
  86. +14 −0 root/flavour/www/kanji/details_data/classifications.tt
  87. +14 −0 root/flavour/www/kanji/details_data/codepoints.tt
  88. +31 −0 root/flavour/www/kanji/details_data/connections.tt
  89. +21 −0 root/flavour/www/kanji/details_data/indices.tt
  90. +43 −0 root/flavour/www/kanji/details_data/meanings.tt
  91. +31 −0 root/flavour/www/kanji/details_data/menu.tt
  92. +84 −0 root/flavour/www/kanji/details_data/readings.tt
  93. +36 −0 root/flavour/www/kanji/details_data/specs.tt
  94. +75 −0 root/flavour/www/kanji/form.tt
  95. +82 −0 root/flavour/www/kanji/form.tt.tct
  96. +19 −0 root/flavour/www/kanji/index.tt
  97. +320 −0 root/flavour/www/kanji/radicals/index.tt
  98. +47 −0 root/flavour/www/kanji/radicals/index.tt.tct
  99. +146 −0 root/flavour/www/kanji/result.tt
  100. +49 −0 root/flavour/www/kanji/similarity/index.tt
  101. +87 −0 root/flavour/www/links.tt
  102. +34 −0 root/flavour/www/lite/header.tt.tct
  103. +44 −0 root/flavour/www/lite/page.tt
  104. +101 −0 root/flavour/www/lite/words/result.tt
  105. +69 −0 root/flavour/www/report/example.tt
  106. +12 −0 root/flavour/www/report/example_no_change.tt
  107. +12 −0 root/flavour/www/report/example_thanks.tt
  108. +12 −0 root/flavour/www/report/invalid_example.tt
  109. +12 −0 root/flavour/www/report/no_such_example.tt
  110. +19 −0 root/flavour/www/sentences/form.tt
  111. +23 −0 root/flavour/www/sentences/index.tt
  112. +89 −0 root/flavour/www/sentences/result.tt
  113. +10 −0 root/flavour/www/text/form.tt
  114. +7 −0 root/flavour/www/text/index.tt
  115. +38 −0 root/flavour/www/text/index.tt.tct
  116. +15 −0 root/flavour/www/text/result.tt
  117. +37 −0 root/flavour/www/words/form.tt
  118. +49 −0 root/flavour/www/words/form.tt.tct
  119. +19 −0 root/flavour/www/words/index.tt
  120. +41 −0 root/flavour/www/words/index.tt.tct
  121. +86 −0 root/flavour/www/words/result-row.tt
  122. +145 −0 root/flavour/www/words/result.tt
  123. +345 −0 root/flavour/www/words/result.tt.tct
  124. +9 −0 root/static/files/denshijisho_ej_opensearch.xml
  125. +9 −0 root/static/files/denshijisho_je_opensearch.xml
  126. 0  root/static/files/robots.txt
  127. +34 −0 root/static/files/sitemap.xml
  128. BIN  root/static/images/apple-touch-icon.png
  129. BIN  root/static/images/catalyst/btn_120x50_built.png
  130. BIN  root/static/images/catalyst/btn_120x50_built_shadow.png
  131. BIN  root/static/images/catalyst/btn_120x50_powered.png
  132. BIN  root/static/images/catalyst/btn_120x50_powered_shadow.png
  133. BIN  root/static/images/catalyst/btn_88x31_built.png
  134. BIN  root/static/images/catalyst/btn_88x31_built_shadow.png
  135. BIN  root/static/images/catalyst/btn_88x31_powered.png
  136. BIN  root/static/images/catalyst/btn_88x31_powered_shadow.png
  137. BIN  root/static/images/catalyst/catalyst_logo.png
  138. BIN  root/static/images/empty.gif
  139. BIN  root/static/images/favicon.ico
  140. BIN  root/static/images/feed-icon-12x12.gif
  141. BIN  root/static/images/iphone/footer.png
  142. BIN  root/static/images/iphone/pinstripes.png
  143. BIN  root/static/images/iphone/search-back.png
  144. BIN  root/static/images/iphone/search-gradient.png
  145. BIN  root/static/images/iphone/search.png
  146. BIN  root/static/images/iphone/top.png
  147. BIN  root/static/images/layout/close_icon.png
  148. BIN  root/static/images/layout/fs-back.png
  149. BIN  root/static/images/layout/intro-back-ie.png
  150. BIN  root/static/images/layout/intro-back.png
  151. BIN  root/static/images/layout/intro_bg.png
  152. BIN  root/static/images/layout/jisho_logo_tagline.png
  153. BIN  root/static/images/layout/logo.png
  154. BIN  root/static/images/layout/top_bg.png
  155. BIN  root/static/images/layout/top_bg2.png
  156. BIN  root/static/images/layout/top_bg3.png
  157. BIN  root/static/images/layout/top_bg4.png
  158. BIN  root/static/images/layout/top_faint.png
  159. BIN  root/static/images/layout/top_grey.png
  160. BIN  root/static/images/layout/top_grey2.png
  161. BIN  root/static/images/layout/top_menu_bottom.png
  162. BIN  root/static/images/layout/top_menu_bottom_long.png
  163. BIN  root/static/images/layout/top_menu_bottom_sakasama.png
  164. BIN  root/static/images/layout/topback.png
  165. BIN  root/static/images/radicals/24/11_236.gif
  166. BIN  root/static/images/radicals/24/1_1.gif
  167. BIN  root/static/images/radicals/24/1_2.gif
  168. BIN  root/static/images/radicals/24/1_3.gif
  169. BIN  root/static/images/radicals/24/1_4.gif
  170. BIN  root/static/images/radicals/24/1_5.gif
  171. BIN  root/static/images/radicals/24/1_6.gif
  172. BIN  root/static/images/radicals/24/2_10.gif
  173. BIN  root/static/images/radicals/24/2_11.gif
  174. BIN  root/static/images/radicals/24/2_12.gif
  175. BIN  root/static/images/radicals/24/2_13.gif
  176. BIN  root/static/images/radicals/24/2_14.gif
  177. BIN  root/static/images/radicals/24/2_15.gif
  178. BIN  root/static/images/radicals/24/2_16.gif
  179. BIN  root/static/images/radicals/24/2_17.gif
  180. BIN  root/static/images/radicals/24/2_18.gif
  181. BIN  root/static/images/radicals/24/2_19.gif
  182. BIN  root/static/images/radicals/24/2_20.gif
  183. BIN  root/static/images/radicals/24/2_21.gif
  184. BIN  root/static/images/radicals/24/2_22.gif
  185. BIN  root/static/images/radicals/24/2_23.gif
  186. BIN  root/static/images/radicals/24/2_24.gif
  187. BIN  root/static/images/radicals/24/2_25.gif
  188. BIN  root/static/images/radicals/24/2_26.gif
  189. BIN  root/static/images/radicals/24/2_27.gif
  190. BIN  root/static/images/radicals/24/2_28.gif
  191. BIN  root/static/images/radicals/24/2_29.gif
  192. BIN  root/static/images/radicals/24/2_30.gif
  193. BIN  root/static/images/radicals/24/2_31.gif
  194. BIN  root/static/images/radicals/24/2_32.gif
  195. BIN  root/static/images/radicals/24/2_33.gif
  196. BIN  root/static/images/radicals/24/2_34.gif
  197. BIN  root/static/images/radicals/24/2_35.gif
  198. BIN  root/static/images/radicals/24/2_36.gif
  199. BIN  root/static/images/radicals/24/2_7.gif
  200. BIN  root/static/images/radicals/24/2_8.gif
  201. BIN  root/static/images/radicals/24/2_9.gif
  202. BIN  root/static/images/radicals/24/3_37.gif
  203. BIN  root/static/images/radicals/24/3_38.gif
  204. BIN  root/static/images/radicals/24/3_39.gif
  205. BIN  root/static/images/radicals/24/3_40.gif
  206. BIN  root/static/images/radicals/24/3_41.gif
  207. BIN  root/static/images/radicals/24/3_42.gif
  208. BIN  root/static/images/radicals/24/3_43.gif
  209. BIN  root/static/images/radicals/24/3_44.gif
  210. BIN  root/static/images/radicals/24/3_45.gif
  211. BIN  root/static/images/radicals/24/3_46.gif
  212. BIN  root/static/images/radicals/24/3_47.gif
  213. BIN  root/static/images/radicals/24/3_48.gif
  214. BIN  root/static/images/radicals/24/3_49.gif
  215. BIN  root/static/images/radicals/24/3_50.gif
  216. BIN  root/static/images/radicals/24/3_51.gif
  217. BIN  root/static/images/radicals/24/3_52.gif
  218. BIN  root/static/images/radicals/24/3_53.gif
  219. BIN  root/static/images/radicals/24/3_54.gif
  220. BIN  root/static/images/radicals/24/3_55.gif
  221. BIN  root/static/images/radicals/24/3_56.gif
  222. BIN  root/static/images/radicals/24/3_57.gif
  223. BIN  root/static/images/radicals/24/3_58.gif
  224. BIN  root/static/images/radicals/24/3_59.gif
  225. BIN  root/static/images/radicals/24/3_60.gif
  226. BIN  root/static/images/radicals/24/3_61.gif
  227. BIN  root/static/images/radicals/24/3_62.gif
  228. BIN  root/static/images/radicals/24/3_63.gif
  229. BIN  root/static/images/radicals/24/3_64.gif
  230. BIN  root/static/images/radicals/24/3_65.gif
  231. BIN  root/static/images/radicals/24/3_66.gif
  232. BIN  root/static/images/radicals/24/3_67.gif
  233. BIN  root/static/images/radicals/24/3_68.gif
  234. BIN  root/static/images/radicals/24/3_69.gif
  235. BIN  root/static/images/radicals/24/3_70.gif
  236. BIN  root/static/images/radicals/24/3_71.gif
  237. BIN  root/static/images/radicals/24/3_72.gif
  238. BIN  root/static/images/radicals/24/3_73.gif
  239. BIN  root/static/images/radicals/24/3_74.gif
  240. BIN  root/static/images/radicals/24/3_75.gif
  241. BIN  root/static/images/radicals/24/3_76.gif
  242. BIN  root/static/images/radicals/24/3_77.gif
  243. BIN  root/static/images/radicals/24/3_78.gif
  244. BIN  root/static/images/radicals/24/3_79.gif
  245. BIN  root/static/images/radicals/24/3_80.gif
  246. BIN  root/static/images/radicals/24/4_100.gif
  247. BIN  root/static/images/radicals/24/4_101.gif
  248. BIN  root/static/images/radicals/24/4_102.gif
  249. BIN  root/static/images/radicals/24/4_103.gif
  250. BIN  root/static/images/radicals/24/4_104.gif
  251. BIN  root/static/images/radicals/24/4_105.gif
  252. BIN  root/static/images/radicals/24/4_106.gif
  253. BIN  root/static/images/radicals/24/4_107.gif
  254. BIN  root/static/images/radicals/24/4_108.gif
  255. BIN  root/static/images/radicals/24/4_109.gif
  256. BIN  root/static/images/radicals/24/4_110.gif
  257. BIN  root/static/images/radicals/24/4_111.gif
  258. BIN  root/static/images/radicals/24/4_112.gif
  259. BIN  root/static/images/radicals/24/4_113.gif
  260. BIN  root/static/images/radicals/24/4_114.gif
  261. BIN  root/static/images/radicals/24/4_115.gif
  262. BIN  root/static/images/radicals/24/4_116.gif
  263. BIN  root/static/images/radicals/24/4_117.gif
  264. BIN  root/static/images/radicals/24/4_118.gif
  265. BIN  root/static/images/radicals/24/4_119.gif
  266. BIN  root/static/images/radicals/24/4_120.gif
  267. BIN  root/static/images/radicals/24/4_121.gif
  268. BIN  root/static/images/radicals/24/4_122.gif
  269. BIN  root/static/images/radicals/24/4_123.gif
  270. BIN  root/static/images/radicals/24/4_81.gif
  271. BIN  root/static/images/radicals/24/4_82.gif
  272. BIN  root/static/images/radicals/24/4_83.gif
  273. BIN  root/static/images/radicals/24/4_84.gif
  274. BIN  root/static/images/radicals/24/4_85.gif
  275. BIN  root/static/images/radicals/24/4_86.gif
  276. BIN  root/static/images/radicals/24/4_87.gif
  277. BIN  root/static/images/radicals/24/4_88.gif
  278. BIN  root/static/images/radicals/24/4_89.gif
  279. BIN  root/static/images/radicals/24/4_90.gif
  280. BIN  root/static/images/radicals/24/4_91.gif
  281. BIN  root/static/images/radicals/24/4_92.gif
  282. BIN  root/static/images/radicals/24/4_93.gif
  283. BIN  root/static/images/radicals/24/4_94.gif
  284. BIN  root/static/images/radicals/24/4_95.gif
  285. BIN  root/static/images/radicals/24/4_96.gif
  286. BIN  root/static/images/radicals/24/4_97.gif
  287. BIN  root/static/images/radicals/24/4_98.gif
  288. BIN  root/static/images/radicals/24/4_99.gif
  289. BIN  root/static/images/radicals/24/5_124.gif
  290. BIN  root/static/images/radicals/24/5_125.gif
  291. BIN  root/static/images/radicals/24/5_126.gif
  292. BIN  root/static/images/radicals/24/5_127.gif
  293. BIN  root/static/images/radicals/24/5_128.gif
  294. BIN  root/static/images/radicals/24/5_129.gif
  295. BIN  root/static/images/radicals/24/5_130.gif
  296. BIN  root/static/images/radicals/24/5_131.gif
  297. BIN  root/static/images/radicals/24/5_132.gif
  298. BIN  root/static/images/radicals/24/5_133.gif
  299. BIN  root/static/images/radicals/24/5_134.gif
  300. BIN  root/static/images/radicals/24/5_135.gif
Sorry, we could not display the entire diff because too many files (505) changed.
4 .gitignore
@@ -0,0 +1,4 @@
+*_local.yml
+work/data
+*.sql
+*.tmp
38 Makefile.PL
@@ -0,0 +1,38 @@
+use inc::Module::Install;
+
+name 'DenshiJisho';
+all_from 'lib/DenshiJisho.pm';
+
+requires 'Catalyst::Runtime' => '5.7011';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Catalyst::Plugin::Log::Colorful';
+requires 'Catalyst::Plugin::I18N';
+requires 'YAML'; # This should reflect the config file format you've chosen
+ # See Catalyst::Plugin::ConfigLoader for supported formats
+
+requires 'Catalyst::View::TT' => 0;
+requires 'Catalyst::View::JSON' => 0;
+requires 'DBIx::Class' => 0.08010;
+
+requires 'Lingua::EN::Numbers' => 0;
+requires 'Text::MeCab' => 0;
+requires 'Text::Balanced' => 0;
+requires 'Catalyst::Model::DBIC::Schema' => 0;
+requires 'HTTP::MobileAgent' => 0;
+requires 'Catalyst::Plugin::FillInForm' => 0;
+requires 'DBIx::Class::QueryLog' => 0;
+requires 'DBIx::Class::QueryLog::Analyzer' => 0;
+requires 'List::Compare' => 0;
+requires 'Array::Unique' => 0;
+requires 'Unicode::Japanese' => 0;
+requires 'Data::Page::Balanced' => 0;
+requires 'Lingua::JA::Romanize::Kana' => 0;
+requires 'Locale::Maketext' => 0;
+
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;
97 denshijisho.yml
@@ -0,0 +1,97 @@
+---
+View::JSON:
+ expose_stash: json
+authentication:
+ dbic:
+ password_field: password
+ user_class: DenshiJisho::Model::DJUserDB::User
+ user_field: username
+sources:
+ words:
+ names:
+ jmdict: General Dictionary
+ jmnedict: Names and Places
+ 4jword3: Yojijukugo
+ engscidic: Engineering and Science
+ classical: Classical Japanese
+ smartfm: Smart.fm
+ order:
+ - jmdict
+ - jmnedict
+ - engscidic
+ - 4jword3
+ - classical
+ - smartfm
+ sentences:
+ names:
+ tanaka: Tanaka
+ smartfm: Smart.fm
+ order:
+ - tanaka
+ - smartfm
+kanjidic:
+ cp_type:
+ jis208: JIS X 0208-1997 kuten code
+ jis212: JIS X 0212-1990 kuten code
+ jis213: JIS X 0213-2000 kuten code
+ ucs: Unicode hex code
+ code_points_sort_order:
+ - ucs
+ - jis208
+ - jis212
+ - jis213
+ dr_type:
+ busy_people: Japanese for Busy People
+ crowley: '<span title="Dale Crowley">The Kanji Way to Japanese Language Power</span>'
+ gakken: A New Dictionary of Kanji Usage
+ halpern_kkld: '<span title="Jack Halpern">Kanji Learners Dictionary</span>'
+ halpern_njecd: '<span title="Jack Halpern">New Japanese English Character Dictionary</span>'
+ heisig: '<span title="James Heisig">Remembering The Kanji</span>'
+ henshall: '<span title="Kenneth G. Henshall">A Guide To Remembering Japanese Characters</span>'
+ henshall3: '<span title="Henshall, Seeley and De Groot">A Guide To Reading and Writing Japanese 3rd ed.</span>'
+ jf_cards: '<span title="Max Hodges and Tomoko Okazaki">Japanese Kanji Flashcards</span>'
+ kanji_in_context: '<span title="Nishiguchi and Kono">Kanji in Context</span>'
+ kodansha_compact: Kodansha Compact Kanji Guide
+ nelson_c: '<span title="Andrew Nelson">Classic Nelson</span>'
+ nelson_n: '<span title="John Haig">New Nelson</span>'
+ oneill_kk: "<span title=\"P.G. O'Neill\">Essential Kanji</span>"
+ oneill_names: "<span title=\"P.G. O'Neill\">Japanese Names</span>"
+ sakade: '<span title="Florence Sakade">A Guide To Reading and Writing Japanese</span>'
+ sh_kk: '<span title="Spahn and Hadamitzky">Kanji and Kana</span>'
+ tutt_cards: '<span title="Alexander Kask">Tuttle Kanji Cards</span>'
+ maniette: '<span title="Yves Maniette">Les Kanji dans la tête</span>'
+ qc_type:
+ deroo: '<span title="Joseph De Roo">2001 Kanji</span>'
+ four_corner: Four corner code
+ sh_desc: '<span title="Spahn and Hadamitzky">The Kanji Dictionary</span>'
+ skip: '<span title="System of Kanji Indexing by Patterns">SKIP code</span>'
+ codes_sort_order:
+ - skip
+ - sh_desc
+ - four_corner
+ - deroo
+ indices_sort_order:
+ - sakade
+ - henshall3
+ - henshall
+ - gakken
+ - nelson_c
+ - moro
+ - oneill_kk
+ - busy_people
+ - jf_cards
+ - oneill_names
+ - sh_kk
+ - kanji_in_context
+ - halpern_kkld
+ - kodansha_compact
+ - halpern_njecd
+ - nelson_n
+ - heisig
+ - crowley
+ - tutt_cards
+ - maniette
+name: DenshiJisho
+result_limit: 25
+default_view: TT
+site_name: Jisho.org
107 lib/Catalyst/Plugin/DenshiJisho/Encoding.pm
@@ -0,0 +1,107 @@
+package Catalyst::Plugin::DenshiJisho::Encoding;
+
+use strict;
+use Encode;
+
+our $VERSION = '0.1';
+
+sub finalize {
+ my $c = shift;
+ my $charset;
+ my $meta_charset;
+
+ unless ( $c->response->body ) {
+ return $c->NEXT::finalize;
+ }
+
+ unless ( $c->response->content_type =~ m|^text/html| ) {
+ return $c->NEXT::finalize;
+ }
+
+ if ( $c->flavour eq 'j_mobile' ) {
+ $meta_charset = $charset = 'shift_jis';
+ }
+ else {
+ $charset = 'utf8';
+ $meta_charset = 'utf-8';
+ }
+
+ my $content_type = $c->response->content_type;
+ $content_type =~ s/\;\s*$//;
+ $content_type =~ s/\;*\s*charset\s*\=.*$//i;
+ $content_type .= sprintf("; charset=%s", $meta_charset );
+ $c->response->content_type($content_type);
+
+ $c->response->body( encode($charset, $c->response->body) );
+
+ $c->NEXT::finalize;
+}
+
+sub prepare_parameters {
+ my $c = shift;
+ my $charset;
+
+ $c->NEXT::prepare_parameters(@_);
+
+ if ( $c->flavour eq 'j_mobile' ) {
+ $charset = 'shift_jis';
+ }
+ else {
+ $charset = 'utf8';
+ }
+
+ for my $value ( values %{ $c->request->{parameters} } ) {
+
+ if ( ref $value && ref $value ne 'ARRAY' ) {
+ next;
+ }
+
+ $_ = decode($charset, $_) for ( ref($value) ? @{$value} : $value );
+ }
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Catalyst::Plugin::DenshiJisho::Encoding
+
+=head1 SYNOPSIS
+
+ use Catalyst qw[DenshiJisho::Encoding];
+
+
+=head1 DESCRIPTION
+
+Based on L<Catalyst::Plugin::Unicode> and L<Catalyst::Plugin::Charsets::Japanese>. Does UTF-8 on everything except Japanese cell phones. Also properly sets the charset in the content-type header.
+
+=head1 OVERLOADED METHODS
+
+=over 4
+
+=item finalize
+
+Encodes body into UTF-8 or Shift-JIS octets.
+
+=item prepare_parameters
+
+Decodes parameters into a sequence of logical characters.
+
+=back
+
+=head1 SEE ALSO
+
+L<Encode>, L<Catalyst>.
+
+=head1 AUTHOR
+
+Kim Ahlström, C<kim.ahlstrom@gmail.com>
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
49 lib/Catalyst/Plugin/DenshiJisho/Flavour.pm
@@ -0,0 +1,49 @@
+package Catalyst::Plugin::DenshiJisho::Flavour;
+
+use warnings;
+use strict;
+use HTTP::MobileAgent;
+
+use base qw/Class::Accessor::Fast/;
+
+our $VERSION = '0.1';
+
+__PACKAGE__->mk_accessors('flavour');
+
+sub prepare_parameters {
+ my $c = shift;
+
+ $c->NEXT::prepare_parameters(@_);
+
+ #
+ # Get flavour from URL param
+ #
+ if ( $c->req->param('flavour') ) {
+ $c->flavour( $c->req->param('flavour') );
+ return;
+ }
+
+ #
+ # Detect the flavour by other means
+ #
+ my $mobile_agent = HTTP::MobileAgent->new($c->req->user_agent);
+
+ if ( !$mobile_agent->is_non_mobile
+ || $c->req->header('host') =~ m{ ^k\. }ix
+ ) {
+ $c->flavour('j_mobile');
+ }
+ elsif ( $c->req->header('host') =~ m{ ^iphone\. }ix
+ || $c->req->user_agent =~ m{ Mobile/\S+\s+ Safari/ }ix
+ ) {
+ $c->flavour('iphone');
+ }
+ elsif ( $c->req->header('host') =~ m{ ^www\. }ix ) {
+ $c->flavour('www');
+ }
+ else {
+ $c->flavour('www');
+ }
+}
+
+1;
48 lib/Catalyst/Plugin/DenshiJisho/Static.pm
@@ -0,0 +1,48 @@
+package Catalyst::Plugin::DenshiJisho::Static;
+
+use strict;
+use warnings;
+use base qw/Catalyst::Plugin::Static::Simple/;
+
+our $VERSION = '0.1';
+
+sub prepare_action {
+ my $c = shift;
+ my $path = $c->req->path;
+
+ $c->SUPER::prepare_action(@_);
+
+ if ( $path =~ m{^/?(.*\.)v[0-9.]+\.(css|js|gif|png|jpg)$}i ) {
+ $c->res->redirect("/$1$2");
+ }
+}
+
+1;
+
+=head1 NAME
+
+Catalyst::Plugin::DenshiJisho::Static
+
+=head1 SYNOPSIS
+
+ use Catalyst qw[DenshiJisho::Static];
+
+
+=head1 DESCRIPTION
+
+Subclasses L<Catalyst::Plugin::Static::Simple> to redirect versioned static URL's as in the http://www.thinkvitamin.com/features/webapps/serving-javascript-fast article, this is to complement the mod_rewrite magic done in Apache, so the dev server can serve the versioned files as well.
+
+=head1 SEE ALSO
+
+L<Catalyst::Plugin::Static::Simple>, L<Catalyst>.
+
+=head1 AUTHOR
+
+Kim Ahlström, C<kim.ahlstrom@gmail.com>
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
65 lib/DenshiJisho.pm
@@ -0,0 +1,65 @@
+package DenshiJisho;
+
+use strict;
+use Cwd;
+use Catalyst qw/
+ StackTrace
+ ConfigLoader
+ FillInForm
+ I18N
+
+ Session
+ Session::State::Cookie
+ Session::Store::File
+
+ DenshiJisho::Static
+ DenshiJisho::Encoding
+ DenshiJisho::Flavour
+/;
+use Carp qw/croak carp/;
+use Data::Dumper;
+
+BEGIN {
+ use lib __PACKAGE__->path_to('work', 'lib')->stringify;
+}
+
+our $VERSION = '3';
+
+__PACKAGE__->setup();
+
+# Load tags
+__PACKAGE__->model('Tags')->load_tags( __PACKAGE__ );
+__PACKAGE__->model('RadicalInfo')->load_radical_info( __PACKAGE__ );
+
+sub ce {
+ my $c = shift;
+ my @context = caller;
+ my $first = q(-) x 10 . q( ) . $context[0] . ', line ' . $context[2] . q( ) . q(-) x 50;
+ my $output = qq(\n+$first\n| ) . $context[1] . qq(\n|\n);
+
+ foreach my $arg (@_) {
+ my $dump = Dumper $arg;
+ foreach my $line (split(qq(\n), $dump)) {
+ $output .= qq(| $line\n);
+ }
+ $output .= qq(|\n);
+ }
+
+ $output .= q(+) . q(-) x length($first) . qq(\n\n);
+ carp $output;
+}
+
+sub persistent_form {
+ my( $c, $name, $controls) = @_;
+
+ if ( keys %{$c->req->params} ) {
+ foreach my $control (@{$controls}) {
+ $c->session->{persistent_form}->{$name}->{$control} = $c->req->params->{$control};
+ }
+ }
+ else {
+ $c->req->params( Catalyst::Utils::merge_hashes($c->req->params, $c->session->{persistent_form}->{$name}) );
+ }
+}
+
+1;
54 lib/DenshiJisho/Controller/About.pm
@@ -0,0 +1,54 @@
+package DenshiJisho::Controller::About;
+
+use strict;
+use base 'Catalyst::Base';
+
+=head1 NAME
+
+DenshiJisho::Controller::About - Catalyst component
+
+=head1 SYNOPSIS
+
+See L<DenshiJisho>
+
+=head1 DESCRIPTION
+
+Catalyst component.
+
+=head1 METHODS
+
+=over 4
+
+=item default
+
+=cut
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'about.tt';
+ $c->stash->{page} = 'about';
+}
+
+sub donations_thanks : Path('donations/thanks') {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'donations_thanks.tt';
+ $c->stash->{page} = 'about';
+}
+
+=back
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
+
+1;
18 lib/DenshiJisho/Controller/Commandline.pm
@@ -0,0 +1,18 @@
+package DenshiJisho::Controller::Commandline;
+
+use strict;
+use base 'Catalyst::Base';
+
+sub jap : Regex('^\s*/?\s*jap(?:anese)?\s+(.*)$') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect( $c->req->base . "words?dict=edict&jap=" . ${$c->req->snippets}[0] );
+}
+
+sub eng : Regex('^\s*/?\s*eng(?:lish)?\s+(.*)$') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect( $c->req->base . "words?dict=edict&eng=" . ${$c->req->snippets}[0] );
+}
+
+1;
329 lib/DenshiJisho/Controller/Kanji.pm
@@ -0,0 +1,329 @@
+package DenshiJisho::Controller::Kanji;
+
+use strict;
+use base 'Catalyst::Base';
+use Encode;
+use utf8;
+use List::Compare;
+use Data::Dumper;
+use SQL::Abstract;
+use Array::Unique;
+use Unicode::Japanese;
+use URI::Escape qw(uri_escape uri_escape_utf8);
+use DenshiJisho::Lingua;
+
+=head1 NAME
+
+DenshiJisho::Controller::Kanji - Catalyst component
+
+=head1 SYNOPSIS
+
+See L<DenshiJisho>
+
+=head1 DESCRIPTION
+
+Catalyst component.
+
+=head1 METHODS
+
+=over 4
+
+=item default
+
+=cut
+
+sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ # We got parameters for kanji details, which takes precedence
+ if ( $c->req->params->{reading} =~ m/\p{Han}/) {
+ if ( $c->flavour eq 'j_mobile' ) {
+ # Convert to shift_jis for j_mobile
+ $c->res->redirect('/kanji/details/'
+ . encode('shift_jis', $c->req->params->{reading})
+ );
+ }
+ elsif ( $c->flavour eq 'iphone' ) {
+ $c->res->redirect('/kanji/details/'
+ . encode_utf8($c->req->params->{reading})
+ . '?flavour=iphone'
+ );
+ }
+ else {
+ $c->res->redirect('/kanji/details/'
+ . uri_escape_utf8($c->req->params->{reading})
+ );
+ }
+ $c->detach;
+ }
+
+ $c->persistent_form('kanji', [qw(rt ct mt jy_only)]);
+
+ # Convenient stuff
+ $c->req->params->{reading} = romaji_to_kana($c->req->params->{reading})
+ if $c->req->params->{rt} eq 'japanese';
+
+ $c->req->params->{code} = fullwidth_to_halfwidth($c->req->params->{code});
+ $c->req->params->{reading} = fullwidth_to_halfwidth($c->req->params->{reading});
+ $c->req->params->{meaning} = fullwidth_to_halfwidth($c->req->params->{meaning});
+
+ # Check limit
+ $c->stash->{limit} = $c->req->param('nolimit') eq 'on' ? 0 : $c->config->{result_limit};
+
+ # Smart switching to the correct rt, mt and ct based on what the user inputs
+ if ($c->req->params->{code} =~ m/^\d+-\d+-\d+$/) {
+ # SKIP
+ $c->req->params->{ct} = 'skip';
+ }
+
+ 1;
+}
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'kanji/index.tt';
+ $c->stash->{page} = 'kanji';
+
+ return unless $c->req->param('reading')
+ || $c->req->param('meaning')
+ || $c->req->param('code');
+
+ my ($kanji, $pager) = $c->model('DJDB::Kanji')->find_by_all({
+ rt => $c->req->params->{rt},
+ ct => $c->req->params->{ct},
+ mt => $c->req->params->{mt},
+ reading => $c->req->params->{reading},
+ code => $c->req->params->{code},
+ meaning => $c->req->params->{meaning},
+ jy_only => $c->req->params->{jy_only} eq 'on' ? 1 : 0,
+ page => $c->req->param('page') || 1,
+ limit => $c->stash->{limit},
+ });
+
+ # If no kanji found, suggest other searches
+ if ($pager->total_entries == 0) {
+ if ($c->req->params->{rt} eq 'japanese') {
+ my $key = $c->req->params->{reading};
+ my $key_uj = Unicode::Japanese->new($key);
+ my $key_euc = $key_uj->euc;
+ my $key_sjis = $key_uj->sjis;
+
+ $c->stash->{suggest}->{key} = $key;
+ $c->stash->{suggest}->{key_euc} = uri_escape( $key_euc );
+ $c->stash->{suggest}->{key_sjis} = uri_escape( $key_sjis );
+ }
+ }
+
+ # Show the result
+ if ( $c->flavour() eq q{iphone} ) {
+ $c->stash->{json} = {
+ type => 'kanji',
+ kanji => $self->inflate_result_for_iphone($c),
+ total => $pager->total_entries,
+ pager => {
+ last_page => $c->stash->{pager}->last_page,
+ current_page => $c->stash->{pager}->current_page,
+ }
+ };
+
+ $c->stash(current_view => 'JSON');
+ }
+ else {
+ $c->stash->{pager} = $pager;
+ $c->stash->{kanji} = $kanji;
+ $c->stash->{template} = 'kanji/result.tt';
+ }
+
+}
+
+sub inflate_result_for_iphone {
+ my ( $self, $c ) = @_;
+
+ my @result;
+
+ foreach my $kanji ( @{$c->stash->{result}->{kanjis}} ) {
+ my $local = {};
+
+ $local->{literal} = $kanji->literal();
+ $local->{grade} = defined $kanji->grade() ? $kanji->grade() : 0;
+
+ foreach my $reading ( $kanji->readings()->all() ) {
+ if ( $reading->r_type() eq 'ja_on' || $reading->r_type() eq 'ja_kun' ) {
+ push @{$local->{readings}}, $reading->reading();
+ }
+ }
+
+ foreach my $meaning ( $kanji->meanings()->all() ) {
+ if ( $meaning->m_lang() eq 'en' ) {
+ push @{$local->{meanings}}, $meaning->meaning();
+ }
+ }
+
+ foreach my $count ( $kanji->stroke_counts()->all() ) {
+ push @{$local->{strokes}}, $count->stroke_count();
+ }
+
+ push @result, $local;
+ }
+
+ return \@result;
+}
+
+sub details : Local {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'kanji/details.tt';
+ $c->stash->{page} = 'kanji';
+
+ return unless scalar @{$c->req->args} > 0;
+
+ # Get the kanji we are to get details on
+ my $unprocessed = $c->req->arguments->[0];
+
+ if ( $c->flavour eq 'j_mobile' ) {
+ $unprocessed = decode('shift-jis', $unprocessed);
+ }
+ else {
+ $unprocessed = decode_utf8($unprocessed);
+ }
+
+ $unprocessed =~ s/\P{Han}//g;
+ $unprocessed = substr $unprocessed, 0, 20;
+
+ return unless $unprocessed;
+
+ # Get from DB
+ my @kanji = split //, $unprocessed;
+ my @result = $c->model('DJDB::Kanji')->search(
+ {
+ kanji => [@kanji]
+ }
+ );
+
+ # Order like in the URL
+ foreach my $kanji (@kanji) {
+ foreach my $found (@result) {
+ if ($found->kanji eq $kanji) {
+ push @{$c->stash->{kanji}}, $found;
+ }
+ }
+ }
+
+ $c->req->params->{reading} = join('', map {$_->kanji} @{$c->stash->{kanji}});
+
+ # Get the variants and parts
+ foreach my $kanji (@{$c->stash->{kanji}}) {
+ # Parts
+ my @parts = $c->model('DJDB::Kanji')->find({kanji => $kanji->kanji})->radicals;
+ push @{$c->stash->{kanji_parts}->{$kanji->id}}, @parts;
+
+ # Variants
+ foreach my $variant ( @{$kanji->data->{variants}} ) {
+ my $variants = $c->model('DJDB::KanjiCodes')->search(
+ {
+ section => 'codepoint',
+ type => $variant->{var_type},
+ value => $variant->{content},
+ }
+ );
+
+ push @{$c->stash->{kanji_variants}->{$kanji->id}}, $variants->first;
+ }
+ }
+
+ if ( $c->flavour eq q{iphone} ) {
+ $c->stash->{json} = {
+ type => 'details',
+ kanji => $self->inflate_details_for_iphone($c),
+ };
+ $c->stash(current_view => 'JSON');
+ }
+}
+
+sub inflate_details_for_iphone {
+ my ( $self, $c ) = @_;
+
+ my @result;
+
+ foreach my $kanji ( @{$c->stash->{kanji}} ) {
+ my $local = {};
+
+ $local->{literal} = $kanji->literal();
+ $local->{grade} = defined $kanji->grade() ? $kanji->grade() : 0;
+ $local->{id} = $kanji->id();
+
+ foreach my $reading ( $kanji->readings()->search({},{order_by=>'normalized'})->all() ) {
+ push @{$local->{readings}}, {
+ reading => $reading->reading(),
+ r_type => $reading->r_type(),
+ normalized => $reading->normalized(),
+ };
+ }
+
+ foreach my $radical ( $kanji->radicals()->all() ) {
+ push @{$local->{radicals}}, {
+ rad_type => $radical->rad_type(),
+ rad_value => $radical->rad_value(),
+ glyph => @{$c->config->{radicals}}[$radical->rad_value()]->{glyph},
+ };
+ }
+
+ foreach my $qc ( $kanji->query_codes()->all() ) {
+ if ( $qc->qc_type() eq 'skip' ) {
+ push @{$local->{skip_codes}}, $qc->q_code();
+ }
+ }
+
+ foreach my $cp ( $kanji->codepoints()->all() ) {
+ if ( $cp->cp_type() eq 'ucs' ) {
+ push @{$local->{unicodes}}, $cp->cp_value();
+ }
+ }
+
+ foreach my $part ( @{$c->stash->{kanji_parts}->{$kanji->id}} ) {
+ push @{$local->{parts}}, $part->radical();
+ }
+
+ foreach my $variant ( @{$c->stash->{kanji_variants}->{$kanji->id}} ) {
+ next if $variant == undef;
+ push @{$local->{variants}}, $variant->kanji()->literal();
+ }
+
+ foreach my $nanori ( $kanji->nanoris_rs()->search({},{order_by=>'nanori'})->all() ) {
+ push @{$local->{nanoris}}, $nanori->nanori();
+ }
+
+ foreach my $meaning ( $kanji->meanings_rs()->search({},{order_by=>'meaning'})->all() ) {
+ push @{$local->{'meanings_' . $meaning->m_lang()}}, $meaning->meaning();
+ }
+
+ foreach my $count ( $kanji->stroke_counts_rs()->search({},{order_by=>'stroke_count'})->all() ) {
+ push @{$local->{strokes}}, $count->stroke_count();
+ }
+
+ foreach my $freq ( $kanji->frequencies()->all() ) {
+ push @{$local->{frequencies}}, $freq->frequency();
+ }
+
+ push @result, $local;
+ }
+
+ return \@result;
+}
+
+=back
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
+
+1;
137 lib/DenshiJisho/Controller/Kanji/Radicals.pm
@@ -0,0 +1,137 @@
+package DenshiJisho::Controller::Kanji::Radicals;
+
+use strict;
+use base 'Catalyst::Base';
+use Encode;
+use Data::Dumper;
+use List::MoreUtils qw/uniq/;
+use utf8;
+
+=head1 NAME
+
+DenshiJisho::Controller::Kanji::Radicals - Catalyst component
+
+=head1 SYNOPSIS
+
+See L<DenshiJisho>
+
+=head1 DESCRIPTION
+
+Catalyst component.
+
+=head1 METHODS
+
+=over 4
+
+=item default
+
+=cut
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'kanji/radicals/index.tt';
+ $c->stash->{page} = 'kanji_by_rad';
+}
+
+sub find : Local {
+ my ( $self, $c ) = @_;
+ my @radicals;
+
+ # Check for radicals
+ if ($c->req->param("rad")) {
+ @radicals = $c->req->param("rad");
+ }
+ else {
+ $c->stash->{json} = {reset => 1};
+ $c->stash(current_view => 'JSON');
+ return;
+ }
+
+ # Extract the radical numbers
+ @radicals = map {m/_([^_]*?)$/; $1} @radicals;
+
+ # Search
+ my $kr_rs = $c->model('DJDB::KanjiRadicals')->search(
+ {
+ 'radical.number' => {'-in' => \@radicals},
+ },
+ {
+ join => [qw/radical/],
+ order_by => ['kanji_strokes', 'IF((kanji_grade = 9), 10, (10 - kanji_grade))'],
+ group_by => [qw/kanji_id/],
+ having => {'count(number)' => {'>' => $#radicals}},
+ }
+ );
+
+ my @ids = map {$_->kanji_id} $kr_rs->all;
+ my $kanji_rs = $c->model('DJDB::Kanji')->search({
+ id => {'-in' => \@ids }
+ }, {
+ order_by => ['FIELD(id, '. join(',', @ids) .')']
+ });
+
+ # Create the output
+ my $output = {};
+ my $count = 0;
+ my $current_strokes = 0;
+ my $last_strokes = 0;
+ my @kanjis;
+ my @bunch;
+ my @kanji_ids;
+
+ foreach my $kanji ( $kanji_rs->all ) {
+ $count++;
+ my $ord = ord($kanji->kanji);
+ my $strokes = $kanji->strokes;
+ my $grade = $kanji->grade;
+ push @kanjis, $kanji->kanji;
+ push @kanji_ids, $kanji->id;
+
+ # Make sure strokes is a number, so it doesn't get stringified in the JSON conversion
+ push( @{$output->{kanji}}, {
+ ord => $ord,
+ grade => "g$grade",
+ strokes => $strokes+0,
+ });
+ }
+
+ # Find valid radicals for continued searching
+ my $validation = "";
+ if ( scalar(@kanjis) > 0 ) {
+ my $valid_rs = $c->model('DJDB::KanjiRadicals')->search_related('radical', {
+ kanji_id => {'-in' => \@kanji_ids},
+ }, {
+ group_by => [qw/radical_id/],
+ });
+
+ my @valid_radicals;
+ foreach my $radical ( $valid_rs->all ) {
+ $output->{is_valid_radical}->{"rad_" . $radical->strokes . '_' . $radical->number} = 1;
+ }
+ }
+
+ # Deploy output
+ my $notice = $count == 0 ? "" : "(Jōyō kanji are colored darker)";
+ $output->{count} = $count;
+ $output->{notice} = $notice;
+
+ $c->stash->{json} = $output;
+ $c->stash(current_view => 'JSON');
+}
+
+=back
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
+
+1;
106 lib/DenshiJisho/Controller/Kanji/Similarity.pm
@@ -0,0 +1,106 @@
+package DenshiJisho::Controller::Kanji::Similarity;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+use utf8;
+use Data::Dumper;
+use Encode;
+
+=head1 NAME
+
+DenshiJisho::Controller::Kanji::Similarity - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub default : Private {
+ my ( $self, $c, @args ) = @_;
+
+ $c->stash->{template} = 'kanji/similarity/index.tt';
+ $c->stash->{page} = 'kanji_by_similarity';
+
+
+ #
+ # Get selection and base kanji
+ #
+ my @selected = splice @args, 2;
+ my @base = qw/ 行 走 送 回 使 国 語 夏 一 広 病 気 般 /;
+
+ # Create choices
+ my $i = 0;
+ my %choices = map {
+ $_ => {
+ kanji => $_,
+ id => 'sim_' . $i++,
+ selected => 0,
+ }
+ } @base;
+
+ # Mark selected kanji
+ foreach my $selected (@selected) {
+ $selected = decode_utf8($selected);
+ $choices{$selected}->{selected} = 1;
+ }
+
+
+ #
+ # Search if we have selected kanji
+ #
+ if ( scalar @selected > 0 ) {
+ my $radicals_rs = $c->model('DJDB')->resultset('Radk')->search(
+ {
+ kanji => \@selected,
+ },
+ {
+ group_by => 'radical',
+ }
+ );
+
+ my @radicals = map { $_->radical } $radicals_rs->all();
+
+ my $kanji_rs = $c->model('DJDB')->resultset('Radk')->search(
+ {
+ radical => \@radicals,
+ },
+ {
+ group_by => 'kanji',
+ order_by => [qw/kanji_strokes kanji_grade_sort/],
+ having => {'count(kanji)' => {'>' => $#selected}},
+ }
+ );
+
+ @{$c->stash->{result}->{kanji}} = $kanji_rs->all();
+ $c->stash->{result}->{count} = scalar @{$c->stash->{result}->{kanji}};
+ }
+
+
+ #
+ # Output
+ #
+ $c->stash->{choices} = \%choices;
+}
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;
47 lib/DenshiJisho/Controller/Links.pm
@@ -0,0 +1,47 @@
+package DenshiJisho::Controller::Links;
+
+use strict;
+use base 'Catalyst::Base';
+
+=head1 NAME
+
+DenshiJisho::Controller::Links - Catalyst component
+
+=head1 SYNOPSIS
+
+See L<DenshiJisho>
+
+=head1 DESCRIPTION
+
+Catalyst component.
+
+=head1 METHODS
+
+=over 4
+
+=item default
+
+=cut
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'links.tt';
+ $c->stash->{page} = 'links';
+}
+
+=back
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
+
+1;
48 lib/DenshiJisho/Controller/Lite.pm
@@ -0,0 +1,48 @@
+package DenshiJisho::Controller::Lite;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+=head1 NAME
+
+DenshiJisho::Controller::Lite - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub lite_setup : PathPart('lite') Chained('/') Args(1) {
+ my ( $self, $c, $path ) = @_;
+
+ $c->stash->{is_lite} = 1;
+
+ if ($path eq 'words') {
+ $c->forward("/words/index");
+ }
+ else {
+ $c->res->status(404);
+ }
+}
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;
105 lib/DenshiJisho/Controller/Report.pm
@@ -0,0 +1,105 @@
+package DenshiJisho::Controller::Report;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+=head1 NAME
+
+DenshiJisho::Controller::Report - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub example : Local {
+ my ( $self, $c, $example_id, $action ) = @_;
+
+ $c->stash->{page} = 'sentences';
+
+ # Make sure the example_id is numeric
+ if ($example_id !~ /^\d+$/) {
+ $c->stash->{template} = 'report/invalid_example.tt';
+ return;
+ }
+
+ if ($action eq 'submit') {
+ my $eid = $c->req->param('eid');
+ my $org_jap = $c->req->param('org_jap');
+ my $jap = $c->req->param('jap');
+ my $org_eng = $c->req->param('org_eng');
+ my $eng = $c->req->param('eng');
+ my $comment = $c->req->param('comment');
+ my $name = $c->req->param('name');
+ my $email = $c->req->param('email');
+
+ $org_jap =~ s/[\n\r]//g;
+ $jap =~ s/[\n\r]//g;
+ $org_eng =~ s/[\n\r]//g;
+ $eng =~ s/[\n\r]//g;
+ $comment =~ s/\r//g;
+
+ # Check so there actually was a change, this might also hinder spam-robots
+ if ($org_jap eq $jap && $org_eng eq $eng) {
+ $c->stash->{template} = 'report/example_no_change.tt';
+ return;
+ }
+
+ # Send the edited example
+ $c->controller('Utilities')->email({
+ to => 'kim.ahlstrom@gmail.com',
+ cc => 'Jim Breen <jwb@mail.csse.monash.edu.au>, Francis Bond <bond@ieee.org>',
+ subject => '[Jisho.org] Example Correction',
+ body => <<EOM,
+Database id: $eid
+
+Original Japanese: $org_jap
+Corrected Japanese: $jap
+
+Original English: $org_eng
+Corrected English: $eng
+
+Name: $name
+E-mail $email
+Comment: $comment
+
+EOM
+ });
+
+ $c->stash->{template} = 'report/example_thanks.tt';
+ }
+ else {
+ # Display the example for editing
+ $c->stash->{the_example} = $c->model('DJDB')->resultset('Old::Tanaka')->find( $example_id );
+
+ if ($c->stash->{the_example}) {
+ $c->stash->{template} = 'report/example.tt';
+ }
+ else {
+ $c->stash->{template} = 'report/no_such_example.tt';
+ }
+ }
+}
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;
173 lib/DenshiJisho/Controller/Root.pm
@@ -0,0 +1,173 @@
+package DenshiJisho::Controller::Root;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+use Encode;
+use HTML::Entities;
+use DenshiJisho::Lingua;
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config->{namespace} = '';
+
+sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ # Check limit
+ if (defined $c->req->params->{nolimit} && $c->req->params->{nolimit} eq 'on') {
+ $c->stash->{query}->{limit} = 0;
+ }
+ else {
+ $c->stash->{query}->{limit} = $c->config->{result_limit};
+ }
+
+ $c->languages([qw(en)]);
+}
+
+sub default : Private {
+ my ( $self, $c ) = @_;
+
+ $c->res->status(404);
+}
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'home/index.tt';
+ $c->stash->{page} = 'home';
+}
+
+#
+# Favicon
+#
+sub favicon : Path('/favicon.ico') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect($c->uri_for('/static/images/favicon.ico'));
+ $c->detach;
+}
+
+#
+# Apple touch icon
+#
+sub touch : Path('/apple-touch-icon.png') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect($c->uri_for('/static/images/apple-touch-icon.png'));
+ $c->detach;
+}
+
+#
+# Sitemap
+#
+sub sitemap : Path('/sitemap.xml') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect($c->uri_for('/static/files/sitemap.xml'));
+ $c->detach;
+}
+
+#
+# Robots.txt
+#
+sub robots : Path('/robots.txt') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect($c->uri_for('/static/files/robots.txt'));
+ $c->detach;
+}
+
+#
+# Google verification file
+#
+sub google_verification_file : Path('/google74fb886d33a1c79f.html') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect($c->uri_for('/static/files/google74fb886d33a1c79f.html'));
+ $c->detach;
+}
+
+# ----------------------------
+# Handle old links
+# ----------------------------
+sub old_about : Regex('^about\.pl') {
+ my ( $self, $c ) = @_;
+
+ $c->res->redirect("/about");
+}
+
+sub old_index : Regex('^index\.pl') {
+ my ( $self, $c ) = @_;
+
+ # Determine the page
+ my $page;
+ if ($c->req->param('dict') eq 'ex') {
+ $page = 'sentences';
+ }
+ else {
+ $page = 'words';
+ }
+
+ # Get the search params
+ my $eng = $c->req->param('eng');
+ my $jap = $c->req->param('jap');
+ my $dict = $c->req->param('dict');
+ my $common = $c->req->param('common');
+
+ # Redirect
+ $c->res->redirect("/$page?translation=$eng&japanese=$jap&source=$dict&common=$common");
+}
+
+# ----------------------------
+# Render page
+# ----------------------------
+sub end : Private {
+ my ( $self, $c ) = @_;
+
+ $c->forward('render');
+
+ # Automatically populate the form if we have a template
+ if ( ($c->flavour() eq 'www' || $c->flavour() eq 'j_mobile') && defined $c->stash->{template} ) {
+ $c->fillform($c->req->params);
+ }
+
+ # Print QueryLog to the log if requested
+ if ( $c->log->is_info ) {
+ my $ql = $c->model('DJDB')->querylog;
+ my $total = sprintf('%0.6f', $ql->time_elapsed);
+ my $qcount = $ql->count;
+
+ $c->log->info('Total SQL Time: ' . $total);
+ $c->log->info('Total Queries: ' . $qcount);
+
+ if ( $qcount > 0 ) {
+ my $i = 0;
+ my $ana = DBIx::Class::QueryLog::Analyzer->new({ querylog => $ql });
+
+ foreach my $q (@{$ana->get_sorted_queries}) {
+ $c->log->info(
+ sprintf("%0.6fs, %i%%, %s", $q->time_elapsed, ($q->time_elapsed / $total) * 100, $q->sql)
+ );
+ }
+ }
+ }
+}
+
+sub render : ActionClass('RenderView') {
+ my ( $self, $c ) = @_;
+
+ # Custom 404 page
+ if( $c->res->status == 404 ) {
+ $c->stash->{template} = '404.tt';
+ }
+
+ # Set the flavour if we have a template
+ if ( $c->stash->{template} ) {
+ $c->stash->{template} = q{flavour/} . $c->flavour() . q{/} . $c->stash->{template};
+ }
+}
+
+1;
291 lib/DenshiJisho/Controller/Sentences.pm
@@ -0,0 +1,291 @@
+package DenshiJisho::Controller::Sentences;
+
+use strict;
+use base 'Catalyst::Base';
+use Unicode::Japanese;
+use URI::Escape;
+use Data::Dumper;
+use Encode;
+use DenshiJisho::Lingua;
+
+sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ # Support legacy param names
+ $c->req->params->{japanese} = $c->req->param('jap') if $c->req->param('jap');
+ $c->req->params->{translation} = $c->req->param('eng') if $c->req->param('eng');
+ $c->req->params->{source} = $c->req->param('dict') if $c->req->param('dict');
+
+ # Convenience conversions
+ $c->req->params->{japanese} = romaji_to_kana(fullwidth_to_halfwidth($c->req->param('japanese')));
+ $c->req->params->{english} = fullwidth_to_halfwidth($c->req->param('english'));
+
+ # Check dictionary
+ $c->req->params->{source} = q(tanaka) unless $c->config->{sources}->{sentences}->{names}->{ $c->req->param('source') };
+}
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{page} = 'sentences';
+ $c->stash->{template} = 'sentences/index.tt';
+
+ if ($c->req->param('eng') || $c->req->param('jap')) {
+
+ # If no sentences found, suggest other searches
+ if ($c->stash->{result}->{total} == 0) {
+ my $key = $c->stash->{query}->{form}->{jap};
+ my $key_uj = Unicode::Japanese->new($key);
+ my $key_euc = $key_uj->euc();
+ my $key_sjis = $key_uj->sjis();
+
+ $c->stash->{suggest}->{key} = $key;
+ $c->stash->{suggest}->{key_euc} = uri_escape( $key_euc );
+ $c->stash->{suggest}->{key_sjis} = uri_escape( $key_sjis );
+
+ if ($key =~ m/\p{Han}/) {
+ $c->stash->{suggest}->{key_has_kanji} = 1;
+ }
+ }
+
+ #
+ # Display the sentences
+ #
+ if ( $c->flavour() eq q{iphone} ) {
+ $c->stash->{json} = {
+ sentences => $c->stash->{result}->{sentences},
+ total => $c->stash->{search}->{total},
+ pager => {
+ last_page => $c->stash->{pager}->last_page(),
+ current_page => $c->stash->{pager}->current_page(),
+ }
+ };
+ $c->stash(current_view => 'JSON');
+ }
+ else {
+ $c->stash->{result}->{total} = $c->stash->{search}->{total};
+ $c->stash->{template} = 'sentences/result.tt';
+ }
+ }
+}
+
+sub find : Private {
+ my ( $self, $c ) = @_;
+
+ # =========================================
+ # = Just say no to wildcard-only searches =
+ # =========================================
+ if ( $c->stash->{query}->{form}->{jap} =~ m/^[*?\s]+$/
+ || $c->stash->{query}->{form}->{eng} =~ m/^[*?\s]+$/ ) {
+ $c->stash->{search}->{sentences} = [];
+ $c->stash->{search}->{total} = 0;
+ return;
+ }
+
+ #
+ # Return if no search terms given
+ #
+ if ($c->stash->{query}->{sql}->{jap} eq '' && $c->stash->{query}->{sql}->{eng} eq '') {
+ $c->stash->{search}->{sentences} = [];
+ $c->stash->{search}->{total} = 0;
+ return;
+ }
+
+ #
+ # The search terms
+ #
+ my @where;
+ my $temp_where;
+ my $temp_where2;
+ my @jap;
+ my @eng;
+
+ if ($c->stash->{query}->{sql}->{jap}) {
+ foreach my $token (@{$c->stash->{query}->{sql}->{jap_tokens}}) {
+ push @jap, {'-like', $token};
+ }
+
+ $temp_where->{japanese} = [ -and => @jap];
+ $temp_where2->{japanese_reading} = [ -and => @jap];
+ }
+
+ if ($c->stash->{query}->{sql}->{eng}) {
+ foreach my $token_container (@{$c->stash->{query}->{sql}->{eng_tokens}}) {
+ my $token_for_like = $token_container->{for_like};
+ my $token_for_regexp = $token_container->{for_regexp};
+
+ # Always search with LIKE. This seberely limits the number of rows that the REGEXP has to look through
+ # and thus improves performancy heavily
+ push @eng, {'-like', $token_for_like};
+
+ # Search with regexp if we are searching for just the word and no substrings of
+ # larger words. The number must be 2 since the empty string is quoted by MySQL to ''
+ if( length $token_container->{for_regexp} > 2 ) {
+ push @eng, {'-regexp', $token_for_regexp};
+ }
+ }
+
+ $temp_where->{english} = [-and => @eng];
+ $temp_where2->{english} = [-and => @eng];
+ }
+
+ @where = (
+ { -and => [%$temp_where] },
+ { -and => [%$temp_where2]}
+ );
+
+ #
+ # Attributes
+ #
+ my %attrs = (
+ order_by => "CHAR_LENGTH(japanese)",
+ );
+
+ #
+ # Search
+ #
+ my @result = $c->model('DJDB')->resultset('Old::Tanaka')->search(\@where, \%attrs);
+ $c->stash->{search}->{total} = scalar @result;
+
+ # ===========
+ # = Page it =
+ # ===========
+ my $page = $c->req->param('page') ? $c->req->param('page') : 1;
+ my $pager = Data::Page::Balanced->new({
+ current_page => $page,
+ total_entries => scalar @result,
+ entries_per_page => $c->stash->{query}->{limit} ? $c->stash->{query}->{limit} : $c->config->{'result_limit'},
+ });
+ $c->stash->{pager} = $pager;
+
+ @result = splice(@result, ($pager->current_page() - 1) * $pager->entries_per_page(), $pager->entries_on_this_page());
+
+ #
+ # Give it back
+ #
+ $c->stash->{search}->{sentences} = \@result;
+}
+
+sub fix_up : Private {
+ my ( $self, $c ) = @_;
+
+ my $sid = 1;
+ my $i = 0;
+ my $flavour = $c->flavour;
+
+ foreach my $row (@{$c->stash->{search}->{sentences}}) {
+ #
+ # Sentence object
+ #
+ my $sentence = {
+ 'english' => $row->english,
+ 'japanese' => $row->japanese,
+ 'japanese_reading' => $row->japanese_reading,
+ 'words' => $row->words,
+ 'eid' => $row->eid,
+ };
+
+ my $english = $sentence->{english};
+ my $japanese = $sentence->{japanese};
+ my $key = $japanese;
+
+ #
+ # Tag, if any
+ #
+ my $tag = "";
+ my @tags;
+ $english = $sentence->{english};
+ if( $english =~ s/\[M\]$// ) {
+ push @tags, "Male speech";
+ }
+
+ if( $english =~ s/\[F\]$// ) {
+ push @tags, "Female speech";
+ }
+
+ if( $english =~ s/\[Proverb\]$// ) {
+ push @tags, "Proverb";
+ }
+
+ $tag .= join ", ", @tags;
+
+ #
+ # Special for the www and iphone flavour
+ #
+ if ( $flavour eq 'www' || $flavour eq 'iphone' ) {
+ #
+ # Link words that are in edict (the words column in the examples db)
+ #
+ my %used_words = (); # So we don't link the same word several times
+
+ foreach my $word ( split(/\s/, $sentence->{words}) ) {
+ my( $reading, $inflected );
+
+ # Remove readings, senses and inflected
+ if ($word =~ s/ \( (.*) \) //x) {
+ $reading = $1;
+ }
+
+ if ($word =~ s/ { (.*) } //x) {
+ $inflected = $1;
+ }
+
+ $word =~ s/ \[\d+\] //x;
+
+ $inflected ||= $word;
+
+ # Skip if we've already done this word
+ next if $used_words{$word};
+
+ if ( $flavour eq 'www' ) {
+ $japanese =~ s/ (?: (< [^>]*? $inflected [^<]*?>) | ($inflected) ) /
+ if ($1) {
+ $1;
+ }
+ else {
+ "<a href=\"\/words?jap=$word;dict=edict\">$inflected<\/a>";
+ }
+ /egix;
+ }
+ else {
+ $japanese =~ s/ (?: (< [^>]*? $inflected [^<]*?>) | ($inflected) ) /
+ if ($1) {
+ $1;
+ }
+ else {
+ "<a href=\"#words\" onClick=\"iPhone.doRelatedSearch('words', '$inflected')\">$inflected<\/a>";
+ }
+ /egix;
+ }
+
+ # Mark word as used
+ $used_words{$word} = 1;
+ }
+
+ #
+ # Mark the search terms
+ #
+ foreach my $token ( @{$c->stash->{query}->{regexp}->{jap_tokens}} ) {
+ $japanese = $c->controller('Utilities')->mark_search_terms($japanese, $token);
+ }
+
+ foreach my $token ( @{$c->stash->{query}->{regexp}->{eng_tokens}} ) {
+ $english = $c->controller('Utilities')->mark_search_terms($english, $token);
+ }
+ }
+
+ #
+ # Add the sentence pair
+ #
+ push @{$c->stash->{result}->{sentences}}, {
+ japanese => $japanese,
+ english => $english,
+ sid => $sid++,
+ tag => $tag,
+ key => $key,
+ eid => $sentence->{eid},
+ };
+ }
+}
+
+1;
29 lib/DenshiJisho/Controller/Text.pm
@@ -0,0 +1,29 @@
+package DenshiJisho::Controller::Text;
+
+use strict;
+use base 'Catalyst::Base';
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'text/index.tt';
+ $c->stash->{page} = 'text';
+
+ $c->stash->{query}->{form}->{text} = $c->req->param('text');
+
+ #
+ # We got parameters, so we are searching for something
+ #
+ if ($c->req->param('text')) {
+ $c->stash->{parsed_text} = $c->model('Text')->parse_text($c->req->param('text'), $c);
+ $c->stash->{template} = 'text/result.tt';
+ }
+}
+
+sub url : Local {
+ my ( $self, $c ) = @_;
+
+
+}
+
+1;
88 lib/DenshiJisho/Controller/Utilities.pm
@@ -0,0 +1,88 @@
+package DenshiJisho::Controller::Utilities;
+
+use strict;
+use base 'Catalyst::Base';
+use Encode;
+use Data::Dumper;
+use utf8;
+
+=head1 NAME
+
+DenshiJisho::Controller::Utilities - Catalyst component
+
+=head1 SYNOPSIS
+
+See L<DenshiJisho>
+
+=head1 DESCRIPTION
+
+Catalyst component.
+
+=head1 METHODS
+
+=over 4
+
+=item romaji_to_kana
+
+=cut
+
+sub mark_search_terms : Private {
+ my( $self, $text, $token ) = @_;
+
+ #
+ # Mark the search terms
+ # First with non-alphanumeric so we don't match our own html later on
+ # Like if we search for "span span", the second will match the first
+ #
+ $text =~ s/ (?: (< [^>]*? $token [^<]*?>) | ($token) ) /
+ if ($1) {
+ $1;
+ }
+ else {
+ "<=$2=>";
+ }
+ /egix;
+
+ # Replace intermittent match notation with spans
+ $text =~ s{<=}{<span class="match">}g;
+ $text =~ s{=>}{</span>}g;
+
+ return $text;
+}
+
+sub email {
+ my ($c, $options) = @_;
+
+ my $mail = sprintf(<<EOM,
+From: Jisho.org <kim.ahlstrom\@gmail.com>
+To: $options->{to}
+CC: $options->{cc}
+Content-Type: text/plain; charset=UTF-8; format=flowed
+X-DJ-Process: $$
+Subject: $options->{subject}
+
+$options->{body}
+EOM
+);
+
+ my $sendmail;
+ open($sendmail, '|-', 'sendmail -i ' . $options->{to});
+ print $sendmail $mail;
+ close($sendmail);
+}
+
+=back
+
+
+=head1 AUTHOR
+
+Kim Ahlström
+
+=head1 LICENSE
+
+This library is free software . You can redistribute it and/or modify
+it under the same terms as perl itself.
+
+=cut
+
+1;
119 lib/DenshiJisho/Controller/Words.pm
@@ -0,0 +1,119 @@
+package DenshiJisho::Controller::Words;
+
+use strict;
+use base 'Catalyst::Base';
+use Unicode::Japanese;
+use utf8;
+use URI::Escape;
+use Encode;
+use DenshiJisho::Lingua;
+use Data::Dumper;
+use Carp;
+
+sub auto : Private {
+ my ( $self, $c ) = @_;
+
+ # Support legacy param names
+ $c->req->params->{japanese} = $c->req->param('jap') if $c->req->param('jap');
+ $c->req->params->{translation} = $c->req->param('eng') if $c->req->param('eng');
+ $c->req->params->{source} = $c->req->param('dict') if $c->req->param('dict');
+
+ # Save state
+ $c->persistent_form('words', [qw(common romaji translation_language)]);
+
+ # Convenience conversions
+ $c->req->params->{japanese} = romaji_to_kana(fullwidth_to_halfwidth($c->req->param('japanese')));
+ $c->req->params->{translation} = fullwidth_to_halfwidth($c->req->param('translation'));
+
+ # Set the display language to the same as the search language
+ $c->req->params->{translation_language} = q(eng) unless $c->req->param('translation_language');
+ $c->stash->{display_language} = $c->req->param('display_language') || $c->req->param('translation_language');
+
+ # Check limit
+ $c->stash->{limit} = $c->req->param('nolimit') eq 'on' ? 0 : $c->config->{result_limit};
+
+ # Check dictionary
+ $c->req->params->{source} = q(jmdict) unless $c->config->{sources}->{words}->{names}->{ $c->req->param('source') };
+}
+
+sub index : Private {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{page} = 'words';
+ $c->stash->{template} = 'words/index.tt';
+
+ return unless $c->req->param('translation') || $c->req->param('japanese');
+ $c->stash->{is_search} = 1;
+
+ my ($words, $pager, $dictionary_counts) = $c->model('DJDB::Words')->find_words_with_dictionary_counts({
+ source => $c->req->param('source'),
+ japanese => $c->req->param('japanese'),
+ gloss => $c->req->param('translation'),
+ language => $c->req->param('translation_language'),
+ common_only => $c->req->param('common') eq 'on' ? 1 : 0,
+ page => $c->req->param('page') || 1,
+ limit => $c->stash->{limit},
+ });
+
+ $c->stash->{dictionary_counts} = $dictionary_counts;
+ $c->stash->{pager} = $pager;
+
+ # Check with MeCab if it's an inflected word
+ my $lemmatized = lemmatize_japanese($c->stash->{form}->{j});
+ $c->stash->{suggest}->{deinflected} = ($lemmatized && $lemmatized eq $c->req->param('japanese')) ? q() : $lemmatized;
+
+ # If no words found, suggest other searches
+ my $total = $dictionary_counts->{$c->req->param('source')};
+ if ($total == 0) {
+ my $key = $c->req->param('japanese');
+ my $key_uj = Unicode::Japanese->new($key);
+ my $key_euc = $key_uj->euc;
+ my $key_sjis = $key_uj->sjis;
+
+ $c->stash->{suggest}->{key} = $key;
+ $c