narou 2.6.1 → 2.7.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog.md +65 -0
  3. data/README.md +69 -22
  4. data/lib/command.rb +1 -0
  5. data/lib/command/alias.rb +2 -2
  6. data/lib/command/convert.rb +3 -3
  7. data/lib/command/csv.rb +108 -0
  8. data/lib/command/download.rb +13 -1
  9. data/lib/command/freeze.rb +1 -1
  10. data/lib/command/init.rb +5 -1
  11. data/lib/command/inspect.rb +1 -1
  12. data/lib/command/mail.rb +1 -1
  13. data/lib/command/send.rb +2 -2
  14. data/lib/command/setting.rb +17 -1
  15. data/lib/command/tag.rb +28 -9
  16. data/lib/command/update.rb +57 -30
  17. data/lib/commandbase.rb +3 -2
  18. data/lib/commandline.rb +2 -2
  19. data/lib/converterbase.rb +70 -7
  20. data/lib/database.rb +1 -1
  21. data/lib/device.rb +3 -0
  22. data/lib/device/ibooks.rb +2 -2
  23. data/lib/device/ibunko.rb +4 -0
  24. data/lib/diffviewer.rb +2 -2
  25. data/lib/downloader.rb +16 -9
  26. data/lib/helper.rb +53 -22
  27. data/lib/inventory.rb +6 -2
  28. data/lib/narou.rb +13 -6
  29. data/lib/novelconverter.rb +35 -13
  30. data/lib/novelsetting.rb +7 -1
  31. data/lib/version.rb +1 -1
  32. data/lib/web/appserver.rb +78 -8
  33. data/lib/web/public/resources/common.ui.js +0 -39
  34. data/lib/web/public/resources/dataTables.colVis.js +1 -1
  35. data/lib/web/public/resources/default-style.css +1 -1
  36. data/lib/web/public/resources/help/bookmarklet1.png +0 -0
  37. data/lib/web/public/resources/help/bookmarklet2.png +0 -0
  38. data/lib/web/public/resources/images/dl_button0.gif +0 -0
  39. data/lib/web/public/resources/images/dl_button1.gif +0 -0
  40. data/lib/web/public/resources/narou.library.js +268 -36
  41. data/lib/web/public/resources/narou.queue.js +51 -0
  42. data/lib/web/public/resources/narou.ui.js +156 -59
  43. data/lib/web/settingmessages.rb +2 -1
  44. data/lib/web/views/{js/widget.erb → bookmarklet/download.js.erb} +1 -1
  45. data/lib/web/views/bookmarklet/insert_button.js.erb +133 -0
  46. data/lib/web/views/help.haml +31 -14
  47. data/lib/web/views/index.haml +62 -0
  48. data/lib/web/views/layout.haml +8 -7
  49. data/lib/web/views/parts/csv_import.haml +68 -0
  50. data/lib/web/views/parts/download_form.haml +89 -0
  51. data/lib/web/views/settings.haml +8 -3
  52. data/lib/web/views/style.scss +200 -14
  53. data/lib/web/views/{widget.haml → widget/download.haml} +18 -4
  54. data/lib/web/views/widget/drag_and_drop.haml +97 -0
  55. data/lib/web/worker.rb +39 -1
  56. data/narou.gemspec +56 -56
  57. data/preset/custom_chuki_tag.txt +1 -1
  58. data/preset/doubledash.png +0 -0
  59. data/preset/singledash.png +0 -0
  60. data/preset/vertical_font.css +1 -1
  61. data/spec/convert_spec.rb +5 -1
  62. data/spec/data/convert_test/auto_join_line/correct_test_auto_join_line.txt +6 -0
  63. data/spec/data/convert_test/auto_join_line/test_auto_join_line.txt +8 -0
  64. data/spec/data/convert_test/double_dash_to_image/correct_test_double_dash_to_image.txt +14 -0
  65. data/spec/data/convert_test/double_dash_to_image/setting.ini +1 -0
  66. data/spec/data/convert_test/double_dash_to_image/test_double_dash_to_image.txt +12 -0
  67. data/spec/worker_spec.rb +21 -2
  68. metadata +97 -60
@@ -101,7 +101,7 @@ class NovelSetting
101
101
  # { name: value, ... } 形式のハッシュとして返す
102
102
  #
103
103
  def self.load_settings_by_pattern(pattern)
104
- res = Inventory.load("local_setting", :local).map { |name, value|
104
+ res = Inventory.load("local_setting").map { |name, value|
105
105
  if name =~ /^#{pattern}\.(.+)$/
106
106
  [$1, value]
107
107
  else
@@ -430,6 +430,12 @@ class NovelSetting
430
430
  value: false,
431
431
  help: "HTMLの装飾系タグを削除する(主にArcadiaの作品に影響)"
432
432
  },
433
+ {
434
+ name: "enable_double_dash_to_image",
435
+ type: :boolean,
436
+ value: false,
437
+ help: "2倍ダッシュ(――)を画像に差し替える。Kindleのデフォルトフォントみたいにダッシュが太くて気になる人用"
438
+ },
433
439
  ]
434
440
 
435
441
  ORIGINAL_SETTINGS_KEY_INDEXES = {}.tap { |hash|
@@ -3,5 +3,5 @@
3
3
  # Copyright 2013 whiteleaf. All rights reserved.
4
4
  #
5
5
 
6
- Version = "2.6.1"
6
+ Version = "2.7.0"
7
7
 
@@ -419,6 +419,10 @@ class Narou::AppServer < Sinatra::Base
419
419
  json json_objects
420
420
  end
421
421
 
422
+ post "/api/cancel" do
423
+ Narou::Worker.cancel
424
+ end
425
+
422
426
  post "/api/convert" do
423
427
  ids = select_valid_novel_ids(params["ids"]) or pass
424
428
  Narou::Worker.push do
@@ -428,7 +432,7 @@ class Narou::AppServer < Sinatra::Base
428
432
 
429
433
  post "/api/download" do
430
434
  targets = params["targets"] or pass
431
- targets = targets.split
435
+ targets = targets.kind_of?(Array) ? targets : targets.split
432
436
  pass if targets.size == 0
433
437
  Narou::Worker.push do
434
438
  CommandLine.run!(["download"] + targets)
@@ -527,6 +531,14 @@ class Narou::AppServer < Sinatra::Base
527
531
  haml :diff_list, layout: false
528
532
  end
529
533
 
534
+ post "/api/diff_clean" do
535
+ target = params["target"] or pass
536
+ id = Downloader.get_id_by_target(target) or pass
537
+ Narou::Worker.push do
538
+ CommandLine.run!(%W(diff --clean #{id}))
539
+ end
540
+ end
541
+
530
542
  post "/api/inspect" do
531
543
  ids = select_valid_novel_ids(params["ids"]) or pass
532
544
  Narou::Worker.push do
@@ -554,7 +566,9 @@ class Narou::AppServer < Sinatra::Base
554
566
  result = '<div><span class="tag label label-default" data-tag="">タグ検索を解除</span></div>'
555
567
  tagname_list = Command::Tag.get_tag_list.keys
556
568
  tagname_list.each do |tagname|
557
- result << "<div>#{decorate_tags([tagname])}</div>"
569
+ result << "<div>#{decorate_tags([tagname])} " \
570
+ "<span class='select-color-button' data-target-tag='#{h tagname}'>" \
571
+ "<span class='#{Command::Tag.get_color(tagname)}'>a</span></span></div>"
558
572
  end
559
573
  result
560
574
  end
@@ -622,14 +636,69 @@ class Narou::AppServer < Sinatra::Base
622
636
  end
623
637
  end
624
638
 
639
+ post "/api/change_tag_color" do
640
+ tag = params["tag"] or pass
641
+ color = params["color"] or pass
642
+ tag_colors = Inventory.load("tag_colors")
643
+ tag_colors[tag] = color
644
+ tag_colors.save
645
+ @@push_server.send_all(:"table.reload")
646
+ @@push_server.send_all(:"tag.updateCanvas")
647
+ end
648
+
649
+ get "/api/csv/download" do
650
+ content_type "application/csv"
651
+ attachment "novels.csv"
652
+
653
+ Command::Csv.new.generate
654
+ end
655
+
656
+ post "/api/csv/import" do
657
+ files = params["files"] or pass
658
+ csv = Command::Csv.new
659
+ files.each do |file|
660
+ csv.import(file[:tempfile])
661
+ end
662
+ ""
663
+ end
664
+
665
+ # ダウンロード登録すると同時にグレーのボタン画像を返す
666
+ get "/api/download4ie" do
667
+ Narou::Worker.push do
668
+ CommandLine.run!(%W(download #{params["target"]}))
669
+ @@push_server.send_all(:"table.reload")
670
+ end
671
+ redirect "/resources/images/dl_button1.gif"
672
+ end
673
+
674
+ get "/api/validate_url_regexp_list" do
675
+ json Downloader.load_settings.map { |setting|
676
+ "(#{setting["url"].gsub(/\?<.+?>/, "?:").gsub("\\", "\\\\")})"
677
+ }
678
+ end
679
+
680
+ # -------------------------------------------------------------------------------
681
+ # 一部分に表示するためのHTMLを取得する
682
+ # -------------------------------------------------------------------------------
683
+
684
+ get "/parts/csv_import" do
685
+ haml :"parts/csv_import", layout: false
686
+ end
687
+
688
+ get "/parts/download_form" do
689
+ haml :"parts/download_form", layout: false
690
+ end
691
+
625
692
  # -------------------------------------------------------------------------------
626
693
  # ウィジット関係
627
694
  # -------------------------------------------------------------------------------
628
695
 
696
+ BOOKMARKLET_MODE = %w(download insert_button)
697
+
629
698
  get "/js/widget.js" do
630
- if %w(download).include?(params["mode"])
699
+ if BOOKMARKLET_MODE.include?(params["mode"])
631
700
  content_type :js
632
- erb :"js/widget"
701
+ erb :"bookmarklet/#{params['mode']}.js"
633
702
  else
634
703
  error("invaid mode")
635
704
  end
@@ -646,9 +715,6 @@ class Narou::AppServer < Sinatra::Base
646
715
  from = params["from"]
647
716
  if ALLOW_HOSTS.include?(from)
648
717
  headers "X-Frame-Options" => "ALLOW-FROM http://#{from}/"
649
- else
650
- headers "X-Frame-Options" => "DENY"
651
- halt
652
718
  end
653
719
  end
654
720
 
@@ -658,7 +724,11 @@ class Narou::AppServer < Sinatra::Base
658
724
  CommandLine.run!(["download", target])
659
725
  @@push_server.send_all(:"table.reload")
660
726
  end
661
- haml :widget, :layout => nil
727
+ haml :"widget/download", layout: nil
728
+ end
729
+
730
+ get "/widget/drag_and_drop" do
731
+ haml :"widget/drag_and_drop", layout: nil
662
732
  end
663
733
  end
664
734
 
@@ -28,45 +28,6 @@ $(document).ready(function() {
28
28
  */
29
29
  $("#fadeout-alert").delay(2000).animate({ opacity: "hide" }, 1500);
30
30
 
31
- /*
32
- * キューに積まれた数を表示
33
- */
34
- var notification = Narou.Notification.instance();
35
- var $queue = $("#queue");
36
- function highlightQueuBoxIcon(size) {
37
- if (size > 0) {
38
- $("#queue-text").addClass("active");
39
- }
40
- else {
41
- $("#queue-text").removeClass("active");
42
- }
43
- }
44
- notification.on("notification.queue", function(data) {
45
- var before_size = $queue.text();
46
- $queue.text(data);
47
- highlightQueuBoxIcon(data);
48
- // キューのスタック数の横に +1 が浮かび上がって消えるアニメーション
49
- var diff = data - before_size;
50
- if (diff <= 0) return;
51
- var $plus = $("<span>+" + diff + "</span>");
52
- $plus.addClass("queue-plus").css({
53
- position: "absolute",
54
- left: $queue.offset().left + $queue.outerWidth() + 1 - $(document).scrollLeft(),
55
- top: $queue.offset().top - 3 - $(document).scrollTop(),
56
- });
57
- $(".navbar").append($plus);
58
- $plus.delay(100).animate({
59
- top: - $plus.outerHeight(),
60
- opacity: 0,
61
- }, 3000, function() {
62
- $(this).remove();
63
- });
64
- });
65
- $.get("/api/get_queue_size", function(json) {
66
- $queue.text(json.size);
67
- highlightQueuBoxIcon(json.size);
68
- });
69
-
70
31
  /*************************************************************************
71
32
  * Webサーバの方から入力を求められた時にモーダルを表示して返事を返す
72
33
  *************************************************************************/
@@ -605,7 +605,7 @@ ColVis.prototype = {
605
605
  '<li '+(dt.bJUI ? 'class="ui-button ui-state-default"' : '')+'>'+
606
606
  '<label>'+
607
607
  '<input type="checkbox" />'+
608
- '<span>'+title+'</span>'+
608
+ '<span>'+title.replace("<br>", "")+'</span>'+
609
609
  '</label>'+
610
610
  '</li>'
611
611
  )
@@ -3,7 +3,7 @@
3
3
  background-color: rgba(248,248,248,0.93);
4
4
  }
5
5
  @media (max-width:767px) {
6
- .navbar.navbar-collapse {
6
+ .navbar .navbar-collapse {
7
7
  background-color: rgba(248,248,248,0.93);
8
8
  }
9
9
  }
@@ -10,6 +10,13 @@ var Narou = (function() {
10
10
  var storage_cache = null;
11
11
  var storage = null;
12
12
 
13
+ $.ajaxSetup({
14
+ cache: false // IE でキャッシュさせないため
15
+ });
16
+
17
+ // jQuery はデフォルトだと dataTransfer オブジェクトをコピーしないので
18
+ $.event.props.push("dataTransfer");
19
+
13
20
  /*************************************************************************
14
21
  * ローカルストレージ
15
22
  *************************************************************************/
@@ -90,6 +97,24 @@ var Narou = (function() {
90
97
  }
91
98
  return format;
92
99
  },
100
+
101
+ get_event_position: function(e) {
102
+ if (e.type !== "touchstart") {
103
+ return { x: e.pageX, y: e.pageY };
104
+ }
105
+ else {
106
+ return { x: e.originalEvent.touches[0].pageX,
107
+ y: e.originalEvent.touches[0].pageY };
108
+ }
109
+ },
110
+
111
+ noScroll: function() {
112
+ $("html, body").css("overflow", "hidden");
113
+ },
114
+
115
+ allowScroll: function() {
116
+ $("html, body").css("overflow", "");
117
+ },
93
118
  });
94
119
 
95
120
  /*************************************************************************
@@ -202,11 +227,17 @@ var Narou = (function() {
202
227
  $(document).trigger("click");
203
228
  }
204
229
  this.closed = false;
230
+ var caller = function() {
231
+ if (typeof callback === "function")
232
+ callback();
233
+ };
234
+ $(document).one("show.bs.dropdown", function() {
235
+ $("#context-menu").hide();
236
+ caller();
237
+ });
205
238
  Narou.popupMenu("#context-menu", pos, function() {
206
239
  $("#context-menu").hide();
207
- if (typeof callback === "function") {
208
- callback();
209
- }
240
+ caller();
210
241
  });
211
242
  },
212
243
 
@@ -230,12 +261,25 @@ var Narou = (function() {
230
261
 
231
262
  openSelectDiffListDialog: function(target_id) {
232
263
  $.get("/api/diff_list", { target: target_id }, function(html) {
233
- bootbox.dialog({
264
+ var diff_modal = bootbox.dialog({
234
265
  title: "表示したい差分を選択して下さい",
235
266
  message: html,
236
267
  backdrop: true,
237
268
  className: "diff-list-modal",
238
269
  buttons: {
270
+ clear: {
271
+ label: "差分を消去",
272
+ className: "btn-danger",
273
+ callback: function() {
274
+ bootbox.confirm("本当に消去してよろしいですか?", function(result) {
275
+ if (result) {
276
+ $.post("/api/diff_clean", { target: target_id });
277
+ }
278
+ diff_modal.modal("hide");
279
+ });
280
+ return false; // 親モーダルはこの時点では閉じさせない
281
+ }
282
+ },
239
283
  main: {
240
284
  label: "閉じる",
241
285
  className: "btn-default"
@@ -351,28 +395,39 @@ var Narou = (function() {
351
395
  },
352
396
 
353
397
  download: function() {
354
- var download_modal = bootbox.dialog({
355
- title: "ダウンロードする小説のURL、もしくはNコードを入力(複数可)",
356
- message: '<textarea id="download-input" class="form-control" rows="3"></textarea>',
357
- backdrop: true,
358
- buttons: {
359
- cancel: {
360
- label: "キャンセル",
361
- className: "btn-default"
362
- },
363
- main: {
364
- label: "OK",
365
- className: "btn-primary",
366
- callback: function() {
367
- var targets = $("#download-input").val();
368
- $.post("/api/download", { targets: targets });
369
- }
370
- }
371
- }
372
- });
373
- download_modal.one("shown.bs.modal", function () {
374
- $("#download-input").focus();
375
- });
398
+ var urls = [];
399
+ if (typeof arguments !== "undefined" && arguments.length > 0) {
400
+ urls = Array.prototype.slice.call(arguments);
401
+ }
402
+ if (urls.length == 0) {
403
+ $.get("/parts/download_form")
404
+ .done(function(html) {
405
+ var download_modal = bootbox.dialog({
406
+ title: "ダウンロードする小説のURL、もしくはNコードを入力(複数可)",
407
+ message: html,
408
+ backdrop: true,
409
+ buttons: {
410
+ cancel: {
411
+ label: "キャンセル",
412
+ className: "btn-default"
413
+ },
414
+ main: {
415
+ label: "ダウンロード",
416
+ className: "btn-primary",
417
+ callback: function() {
418
+ $("#download-link-submit").click();
419
+ }
420
+ }
421
+ }
422
+ });
423
+ download_modal.one("shown.bs.modal", function () {
424
+ $("#download-input").focus();
425
+ });
426
+ });
427
+ }
428
+ else {
429
+ $.post("/api/download", { targets: urls });
430
+ }
376
431
  },
377
432
 
378
433
  downloadForce: function() {
@@ -529,6 +584,28 @@ var Narou = (function() {
529
584
  if (ids.length === 0) return;
530
585
  $.post("/api/setting_burn", { "ids": ids });
531
586
  },
587
+
588
+ csvImport: function() {
589
+ $.get("/parts/csv_import", function(html) {
590
+ var box = bootbox.dialog({
591
+ title: "CSVファイルからのインポート",
592
+ message: html,
593
+ buttons: {
594
+ cancel: {
595
+ label: "キャンセル",
596
+ className: "btn-default",
597
+ },
598
+ main: {
599
+ label: "インポート",
600
+ className: "btn-primary",
601
+ callback: function() {
602
+ $("#csv-import-files").click();
603
+ }
604
+ }
605
+ }
606
+ });
607
+ });
608
+ },
532
609
  });
533
610
 
534
611
  /*************************************************************************
@@ -576,6 +653,9 @@ var Narou = (function() {
576
653
  $(this.options.buttons_id + " .console-trash").on("click", function(e) {
577
654
  self.trash_console();
578
655
  });
656
+ $(this.options.buttons_id + " .queue-cancel").on("click", function(e) {
657
+ $.post("/api/cancel");
658
+ });
579
659
  },
580
660
 
581
661
  init_events: function() {
@@ -600,15 +680,14 @@ var Narou = (function() {
600
680
  var self = this;
601
681
  var $progress = null;
602
682
  var createProgressHtml = function(percent) {
603
- return '<div class="progress"><div class="progress-bar progress-bar-success progress-bar-striped active" role="progressbar" aria-valuenow="' + percent + '" aria-valuemin="0" aria-valuemax="100" style="width:' + percent + '%"></div></div>';
683
+ return '<div class="progress"><div class="progress-bar progress-bar-success progress-bar-striped active" role="progressbar" style="width:' + percent + '%"></div></div>';
604
684
  };
605
685
  var initializeProgressbar = function(percent) {
606
686
  self.puts(createProgressHtml(percent));
607
687
  $progress = self.console.find(".progress > div");
608
688
  };
609
689
  var setProgressValue = function(step) {
610
- $progress.attr("aria-valuenow", step)
611
- .width(step + "%");
690
+ $progress.width(step + "%");
612
691
  };
613
692
  this.notification.on("progressbar.init", function() {
614
693
  initializeProgressbar(0);
@@ -623,13 +702,8 @@ var Narou = (function() {
623
702
  });
624
703
  this.notification.on("progressbar.clear", function() {
625
704
  if (!$progress) return;
626
- // 表示と通信のタイムラグで100%付近のが表示されないまま消えてしまうので、
627
- // 演出的にプログレスバーの消去を遅らせて、100%付近まで表示する
628
- setProgressValue(100);
629
705
  $progress = null;
630
- setTimeout(function() {
631
- $(".progress").parent("div").remove();
632
- }, 500);
706
+ $(".progress").parent("div").remove();
633
707
  });
634
708
  },
635
709
 
@@ -740,6 +814,7 @@ var Narou = (function() {
740
814
  $.post("/api/clear_history");
741
815
  },
742
816
 
817
+ // コンソールの内容をクリア
743
818
  clear: function() {
744
819
  this.console.find("div.console-line").remove();
745
820
  }
@@ -750,13 +825,13 @@ var Narou = (function() {
750
825
  *************************************************************************/
751
826
  var Tag = Narou.Tag = (function(table) {
752
827
  this.table = table;
828
+ this.registerEvents($("#tag-list-canvas"));
753
829
  this.updateCanvas();
754
830
  });
755
831
 
756
832
  $.extend(Tag.prototype, {
757
833
  updateCanvas: function() {
758
834
  var $canvas = $("#tag-list-canvas");
759
- this.registerEvents($canvas);
760
835
  $.get("/api/tag_list", function(source) {
761
836
  $canvas.html(source);
762
837
  });
@@ -773,6 +848,7 @@ var Narou = (function() {
773
848
  storage.set("tag_search", tag_name);
774
849
  storage.save();
775
850
  self.table.draw();
851
+ self.table.$("[data-toggle=tooltip]").tooltip("hide");
776
852
  }).on("mousedown", ".tag", args, function(e) {
777
853
  // 範囲選択モードでもクリック出来るように
778
854
  if (e.data.stop_bubbling) e.stopPropagation();
@@ -882,6 +958,162 @@ var Narou = (function() {
882
958
  },
883
959
  });
884
960
 
961
+ /*************************************************************************
962
+ * タグの色選択機能
963
+ *************************************************************************/
964
+ var SelectColorMenu = Narou.SelectColorMenu = (function() {
965
+ this.initializeEvents();
966
+ this.initializeMenuEvents();
967
+ this.$menu = $("#select-color-menu");
968
+ });
969
+
970
+ $.extend(SelectColorMenu.prototype, {
971
+ open: function(target_tag, pos, callback) {
972
+ var self = this;
973
+ this.target_tag = target_tag;
974
+ var caller = function() {
975
+ if (typeof callback === "function")
976
+ callback();
977
+ };
978
+ $(document).one("hide.bs.dropdown", function() {
979
+ self.$menu.hide();
980
+ });
981
+ Narou.popupMenu("#select-color-menu", pos, function() {
982
+ self.$menu.hide();
983
+ caller();
984
+ });
985
+ },
986
+
987
+ initializeEvents: function() {
988
+ var self = this;
989
+ $(document).on("click", ".select-color-button", function(e) {
990
+ e.stopPropagation();
991
+ var $this = $(this);
992
+ var pos = {
993
+ x: $this.offset().left + $this.width() + 10,
994
+ y: $this.offset().top - 20
995
+ }
996
+ self.open($(this).data("targetTag"), pos, function() {
997
+ });
998
+ });
999
+ },
1000
+
1001
+ initializeMenuEvents: function() {
1002
+ var self = this;
1003
+ var colors = ["green", "yellow", "blue", "magenta", "cyan", "red", "white"];
1004
+ $.each(colors, function(i, color) {
1005
+ $("#select-color-menu-" + color).on("click", function(e) {
1006
+ e.preventDefault();
1007
+ e.stopPropagation();
1008
+ $.post("/api/change_tag_color", {
1009
+ tag: self.target_tag,
1010
+ color: color
1011
+ });
1012
+ self.$menu.hide();
1013
+ });
1014
+ });
1015
+ },
1016
+ });
1017
+
1018
+ /*************************************************************************
1019
+ * ドラッグ&ドロップ関係
1020
+ *************************************************************************/
1021
+ var DragDrop = Narou.DragDrop = (function() {
1022
+ this.initializeValidateUrlRegexpList();
1023
+ });
1024
+
1025
+ $.extend(DragDrop.prototype, {
1026
+ initializeValidateUrlRegexpList: function() {
1027
+ if (typeof this.validate_url_regexp_list !== "undefined") return;
1028
+ var self = this;
1029
+ this.validate_url_regexp_list = [];
1030
+ $.ajax({
1031
+ type: "GET",
1032
+ url: "/api/validate_url_regexp_list",
1033
+ dataType: "json",
1034
+ success: function(json) {
1035
+ $.each(json, function(i, value) {
1036
+ self.validate_url_regexp_list.push(value);
1037
+ });
1038
+ }
1039
+ });
1040
+ },
1041
+
1042
+ isValidNovelUrl: function(url) {
1043
+ var result = false;
1044
+ $.each(this.validate_url_regexp_list, function(i, regexp) {
1045
+ if (url.match(regexp)) {
1046
+ result = true;
1047
+ return false; // break
1048
+ }
1049
+ });
1050
+ return result;
1051
+ },
1052
+
1053
+ getLinkText: function(event, callback) {
1054
+ var self = this;
1055
+ var data_transfer = event.dataTransfer;
1056
+ $.each(data_transfer.types, function(i, type) {
1057
+ if (type === "Files") {
1058
+ // ショートカットファイルがドラッグされた場合は中身からリンクを抜き出す
1059
+ $.each(data_transfer.files, function(i, file) {
1060
+ if (!self.isSupportFile(file.name)) return;
1061
+ var reader = new FileReader;
1062
+ reader.onload = function(e) {
1063
+ var link = self.parseLink(file.name, reader.result);
1064
+ if (link) callback(link);
1065
+ };
1066
+ reader.readAsText(file);
1067
+ });
1068
+ }
1069
+ else {
1070
+ // TextはIE用
1071
+ if (type === "text/plain" || type === "Text") {
1072
+ var link = data_transfer.getData(type);
1073
+ if (link.length > 0) {
1074
+ callback(link);
1075
+ }
1076
+ }
1077
+ }
1078
+ });
1079
+ return;
1080
+ },
1081
+
1082
+ isSupportFile: function(filename) {
1083
+ return !!filename.match(/\.(?:url|webloc)$/i);
1084
+ },
1085
+
1086
+ parseLink: function(filename, text) {
1087
+ filename.toLowerCase().match(/\.(url|webloc)$/);
1088
+ var link = null;
1089
+ switch (RegExp.$1) {
1090
+ case "url":
1091
+ link = this.parseLinkDotURL(text);
1092
+ break;
1093
+ case "webloc":
1094
+ link = this.parseLinkDotWebloc(text);
1095
+ break;
1096
+ }
1097
+ return link;
1098
+ },
1099
+
1100
+ // OSX で使われる .webloc 形式のショートカット
1101
+ parseLinkDotWebloc: function(text) {
1102
+ if (text.match(/<string>(.+?)<\/string>/i)) {
1103
+ return RegExp.$1;
1104
+ }
1105
+ return null;
1106
+ },
1107
+
1108
+ // Windows で使われる .URL 形式のショートカット
1109
+ parseLinkDotURL: function(text) {
1110
+ if (text.match(/URL=(.+)/i)) {
1111
+ return RegExp.$1;
1112
+ }
1113
+ return null;
1114
+ },
1115
+ });
1116
+
885
1117
  return Narou;
886
1118
  })();
887
1119