narou 3.2.4 → 3.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -383,6 +383,13 @@ module Command
383
383
  " ※注意:存在しないフォルダだとエラーになる",
384
384
  tab: :general
385
385
  },
386
+ "convert.copy-to-grouping" => {
387
+ type: :multiple,
388
+ help: "copy-toで指定したフォルダの中で更に指定の各種フォルダにまとめる",
389
+ select_keys: %w(device site),
390
+ select_summaries: %w(端末毎にまとめる 掲載サイト毎にまとめる),
391
+ tab: :general
392
+ },
386
393
  "convert.copy_to" => {
387
394
  type: :directory, help: "copy-toの昔の書き方(非推奨)", invisible: true
388
395
  },
@@ -421,10 +428,6 @@ module Command
421
428
  select_summaries: Device::DEVICES.values.map { |d| d::DISPLAY_NAME },
422
429
  tab: :general
423
430
  },
424
- "convert.copy-to-grouping" => {
425
- type: :boolean, help: "copy-toで指定したフォルダの中で更に端末毎にフォルダを振り分ける",
426
- tab: :general
427
- },
428
431
  "convert.filename-to-ncode" => {
429
432
  type: :boolean, help: "書籍ファイル名をNコードで出力する(ドメイン_Nコードの形式)",
430
433
  tab: :general
@@ -474,6 +474,13 @@ class ConverterBase
474
474
  end
475
475
  end
476
476
 
477
+ def convert_prolonged_sound_mark_to_dash(data)
478
+ return unless @setting.enable_prolonged_sound_mark_to_dash
479
+ data.gsub!(/(ー{2,})/) do |match|
480
+ "―" * match.length
481
+ end
482
+ end
483
+
477
484
  #
478
485
  # 小説のルールに沿うように変換
479
486
  #
@@ -508,8 +515,9 @@ class ConverterBase
508
515
  end
509
516
  else
510
517
  data.gsub!(ENGLISH_SENTENCES_CHARACTERS) do |match|
511
- if match.split(" ").size >= 2 \
512
- || (match.length >= ENGLISH_SENTENCES_MIN_LENGTH && match.match(/[a-z]/i))
518
+ if match.split(" ").size >= 2 ||
519
+ (match.length >= ENGLISH_SENTENCES_MIN_LENGTH && match.match(/[a-z]/i)) ||
520
+ @setting.disable_alphabet_word_to_zenkaku
513
521
  @english_sentences << match
514
522
  "[#英文=#{@english_sentences.size - 1}]"
515
523
  else
@@ -858,7 +866,7 @@ class ConverterBase
858
866
  def auto_join_line(data)
859
867
  # 次の行の冒頭が開き記号だったら意図的な改行だと判断して連結しない
860
868
  # 行頭の全角スペースが2個以上の場合も連結しない
861
- data.gsub!(/([^、])、\n ([^「『((【<<〈《≪・■…‥― ])/, "\\1、\\2")
869
+ data.gsub!(/([^、])、\n ([^「『((【<<〈《≪・■…‥― 1-9一-九])/, "\\1、\\2")
862
870
  end
863
871
 
864
872
  CHARACTER_OF_RUBY = "一-龠A-Za-zA-Za-z"
@@ -1164,6 +1172,7 @@ class ConverterBase
1164
1172
  convert_fraction_and_date(data)
1165
1173
  modify_kana_ni_to_kanji_ni(data)
1166
1174
  convert_dakuten_char_to_font(data)
1175
+ convert_prolonged_sound_mark_to_dash(data)
1167
1176
  end
1168
1177
 
1169
1178
  def before_convert(io)
@@ -1428,20 +1437,4 @@ class ConverterBase
1428
1437
  end
1429
1438
  result
1430
1439
  end
1431
-
1432
- def dash_image_relative_paths(base_dir, output_text_dir)
1433
- DASH_FILES.map do |name|
1434
- pathname = Pathname(File.join(base_dir, name))
1435
- pathname.relative_path_from(Pathname(output_text_dir)).to_s
1436
- end
1437
- end
1438
-
1439
- def copy_dash_images_to_local_setting_dir
1440
- DASH_FILES.each do |name|
1441
- path = File.join(Narou.local_setting_dir, name)
1442
- unless File.exist?(path)
1443
- FileUtils.copy(File.join(Narou.get_preset_dir, name), path)
1444
- end
1445
- end
1446
- end
1447
1440
  end
@@ -160,6 +160,11 @@ class Downloader
160
160
  YAML.load_file(File.join(archive_path, TOC_FILE_NAME))
161
161
  end
162
162
 
163
+ def self.get_toc_by_target(target)
164
+ dir = Downloader.get_novel_data_dir_by_target(target)
165
+ get_toc_data(dir)
166
+ end
167
+
163
168
  #
164
169
  # 指定の小説の目次ページのURLを取得する
165
170
  #
@@ -401,6 +406,10 @@ class Downloader
401
406
  # あらすじが更新されている場合
402
407
  @stream.puts "#{id_and_title} のあらすじが更新されています"
403
408
  :ok
409
+ when old_toc["author"] != latest_toc["author"]
410
+ # 作者名が更新されている場合
411
+ @stream.puts "#{id_and_title} の作者名が更新されています"
412
+ update_database
404
413
  else
405
414
  :none
406
415
  end
@@ -780,7 +789,7 @@ class Downloader
780
789
  "subtitles" => subtitles
781
790
  }
782
791
  toc_objects
783
- rescue OpenURI::HTTPError, Errno::ECONNRESET => e
792
+ rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ETIMEDOUT, Net::OpenTimeout => e
784
793
  raise if through_error # エラー処理はしなくていいからそのまま例外を受け取りたい時用
785
794
  if e.message.include?("404")
786
795
  @stream.error "小説が削除されているか非公開な可能性があります"
@@ -1131,7 +1140,7 @@ narou s download.wait-steps=5
1131
1140
  open(url, "r:#{@setting["encoding"]}", open_uri_options) do |fp|
1132
1141
  raw = Helper.pretreatment_source(fp.read, @setting["encoding"])
1133
1142
  end
1134
- rescue OpenURI::HTTPError => e
1143
+ rescue OpenURI::HTTPError, Errno::ECONNRESET, Errno::ETIMEDOUT, Net::OpenTimeout => e
1135
1144
  if e.message =~ /^503/
1136
1145
  if retry_count == 0
1137
1146
  @stream.error "上限までリトライしましたがファイルがダウンロード出来ませんでした"
@@ -111,4 +111,8 @@ class HTML
111
111
  def delete_ruby_tag(text = @string)
112
112
  text.gsub(%r!<\/?(?:ruby|rb|rp|rt)>!, "")
113
113
  end
114
+
115
+ def ln_to_br(text = @string)
116
+ text.to_s.gsub("\n", "<br>")
117
+ end
114
118
  end
@@ -338,9 +338,9 @@ module Narou
338
338
  end
339
339
  memoize :kindlegen_path
340
340
 
341
- def line_height
341
+ def line_height(default: LINE_HEIGHT_DEFAULT)
342
342
  global_setting = Inventory.load("global_setting", :global)
343
- global_setting["line-height"] || LINE_HEIGHT_DEFAULT
343
+ global_setting["line-height"] || default
344
344
  end
345
345
 
346
346
  end
@@ -6,6 +6,7 @@
6
6
  require "fileutils"
7
7
  require_relative "ini"
8
8
  require_relative "downloader"
9
+ require_relative "converterbase"
9
10
 
10
11
  class NovelSetting
11
12
  INI_NAME = "setting.ini"
@@ -256,7 +257,13 @@ class NovelSetting
256
257
  name: "enable_alphabet_force_zenkaku",
257
258
  type: :boolean,
258
259
  value: false,
259
- help: "アルファベットを強制的に全角にする。falseの場合英文は半角、それ以外は全角になる"
260
+ help: "アルファベットを強制的に全角にする。false の場合は英文は半角、#{::ConverterBase::ENGLISH_SENTENCES_MIN_LENGTH}文字未満の英単語は全角になる"
261
+ },
262
+ {
263
+ name: "disable_alphabet_word_to_zenkaku",
264
+ type: :boolean,
265
+ value: false,
266
+ help: "enable_alphabet_force_zenkaku が false の場合に、#{::ConverterBase::ENGLISH_SENTENCES_MIN_LENGTH}文字未満の英単語を全角にする機能を抑制する。英文中にルビがふってあり、英文ではなく英単語と認識されて全角化されてしまう場合などに使用"
260
267
  },
261
268
  {
262
269
  name: "enable_half_indent_bracket",
@@ -450,6 +457,12 @@ $ntag 小説のタグをカンマ区切りにしたもの
450
457
  value: false,
451
458
  help: "完結済み小説のタイトルに(完結)と表示する"
452
459
  },
460
+ {
461
+ name: "enable_prolonged_sound_mark_to_dash",
462
+ type: :boolean,
463
+ value: false,
464
+ help: "長音記号を2つ以上つなげている場合に全角ダッシュに置換する"
465
+ },
453
466
  {
454
467
  name: "cut_old_subtitles",
455
468
  type: :integer,
@@ -4,5 +4,5 @@
4
4
  #
5
5
 
6
6
  module Narou
7
- VERSION = "3.2.4"
7
+ VERSION = "3.2.5"
8
8
  end
@@ -419,6 +419,42 @@ class Narou::AppServer < Sinatra::Base
419
419
  end
420
420
  end
421
421
 
422
+ get "/novels/:id/author_comments" do
423
+ downloader = Downloader.new(@id)
424
+ toc = downloader.load_toc_file
425
+ @comments = []
426
+ introductions_count = 0
427
+ postscripts_count = 0
428
+ toc["subtitles"].each do |sub|
429
+ begin
430
+ element = YAML.load_file(downloader.create_section_file_path(sub))["element"]
431
+ data_type = element["data_type"] || "text"
432
+ introduction = element["introduction"] || ""
433
+ postscript = element["postscript"] || ""
434
+ if data_type == "html"
435
+ html = HTML.new
436
+ html.strip_decoration_tag = true
437
+ html.string = introduction
438
+ introduction = html.to_aozora
439
+ html.string = postscript
440
+ postscript = html.to_aozora
441
+ end
442
+ @comments.push(
443
+ sub: sub,
444
+ introduction: introduction,
445
+ postscript: postscript
446
+ )
447
+ introductions_count += 1 unless introduction.empty?
448
+ postscripts_count += 1 unless postscript.empty?
449
+ rescue Errno::ENOENT
450
+ end
451
+ end
452
+ total = toc["subtitles"].count.to_f
453
+ @introductions_ratio = (introductions_count / total * 100).round(2)
454
+ @postscripts_ratio = (postscripts_count / total * 100).round(2)
455
+ haml :"novels/author_comments"
456
+ end
457
+
422
458
  get "/notepad" do
423
459
  @title = "メモ帳"
424
460
  haml :notepad
@@ -850,6 +886,14 @@ class Narou::AppServer < Sinatra::Base
850
886
  ""
851
887
  end
852
888
 
889
+ get "/api/story" do
890
+ id = params["id"] or pass
891
+ toc = Downloader.get_toc_by_target(id)
892
+ story = toc["story"] || ""
893
+ html = HTML.new
894
+ json title: toc["title"], story: html.ln_to_br(story.strip)
895
+ end
896
+
853
897
  # -------------------------------------------------------------------------------
854
898
  # 一部分に表示するためのHTMLを取得する(パーシャル)
855
899
  # -------------------------------------------------------------------------------
@@ -58,6 +58,10 @@ var Narou = (function() {
58
58
  * ユーティリティ / Utility
59
59
  *************************************************************************/
60
60
  $.extend(Narou, {
61
+ // windows: はみ出したらカーソル位置がメニューの下端になるよう表示
62
+ // mac: はみ出したらメニュー下端とブラウザ下端が同じになるようにずれる
63
+ popupMenuStyle: storage.get("menu_style") || "windows",
64
+
61
65
  registerCloseHandler: function(callback) {
62
66
  // Chrome, IEですぐにclickイベントをバインドすると、メニュー表示時の
63
67
  // クリックに反応してしまう(表示上のズレによって、クリック時のマウス
@@ -70,17 +74,30 @@ var Narou = (function() {
70
74
  },
71
75
 
72
76
  popupMenu: function(menu_id, pos, close_menu_handler) {
73
- var $menu = $(menu_id);
74
- var left = $(window).width() < pos.x - $(document).scrollLeft() + $menu.outerWidth() ?
75
- pos.x - $menu.outerWidth() : pos.x;
76
- var top = $(window).height() < pos.y - $(document).scrollTop() + $menu.outerHeight() ?
77
- pos.y - $menu.outerHeight() : pos.y;
78
- $menu.show().offset({
79
- left: left, top: top
80
- });
77
+ var menu = $(menu_id);
78
+ var offset = this._calcMenuOffset(pos, menu);
79
+ menu.show().offset(offset);
81
80
  Narou.registerCloseHandler(close_menu_handler);
82
81
  },
83
82
 
83
+ _calcMenuOffset: function(pos, menu) {
84
+ var left = pos.x, top = pos.y;
85
+ if ($(window).width() < pos.x - $(document).scrollLeft() + menu.outerWidth()) {
86
+ left -= menu.outerWidth();
87
+ }
88
+ if ($(window).height() < pos.y - $(document).scrollTop() + menu.outerHeight()) {
89
+ if (this.popupMenuStyle === "windows") {
90
+ top -= menu.outerHeight();
91
+ }
92
+ else {
93
+ top -= (pos.y - $(document).scrollTop() + menu.outerHeight()) - $(window).height() + 5;
94
+ }
95
+ }
96
+ return {
97
+ left: left, top: top
98
+ };
99
+ },
100
+
84
101
  // http://qiita.com/osakanafish/items/c64fe8a34e7221e811d0
85
102
  formatDate: function(date, format) {
86
103
  if (!format) format = 'YYYY-MM-DD hh:mm:ss.SSS';
@@ -128,6 +145,10 @@ var Narou = (function() {
128
145
  }
129
146
  return num;
130
147
  },
148
+
149
+ tableReload: function() {
150
+ Notification.instance().trigger("table.reload");
151
+ },
131
152
  });
132
153
 
133
154
  /*************************************************************************
@@ -364,7 +385,7 @@ var Narou = (function() {
364
385
  _default_commands: [
365
386
  "setting", "diff", "edit_tag", "freeze_toggle", "update",
366
387
  "send", "remove", "convert", "inspect", "folder", "backup",
367
- "mail"
388
+ "mail", "author_comments"
368
389
  ],
369
390
 
370
391
  items: [
@@ -383,6 +404,7 @@ var Narou = (function() {
383
404
  { label: "バックアップを作成", command: "backup" },
384
405
  { label: "再ダウンロード", command: "download_force" },
385
406
  { label: "メールで送信", command: "mail" },
407
+ { label: "作者コメント表示", command: "author_comments" },
386
408
  ],
387
409
 
388
410
  events: {
@@ -465,6 +487,11 @@ var Narou = (function() {
465
487
  this.action.mail(this.target_id);
466
488
  }.bind(this));
467
489
  },
490
+
491
+ author_comments: function() {
492
+ var authorCommentPath = "/novels/" + this.target_id + "/author_comments";
493
+ window.open(authorCommentPath);
494
+ },
468
495
  },
469
496
 
470
497
  initializeMenuEvents: function() {
@@ -873,6 +900,42 @@ var Narou = (function() {
873
900
  ejectNow: function() {
874
901
  $.post("/api/eject", { enqueue: false });
875
902
  },
903
+
904
+ displayStory: function(button, targetId) {
905
+ $.get("/api/story", { id: targetId }, function(json) {
906
+ button.popover({
907
+ container: "body",
908
+ placement: "left",
909
+ trigger: "focus",
910
+ html: true,
911
+ title: json.title,
912
+ content: json.story
913
+ });
914
+ button.popover("show");
915
+ });
916
+ },
917
+ });
918
+
919
+ /*************************************************************************
920
+ * メニューアクション
921
+ *************************************************************************/
922
+ var MenuAction = Narou.MenuAction = function() {
923
+ };
924
+
925
+ $.extend(MenuAction.prototype, {
926
+ toggleNovelListWide: function() {
927
+ $("#novel-list-container").
928
+ toggleClass("container").
929
+ toggleClass("container-fluid");
930
+ $("#action-view-novel-list-wide").toggleClass("active");
931
+ },
932
+
933
+ novelListToWiden: function() {
934
+ var isWide = storage.get("novel_list_is_wide");
935
+ storage.set("novel_list_is_wide", !isWide);
936
+ storage.save();
937
+ this.toggleNovelListWide();
938
+ },
876
939
  });
877
940
 
878
941
  /*************************************************************************
@@ -25,6 +25,7 @@ $(document).ready(function() {
25
25
  toc_url: false, novel_type: false, tags: false, status: false, menu: true,
26
26
  download: true, folder: false, update_button: false, general_lastup: false,
27
27
  general_all_no: false, last_check_date: false, length: false, average_length: false,
28
+ story: false,
28
29
  }
29
30
  // PC
30
31
  : {
@@ -32,6 +33,7 @@ $(document).ready(function() {
32
33
  toc_url: true, novel_type: false, tags: true, status: true, menu: true,
33
34
  download: false, folder: true, update_button: true, general_lastup: true,
34
35
  general_all_no: false, last_check_date: false, length: false, average_length: false,
36
+ story: false,
35
37
  });
36
38
 
37
39
  $.fn.dataTable.Api.register("fireChangeSelect()", function() {
@@ -91,10 +93,6 @@ $(document).ready(function() {
91
93
  });
92
94
  }
93
95
 
94
- function tableReload() {
95
- notification.trigger("table.reload");
96
- }
97
-
98
96
  /*
99
97
  * 凍結の表示状態制御
100
98
  */
@@ -168,12 +166,17 @@ $(document).ready(function() {
168
166
  order: [[ 2, "desc" ]], // 初期状態は「最新話掲載日」でソート
169
167
  columns: [
170
168
  // ID
171
- { title: "ID",
172
- data: "id", className: "column-id text-center", visible: cell_visible.id },
169
+ {
170
+ title: "ID",
171
+ data: "id", className: "column-id text-center", visible: cell_visible.id,
172
+ width: "25px"
173
+ },
173
174
  // UPDATEした日時
174
- { title: "更新日",
175
+ {
176
+ title: "更新日",
175
177
  data: "last_update", className: "text-center",
176
178
  orderSequence: [ "desc", "asc" ], visible: cell_visible.last_update,
179
+ width: "35px",
177
180
  render: function(data, type, row) {
178
181
  if (type === "display" || type === "filter") {
179
182
  var now = new Date;
@@ -204,6 +207,7 @@ $(document).ready(function() {
204
207
  title: "最新話<br>掲載日",
205
208
  data: "general_lastup", className: "text-center",
206
209
  orderSequence: [ "desc", "asc" ], visible: cell_visible.general_lastup,
210
+ width: "35px",
207
211
  render: function(data, type, row) {
208
212
  if (type === "display" || type === "filter") {
209
213
  if (!data) return "";
@@ -237,6 +241,7 @@ $(document).ready(function() {
237
241
  title: "更新チェ<br>ック日",
238
242
  data: "last_check_date", className: "text-center",
239
243
  orderSequence: [ "desc", "asc" ], visible: cell_visible.last_check_date,
244
+ width: "35px",
240
245
  render: function(data, type, row) {
241
246
  if (type === "display" || type === "filter") {
242
247
  if (!data)
@@ -254,11 +259,16 @@ $(document).ready(function() {
254
259
  }
255
260
  },
256
261
  // タイトル
257
- { title: "タイトル",
258
- data: "title", visible: cell_visible.title },
262
+ {
263
+ title: "タイトル",
264
+ data: "title", visible: cell_visible.title,
265
+ width: "300px",
266
+ },
259
267
  // 作者名
260
- { title: "作者名",
268
+ {
269
+ title: "作者名",
261
270
  data: "author", visible: cell_visible.author,
271
+ width: "100px",
262
272
  render: function(author, type, row) {
263
273
  if (author && type === "display") {
264
274
  return sprintf(
@@ -270,8 +280,10 @@ $(document).ready(function() {
270
280
  }
271
281
  },
272
282
  // 掲載サイト
273
- { title: "掲載",
283
+ {
284
+ title: "掲載",
274
285
  data: "sitename", visible: cell_visible.sitename,
286
+ width: "60px",
275
287
  render: function(sitename, type, row) {
276
288
  if (sitename && type === "display") {
277
289
  return sprintf(
@@ -288,7 +300,7 @@ $(document).ready(function() {
288
300
  orderSequence: [ "desc", "asc" ], visible: cell_visible.novel_type },
289
301
  // タグ
290
302
  { title: "タグ",
291
- data: "tags", orderSequence: [ "desc", "asc" ], width: "70px",
303
+ data: "tags", orderSequence: [ "desc", "asc" ], width: "80px",
292
304
  visible: cell_visible.tags },
293
305
  // 話数
294
306
  { title: "話数",
@@ -328,13 +340,17 @@ $(document).ready(function() {
328
340
  }
329
341
  },
330
342
  // 状態
331
- { title: "状態",
332
- data: "status", visible: cell_visible.status },
343
+ {
344
+ title: "状態",
345
+ data: "status", visible: cell_visible.status,
346
+ width: "40px"
347
+ },
333
348
  // 掲載URL
334
349
  {
335
350
  title: "リンク",
336
351
  data: "toc_url", className: "text-center",
337
352
  visible: cell_visible.toc_url,
353
+ width: "25px",
338
354
  render: function(data, type, row) {
339
355
  if (!data) return "";
340
356
 
@@ -359,11 +375,14 @@ $(document).ready(function() {
359
375
  {
360
376
  title: "DL",
361
377
  data: "download", className: "text-center", orderable: false,
362
- searchable: false, visible: cell_visible.download },
378
+ searchable: false, visible: cell_visible.download,
379
+ width: "25px",
380
+ },
363
381
  {
364
382
  title: "保存先",
365
383
  data: "folder", className: "text-center", orderable: false,
366
384
  searchable: false, visible: cell_visible.folder,
385
+ width: "25px",
367
386
  defaultContent: '<button class="btn btn-default btn-xs">' +
368
387
  '<span class="glyphicon glyphicon-folder-open"></span></button>',
369
388
  createdCell: function(td, cellData, rowData, rowIndex, colIndex) {
@@ -384,6 +403,7 @@ $(document).ready(function() {
384
403
  searchable: false, visible: cell_visible.update_button,
385
404
  defaultContent: '<button class="btn btn-default btn-xs">' +
386
405
  '<span class="glyphicon glyphicon-refresh"></span></button>',
406
+ width: "25px",
387
407
  createdCell: function(td, cellData, rowData, rowIndex, colIndex) {
388
408
  $(td).children("button")
389
409
  .data("targetId", rowData.id)
@@ -396,12 +416,34 @@ $(document).ready(function() {
396
416
  },
397
417
  render: function() {}
398
418
  },
419
+ // あらすじ
420
+ {
421
+ title: "あらすじ",
422
+ data: "story", visible: cell_visible.story, className: "text-center",
423
+ searchable: false, orderable: false,
424
+ width: "25px",
425
+ render: function () {},
426
+ defaultContent: '<button class="btn btn-default btn-xs n-popover">' +
427
+ '<span class="glyphicon glyphicon-info-sign"></span></button>',
428
+ createdCell: function(td, cellData, rowData, rowIndex, colIndex) {
429
+ var popoverToggled = false;
430
+ var beforeTargetId = null;
431
+ $(td).children("button")
432
+ .data("targetId", rowData.id)
433
+ .one("click", function(e) {
434
+ var $this = $(this);
435
+ var targetId = $this.data("targetId");
436
+ action.displayStory($this, targetId);
437
+ })
438
+ },
439
+ },
399
440
  {
400
441
  title: "個別",
401
442
  data: "menu", className: "text-center",
402
443
  defaultContent: '<button class="btn btn-default btn-xs">' +
403
444
  '<span class="glyphicon glyphicon-option-horizontal"></span></button>',
404
445
  visible: cell_visible.menu,
446
+ width: "25px",
405
447
  createdCell: function(td, cellData, rowData, rowIndex, colIndex) {
406
448
  $(td).children("button")
407
449
  .data("targetId", rowData.id)
@@ -494,13 +536,14 @@ $(document).ready(function() {
494
536
  // initComplete イベントが発火する前に一度この reorderCallback が発火されるので、
495
537
  // 無駄なリロードをしないように初期化後のみ有効にする
496
538
  if (datatables_init_completed) {
497
- tableReload();
539
+ Narou.tableReload();
498
540
  }
499
541
  },
500
542
  },
501
543
  });
502
544
 
503
545
  var action = new Narou.Action(table);
546
+ var menuAction = new Narou.MenuAction();
504
547
  var notification = Narou.Notification.instance();
505
548
  var stream_console = new Narou.Console;
506
549
  var search = Narou.Search.get(table);
@@ -565,6 +608,13 @@ $(document).ready(function() {
565
608
  $(".eject-group").addClass("hide");
566
609
  });
567
610
 
611
+ /*
612
+ * 小説リストの幅を変更
613
+ */
614
+ if (storage.get("novel_list_is_wide")) {
615
+ menuAction.toggleNovelListWide();
616
+ }
617
+
568
618
  /*************************************************************
569
619
  * 小説の選択関係
570
620
  *************************************************************/
@@ -824,6 +874,7 @@ $(document).ready(function() {
824
874
  table.fireChangeSelect();
825
875
  $("#rect-select-menu").hide();
826
876
  selected_rect_element.hide();
877
+ $(".n-popover").blur();
827
878
  };
828
879
  if (not_clicked_yet) {
829
880
  // 範囲選択開始
@@ -938,6 +989,16 @@ $(document).ready(function() {
938
989
  colvis._fnCollectionShow();
939
990
  });
940
991
 
992
+ /*
993
+ * メニュー
994
+ * 表示>小説リストの幅を広げる
995
+ */
996
+ $("#action-view-novel-list-wide").on(click_event_name, function(e) {
997
+ e.preventDefault();
998
+ slideNavbar.slide();
999
+ menuAction.novelListToWiden();
1000
+ });
1001
+
941
1002
  /*
942
1003
  * メニュー
943
1004
  * 表示>凍結中以外を表示
@@ -948,7 +1009,7 @@ $(document).ready(function() {
948
1009
  $(this).toggleClass("active");
949
1010
  toggleVisibilityNonFrozen();
950
1011
  saveVisibilityFrozen();
951
- tableReload();
1012
+ Narou.tableReload();
952
1013
  });
953
1014
 
954
1015
  /*
@@ -961,7 +1022,7 @@ $(document).ready(function() {
961
1022
  $(this).toggleClass("active");
962
1023
  toggleVisibilityFrozen();
963
1024
  saveVisibilityFrozen();
964
- tableReload();
1025
+ Narou.tableReload();
965
1026
  });
966
1027
 
967
1028
  /*
@@ -1011,6 +1072,39 @@ $(document).ready(function() {
1011
1072
  $("#footer-navbar").addClass("hide");
1012
1073
  });
1013
1074
 
1075
+ /*
1076
+ * メニュー
1077
+ * 表示>個別メニューの表示スタイルを選択
1078
+ */
1079
+ var _menuStyleRenderer = _.template(
1080
+ (function () {/*
1081
+ <div class="radio"><label>
1082
+ <input type="radio" value="windows" name="select-menu-style" <% if (style == "windows") { %>checked<% } %>>
1083
+ Windows スタイル:<br>メニューが画面外にはみ出そうとしたら、カーソルの上側に表示する
1084
+ </label></div>
1085
+ <div class="radio"><label>
1086
+ <input type="radio" value="mac" name="select-menu-style" <% if (style == "mac") { %>checked<% } %>>
1087
+ Mac スタイル:<br>メニューが画面外にはみ出そうとしたら、メニューがはみ出ないよう上にずれる
1088
+ </label></div>
1089
+ */}).toString().replace(/(\n)/g, '').split('*')[1]
1090
+ );
1091
+
1092
+ $("#action-view-select-menu-style").on(click_event_name, function(e) {
1093
+ e.preventDefault();
1094
+ slideNavbar.slide();
1095
+ var menuStyle = storage.get("menu_style") || "windows";
1096
+ var box = bootbox.alert({
1097
+ title: "個別メニューの表示スタイルを選択",
1098
+ message: _menuStyleRenderer({ style: menuStyle }),
1099
+ callback: function() {
1100
+ menuStyle = box.find("input:checked").val();
1101
+ storage.set("menu_style", menuStyle);
1102
+ storage.save();
1103
+ Narou.popupMenuStyle = menuStyle;
1104
+ }
1105
+ });
1106
+ });
1107
+
1014
1108
  /*
1015
1109
  * メニュー
1016
1110
  * 表示>表示設定を全てリセット
@@ -1464,7 +1558,7 @@ $(document).ready(function() {
1464
1558
  * ○件表示の横のリロードアイコン
1465
1559
  */
1466
1560
  $("#icon-refresh-table").on("click", function(e) {
1467
- tableReload();
1561
+ Narou.tableReload();
1468
1562
  });
1469
1563
 
1470
1564
  /*
@@ -1497,6 +1591,13 @@ $(document).ready(function() {
1497
1591
  shortcut.add(keys[i], callback, options);
1498
1592
  }
1499
1593
  };
1594
+
1595
+ var click = function(id) {
1596
+ return function() {
1597
+ $(id).trigger("click");
1598
+ };
1599
+ };
1600
+
1500
1601
  add("Ctrl+A", "Meta+A", function() { action.selectView(); });
1501
1602
  add("Shift+A", function() { action.selectAll(); });
1502
1603
  add("Ctrl+Shift+A", "Meta+Shift+A", function() { action.selectClear(); });
@@ -1511,9 +1612,13 @@ $(document).ready(function() {
1511
1612
  action.selectClear();
1512
1613
  }
1513
1614
  });
1514
- add("S", function() { $("#action-select-mode-single").trigger("click"); });
1515
- add("R", function() { $("#action-select-mode-rect").trigger("click"); });
1516
- add("H", function() { $("#action-select-mode-hybrid").trigger("click"); });
1615
+ add("S", click("#action-select-mode-single"));
1616
+ add("R", click("#action-select-mode-rect"));
1617
+ add("H", click("#action-select-mode-hybrid"));
1618
+ add("W", click("#action-view-novel-list-wide"));
1619
+ add("F", click("#action-view-frozen"));
1620
+ add("Shift+F", click("#action-view-nonfrozen"));
1621
+ add("T", click("#action-tag-edit"));
1517
1622
  };
1518
1623
  if (!touchable_device) {
1519
1624
  initialize_shortcut();