narou 2.8.3.1 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of narou might be problematic. Click here for more details.

Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +77 -0
  3. data/Gemfile +2 -1
  4. data/README.md +69 -86
  5. data/lib/command/convert.rb +10 -11
  6. data/lib/command/init.rb +2 -2
  7. data/lib/command/list.rb +12 -3
  8. data/lib/command/setting.rb +20 -5
  9. data/lib/command/update.rb +0 -3
  10. data/lib/command/version.rb +2 -3
  11. data/lib/command/web.rb +58 -7
  12. data/lib/converterbase.rb +7 -24
  13. data/lib/database.rb +7 -6
  14. data/lib/device.rb +15 -1
  15. data/lib/device/ibunko.rb +1 -2
  16. data/lib/device/library/mac.rb +7 -0
  17. data/lib/downloader.rb +44 -21
  18. data/lib/extension.rb +25 -0
  19. data/lib/helper.rb +4 -3
  20. data/lib/html.rb +18 -2
  21. data/lib/narou.rb +20 -0
  22. data/lib/novelconverter.rb +11 -35
  23. data/lib/novelinfo.rb +19 -8
  24. data/lib/novelsetting.rb +0 -6
  25. data/lib/version.rb +1 -1
  26. data/lib/web/appserver.rb +134 -15
  27. data/lib/web/public/favicon.ico +0 -0
  28. data/lib/web/public/resources/common.ui.js +2 -2
  29. data/lib/web/public/resources/narou.library.js +657 -91
  30. data/lib/web/public/resources/narou.queue.js +1 -1
  31. data/lib/web/public/resources/narou.ui.js +253 -102
  32. data/lib/web/public/resources/sprintf.js +245 -0
  33. data/lib/web/pushserver.rb +14 -3
  34. data/lib/web/views/_about.haml +188 -0
  35. data/lib/web/views/{diff_list.haml → _diff_list.haml} +0 -0
  36. data/lib/web/views/{edit_replace_txt.haml → _edit_replace_txt.haml} +4 -4
  37. data/lib/web/views/_rebooting.haml +18 -0
  38. data/lib/web/views/bookmarklet/download.js.erb +2 -2
  39. data/lib/web/views/bookmarklet/insert_button.js.erb +1 -1
  40. data/lib/web/views/edit_menu.haml +223 -0
  41. data/lib/web/views/help.haml +29 -12
  42. data/lib/web/views/index.haml +99 -88
  43. data/lib/web/views/layout.haml +5 -3
  44. data/lib/web/views/notepad.haml +39 -0
  45. data/lib/web/views/novels/setting.haml +15 -5
  46. data/lib/web/views/settings.haml +2 -2
  47. data/lib/web/views/style.scss +72 -21
  48. data/lib/web/views/widget/download.haml +3 -2
  49. data/lib/web/views/widget/drag_and_drop.haml +3 -2
  50. data/lib/web/views/widget/notepad.haml +44 -0
  51. data/narou.gemspec +75 -6
  52. data/narou.rb +8 -14
  53. data/preset/ncode.syosetu.com/n5115cq/converter.rb +30 -0
  54. data/preset/ncode.syosetu.com/n7594ct/converter.rb +37 -0
  55. data/preset/ncode.syosetu.com/n8725k/converter.rb +1 -1
  56. data/preset/vertical_font.css +0 -10
  57. data/spec/generator/convert_spec_gen.rb +2 -0
  58. data/template/novel.txt.erb +3 -0
  59. data/webnovel/https.syosetu.org.yaml +1 -1
  60. data/webnovel/kakuyomu.jp.yaml +82 -0
  61. data/webnovel/ncode.syosetu.com.yaml +1 -0
  62. data/webnovel/syosetu.org.yaml +1 -1
  63. metadata +89 -12
  64. data/lib/web/views/about.haml +0 -85
  65. data/preset/DMincho.ttf +0 -0
@@ -0,0 +1,245 @@
1
+ /* globals window, exports, define */
2
+
3
+ (function(window) {
4
+ 'use strict'
5
+
6
+ var re = {
7
+ not_string: /[^s]/,
8
+ not_bool: /[^t]/,
9
+ not_type: /[^T]/,
10
+ not_primitive: /[^v]/,
11
+ number: /[diefg]/,
12
+ numeric_arg: /bcdiefguxX/,
13
+ json: /[j]/,
14
+ not_json: /[^j]/,
15
+ text: /^[^\x25]+/,
16
+ modulo: /^\x25{2}/,
17
+ placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,
18
+ key: /^([a-z_][a-z_\d]*)/i,
19
+ key_access: /^\.([a-z_][a-z_\d]*)/i,
20
+ index_access: /^\[(\d+)\]/,
21
+ sign: /^[\+\-]/
22
+ }
23
+
24
+ function sprintf() {
25
+ var key = arguments[0], cache = sprintf.cache
26
+ if (!(cache[key] && cache.hasOwnProperty(key))) {
27
+ cache[key] = sprintf.parse(key)
28
+ }
29
+ return sprintf.format.call(null, cache[key], arguments)
30
+ }
31
+
32
+ sprintf.format = function(parse_tree, argv) {
33
+ var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ''
34
+ for (i = 0; i < tree_length; i++) {
35
+ node_type = get_type(parse_tree[i])
36
+ if (node_type === 'string') {
37
+ output[output.length] = parse_tree[i]
38
+ }
39
+ else if (node_type === 'array') {
40
+ match = parse_tree[i] // convenience purposes only
41
+ if (match[2]) { // keyword argument
42
+ arg = argv[cursor]
43
+ for (k = 0; k < match[2].length; k++) {
44
+ if (!arg.hasOwnProperty(match[2][k])) {
45
+ throw new Error(sprintf('[sprintf] property "%s" does not exist', match[2][k]))
46
+ }
47
+ arg = arg[match[2][k]]
48
+ }
49
+ }
50
+ else if (match[1]) { // positional argument (explicit)
51
+ arg = argv[match[1]]
52
+ }
53
+ else { // positional argument (implicit)
54
+ arg = argv[cursor++]
55
+ }
56
+
57
+ if (re.not_type.test(match[8]) && re.not_primitive.test(match[8]) && get_type(arg) == 'function') {
58
+ arg = arg()
59
+ }
60
+
61
+ if (re.numeric_arg.test(match[8]) && (get_type(arg) != 'number' && isNaN(arg))) {
62
+ throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)))
63
+ }
64
+
65
+ if (re.number.test(match[8])) {
66
+ is_positive = arg >= 0
67
+ }
68
+
69
+ switch (match[8]) {
70
+ case 'b':
71
+ arg = parseInt(arg, 10).toString(2)
72
+ break
73
+ case 'c':
74
+ arg = String.fromCharCode(parseInt(arg, 10))
75
+ break
76
+ case 'd':
77
+ case 'i':
78
+ arg = parseInt(arg, 10)
79
+ break
80
+ case 'j':
81
+ arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
82
+ break
83
+ case 'e':
84
+ arg = match[7] ? parseFloat(arg).toExponential(match[7]) : parseFloat(arg).toExponential()
85
+ break
86
+ case 'f':
87
+ arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
88
+ break
89
+ case 'g':
90
+ arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
91
+ break
92
+ case 'o':
93
+ arg = arg.toString(8)
94
+ break
95
+ case 's':
96
+ arg = String(arg)
97
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
98
+ break
99
+ case 't':
100
+ arg = String(!!arg)
101
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
102
+ break
103
+ case 'T':
104
+ arg = get_type(arg)
105
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
106
+ break
107
+ case 'u':
108
+ arg = parseInt(arg, 10) >>> 0
109
+ break
110
+ case 'v':
111
+ arg = arg.valueOf()
112
+ arg = (match[7] ? arg.substring(0, match[7]) : arg)
113
+ break
114
+ case 'x':
115
+ arg = parseInt(arg, 10).toString(16)
116
+ break
117
+ case 'X':
118
+ arg = parseInt(arg, 10).toString(16).toUpperCase()
119
+ break
120
+ }
121
+ if (re.json.test(match[8])) {
122
+ output[output.length] = arg
123
+ }
124
+ else {
125
+ if (re.number.test(match[8]) && (!is_positive || match[3])) {
126
+ sign = is_positive ? '+' : '-'
127
+ arg = arg.toString().replace(re.sign, '')
128
+ }
129
+ else {
130
+ sign = ''
131
+ }
132
+ pad_character = match[4] ? match[4] === '0' ? '0' : match[4].charAt(1) : ' '
133
+ pad_length = match[6] - (sign + arg).length
134
+ pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : '') : ''
135
+ output[output.length] = match[5] ? sign + arg + pad : (pad_character === '0' ? sign + pad + arg : pad + sign + arg)
136
+ }
137
+ }
138
+ }
139
+ return output.join('')
140
+ }
141
+
142
+ sprintf.cache = {}
143
+
144
+ sprintf.parse = function(fmt) {
145
+ var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
146
+ while (_fmt) {
147
+ if ((match = re.text.exec(_fmt)) !== null) {
148
+ parse_tree[parse_tree.length] = match[0]
149
+ }
150
+ else if ((match = re.modulo.exec(_fmt)) !== null) {
151
+ parse_tree[parse_tree.length] = '%'
152
+ }
153
+ else if ((match = re.placeholder.exec(_fmt)) !== null) {
154
+ if (match[2]) {
155
+ arg_names |= 1
156
+ var field_list = [], replacement_field = match[2], field_match = []
157
+ if ((field_match = re.key.exec(replacement_field)) !== null) {
158
+ field_list[field_list.length] = field_match[1]
159
+ while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
160
+ if ((field_match = re.key_access.exec(replacement_field)) !== null) {
161
+ field_list[field_list.length] = field_match[1]
162
+ }
163
+ else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
164
+ field_list[field_list.length] = field_match[1]
165
+ }
166
+ else {
167
+ throw new SyntaxError("[sprintf] failed to parse named argument key")
168
+ }
169
+ }
170
+ }
171
+ else {
172
+ throw new SyntaxError("[sprintf] failed to parse named argument key")
173
+ }
174
+ match[2] = field_list
175
+ }
176
+ else {
177
+ arg_names |= 2
178
+ }
179
+ if (arg_names === 3) {
180
+ throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
181
+ }
182
+ parse_tree[parse_tree.length] = match
183
+ }
184
+ else {
185
+ throw new SyntaxError("[sprintf] unexpected placeholder")
186
+ }
187
+ _fmt = _fmt.substring(match[0].length)
188
+ }
189
+ return parse_tree
190
+ }
191
+
192
+ var vsprintf = function(fmt, argv, _argv) {
193
+ _argv = (argv || []).slice(0)
194
+ _argv.splice(0, 0, fmt)
195
+ return sprintf.apply(null, _argv)
196
+ }
197
+
198
+ /**
199
+ * helpers
200
+ */
201
+ function get_type(variable) {
202
+ if (typeof variable === 'number') {
203
+ return 'number'
204
+ }
205
+ else if (typeof variable === 'string') {
206
+ return 'string'
207
+ }
208
+ else {
209
+ return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()
210
+ }
211
+ }
212
+
213
+ var preformattedPadding = {
214
+ '0': ['', '0', '00', '000', '0000', '00000', '000000', '0000000'],
215
+ ' ': ['', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
216
+ '_': ['', '_', '__', '___', '____', '_____', '______', '_______'],
217
+ }
218
+ function str_repeat(input, multiplier) {
219
+ if (multiplier >= 0 && multiplier <= 7 && preformattedPadding[input]) {
220
+ return preformattedPadding[input][multiplier]
221
+ }
222
+ return Array(multiplier + 1).join(input)
223
+ }
224
+
225
+ /**
226
+ * export to either browser or node.js
227
+ */
228
+ if (typeof exports !== 'undefined') {
229
+ exports.sprintf = sprintf
230
+ exports.vsprintf = vsprintf
231
+ }
232
+ else {
233
+ window.sprintf = sprintf
234
+ window.vsprintf = vsprintf
235
+
236
+ if (typeof define === 'function' && define.amd) {
237
+ define(function() {
238
+ return {
239
+ sprintf: sprintf,
240
+ vsprintf: vsprintf
241
+ }
242
+ })
243
+ }
244
+ }
245
+ })(typeof window === 'undefined' ? this : window);
@@ -19,7 +19,7 @@ module Narou
19
19
  attr_reader :accepted_domains, :connections
20
20
 
21
21
  def accepted_domains=(domains)
22
- @accepted_domains = domains.kind_of?(Array) ? domains : [domains]
22
+ @accepted_domains = Array(domains)
23
23
  end
24
24
 
25
25
  def initialize
@@ -100,12 +100,23 @@ module Narou
100
100
  # echo 以外のイベントは履歴に保存しない
101
101
  message = data[:echo]
102
102
  if message
103
- @history.push(message)
104
- @history.shift
103
+ stack_to_history(message)
105
104
  end
106
105
  rescue JSON::GeneratorError => e
107
106
  STDERR.puts $@.shift + ": #{e.message} (#{e.class})"
108
107
  end
108
+
109
+ def stack_to_history(message)
110
+ if message == "." && @history[-1] =~ /\A\.+\z/
111
+ # 進行中を表す .... の出力でヒストリーが消費されるのを防ぐため、
112
+ # 連続した . は一つにまとめる
113
+ @history[-1] << "."
114
+ else
115
+ @history.push(message)
116
+ @history.shift
117
+ end
118
+ end
119
+
109
120
  end
110
121
  end
111
122
 
@@ -0,0 +1,188 @@
1
+ :javascript
2
+ +function() {
3
+ var action = new Narou.Action;
4
+
5
+ function getCurrentVersion() {
6
+ var dfd = jQuery.Deferred();
7
+ $.ajax({
8
+ type: "GET",
9
+ url: "/api/version/current.json",
10
+ dataType: "json",
11
+ success: function(json) {
12
+ $("#current-version").text(json.version);
13
+ dfd.resolve(json.version);
14
+ },
15
+ error: function() {
16
+ dfd.reject();
17
+ }
18
+ });
19
+ return dfd;
20
+ }
21
+
22
+ function getLatestVersion() {
23
+ var dfd = jQuery.Deferred();
24
+ $.ajax({
25
+ type: "GET",
26
+ url: "/api/version/latest.json",
27
+ dataType: "json",
28
+ success: function(json) {
29
+ $("#latest-version").text(json.version);
30
+ dfd.resolve(json.version);
31
+ },
32
+ error: function() {
33
+ dfd.reject();
34
+ }
35
+ });
36
+ return dfd;
37
+ }
38
+
39
+ function updateButtonHandler() {
40
+ var dfd = jQuery.Deferred();
41
+ $("#version-update-button").one("click", function(e) {
42
+ e.preventDefault();
43
+ $(this).hide();
44
+ $("#fail-update-system").hide();
45
+ dfd.resolve();
46
+ });
47
+ return dfd;
48
+ }
49
+
50
+ function callUpdateSystem() {
51
+ var dfd = jQuery.Deferred();
52
+ $("#version-updating").show();
53
+ $("#gem-result-log").hide();
54
+ action.updateSystem(function(result, log) {
55
+ $("#version-updating").hide();
56
+ $("#gem-result-log").text(log);
57
+ if (result == "success")
58
+ dfd.resolve();
59
+ else
60
+ dfd.reject(result);
61
+ });
62
+ return dfd;
63
+ }
64
+
65
+ function rebootButtonHandler() {
66
+ $("#gem-result-log").addClass("large").show();
67
+ $("#reboot-button")
68
+ .show()
69
+ .on("click", function(e) {
70
+ action.rebootDialog();
71
+ });
72
+ }
73
+
74
+ function failUpdateSystem(result) {
75
+ if (result == "failure") {
76
+ $("#fail-update-system").show();
77
+ $("#gem-result-log").show();
78
+ updateSysmteProcess();
79
+ }
80
+ else if (result == "nothing") {
81
+ $("#nothing-to-update").show();
82
+ }
83
+ }
84
+
85
+ function updateSysmteProcess() {
86
+ $("#version-update-button").show();
87
+ updateButtonHandler()
88
+ .then(callUpdateSystem)
89
+ .then(rebootButtonHandler)
90
+ .fail(failUpdateSystem);
91
+ }
92
+
93
+ $result = $("#result-of-version-checking");
94
+ $result.children("div").hide();
95
+
96
+ $("#version-update-button").hide();
97
+ $("#reboot-button").hide();
98
+ $("#version-updating").hide();
99
+ $("#fail-update-system").hide();
100
+ $("#nothing-to-update").hide();
101
+ $("#gem-result-log").hide();
102
+
103
+ action.checkUpdatedSystem({
104
+ // まだgemのアップデートは行われていないので、バージョンチェックからはじめる
105
+ not_updated: function() {
106
+ $result.children(".checking").show();
107
+
108
+ $.when(
109
+ getCurrentVersion(),
110
+ getLatestVersion()
111
+ )
112
+ .done(function(current_version, latest_version) {
113
+ $result.children(".checking").hide();
114
+ if (latest_version > current_version) {
115
+ $("#latest-version").text(latest_version);
116
+ $result.children(".need-an-update").show();
117
+ }
118
+ else {
119
+ $result.children(".version-is-latest").show();
120
+ }
121
+ })
122
+ .fail(function() {
123
+ $result.children(".checking").hide();
124
+ $result.children(".error").show();
125
+ });
126
+
127
+ updateSysmteProcess();
128
+ },
129
+
130
+ // すでにgemをアップデート済みなので、再起動ボタンを最初から表示する
131
+ already_updated: function(log) {
132
+ $result.children(".need-an-update").show();
133
+ $("#latest-version-found").hide();
134
+ $("#reboot-button").show();
135
+ $("#gem-result-log").text(log);
136
+ rebootButtonHandler();
137
+ }
138
+ });
139
+
140
+ }();
141
+
142
+ %div.link-to-changelog-in-about
143
+ %a.btn.btn-default.btn-sm(href="https://github.com/whiteleaf7/narou/blob/master/ChangeLog.md" target="_blank")
144
+ 更新履歴
145
+ %span.glyphicon.glyphicon-new-window
146
+
147
+ %div(style="text-align:center")
148
+ %h1
149
+ %strong Narou.rb
150
+ %div
151
+ Version #{@narourb_version}
152
+ %div
153
+ Copyright 2013 whiteleaf. All rights reserved.
154
+ #result-of-version-checking(style="margin-top:10px")
155
+ .checking
156
+ %span.rotate-progress
157
+ %span.icon
158
+ %span.text アップデートを確認しています...
159
+ .version-is-latest
160
+ %p
161
+ %span.glyphicon.glyphicon-thumbs-up.text-primary
162
+ Narou.rb は最新版です。
163
+ .need-an-update
164
+ %p
165
+ %span#latest-version-found
166
+ %span.glyphicon.glyphicon-exclamation-sign.text-primary
167
+ 新しいバージョン(<span id="latest-version"></span>)が見つかりました。<br>
168
+ %a#version-update-button.btn.btn-primary.btn-sm
169
+ %b Narou.rb を更新する
170
+ %span#version-updating
171
+ %span.rotate-progress
172
+ %span.icon
173
+ %span.text 更新しています。
174
+ %a#reboot-button.btn.btn-success.btn-sm
175
+ %b 再起動して更新を反映する
176
+ %span#nothing-to-update.text-success
177
+ Narou.rb はすでに最新版です。
178
+ %p#fail-update-system.text-danger
179
+ Narou.rb の更新に失敗しました。
180
+ .error
181
+ %span.text-danger
182
+ 最新バージョンの取得に失敗しました。
183
+
184
+ %pre#gem-result-log.log-box
185
+
186
+ .well.well-sm(style="font-size:80%; margin-top: 10px")
187
+ #{@ruby_version}<br>
188
+ #{env["HTTP_USER_AGENT"]}