markdownr 0.5.6 → 0.5.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e04e007ceb9d03b7cadc1cfbb9b7d6d4df51f83d82fecba4fc22d52623b50fe
4
- data.tar.gz: ed3c37ef77810c9cd2589314d9d3239fb77f48e0ac3e1ede670146db9ff2c1bc
3
+ metadata.gz: e9a74b1e181f0fd5361c73e64a6e00659451cf075e1e41ec4f45c0cee9167e9d
4
+ data.tar.gz: 44b5d07121159a9efadac6ee85bd4528d2490ec419b0752dbe15e1336e94eea2
5
5
  SHA512:
6
- metadata.gz: dc882e485d1eadb2415443b5056dca89534fe1b561118d508060b126b53332a507ee2ef375faa648444cdc0ef394179de172acdee13981d30d8f89737dba6bed
7
- data.tar.gz: 0feb715779946a7456559618e3493986520dcbbeeab7714caa7049e6d4d196a90471fbd90102e9b060ddee7c9f2028fbf6ab5215e9de85c0b793ec4d9776e18c
6
+ metadata.gz: 866416e07423170e5ec3c84e60da99682bfabb07b09cf1eb74f29e7e74f509c9982ec292641721ec0d054f5361bd6012fc00a34755ab46987c4462e63569926e
7
+ data.tar.gz: 1a18c15a57094d2a5d72670d760d82d1eeca80ab1c694cc550a7ff69e352c6dd9e889840e515b0a40c9e59a9b48e9aa85744d6983f37f33c572b90cdd39c28a8
@@ -580,6 +580,133 @@ module MarkdownServer
580
580
  out.length > 10_000 ? out[0, 10_000] : out
581
581
  end
582
582
 
583
+ def blueletterbible_html(html, url)
584
+ base = "https://www.blueletterbible.org"
585
+
586
+ # ── Word ──────────────────────────────────────────────────────────────
587
+ word = html[/<h6[^>]+class="lexTitle(?:Gk|Hb)"[^>]*>(.*?)<\/h6>/im, 1]
588
+ &.gsub(/<[^>]+>/, "")&.strip || ""
589
+
590
+ # ── Transliteration ───────────────────────────────────────────────────
591
+ transliteration = html[/<div[^>]+id="lexTrans".*?<em>(.*?)<\/em>/im, 1]&.strip || ""
592
+
593
+ # ── Pronunciation + audio ─────────────────────────────────────────────
594
+ pronunciation = html[/class="[^"]*lexicon-pronunc[^"]*"[^>]*>\s*([^\n<]{1,50})/i, 1]&.strip || ""
595
+ data_pronunc = html[/data-pronunc="([a-fA-F0-9]{20,})"/i, 1] || ""
596
+ audio_btn = if data_pronunc.length > 10
597
+ au = "#{base}/lang/lexicon/lexPronouncePlayer.cfm?skin=#{data_pronunc}"
598
+ %(<button onclick="var a=this._a||(this._a=new Audio('#{h(au)}'));a.currentTime=0;a.play();" ) +
599
+ %(style="background:none;border:none;cursor:pointer;padding:0 0 0 4px;font-size:1.1em;vertical-align:middle;" title="Play pronunciation">&#128266;</button>)
600
+ else
601
+ ""
602
+ end
603
+
604
+ # ── Part of speech ────────────────────────────────────────────────────
605
+ pos = html[/<div[^>]+id="lexPart".*?small-text-right"[^>]*>(.*?)<\/div>/im, 1]
606
+ &.gsub(/<[^>]+>/, "")&.strip || ""
607
+
608
+ # ── Info table ────────────────────────────────────────────────────────
609
+ info_rows = [
610
+ ["Word", h(word)],
611
+ ["Transliteration", "<em>#{h(transliteration)}</em>"],
612
+ ["Pronunciation", "#{h(pronunciation)}#{audio_btn}"],
613
+ ["Part of Speech", h(pos)],
614
+ ]
615
+ info_html = %(<table class="blb-table">) +
616
+ info_rows.map { |label, v|
617
+ %(<tr><th class="blb-th">#{h(label)}</th><td>#{v}</td></tr>)
618
+ }.join + "</table>"
619
+
620
+ # ── Inflections ───────────────────────────────────────────────────────
621
+ infl_html = ""
622
+ if (m = html.match(/<div\s[^>]*id="greek-tr-inflections"[^>]*>/im))
623
+ after_infl = html[m.end(0)..]
624
+ stop = after_infl.index(/<div\s[^>]*id="greek-(?:mgnt|lxx)-inflections"/i) || after_infl.length
625
+ infl_section = after_infl[0...stop]
626
+ inflections = []
627
+ infl_section.scan(/<div\s[^>]*class="greekInflection"[^>]*>(.*?)<\/div>\s*<\/div>/im) do |mv|
628
+ chunk = mv[0]
629
+ href = chunk[/href="([^"]+)"/i, 1]
630
+ gk = chunk[/<span[^>]+class="Gk"[^>]*>(.*?)<\/span>/im, 1]&.gsub(/<[^>]+>/, "")&.strip || ""
631
+ freq = chunk[/&#8212;\s*(\d+)x<\/a>/i, 1]&.to_i || 0
632
+ next if gk.empty? || freq.zero?
633
+ inflections << { word: gk, freq: freq,
634
+ href: href ? base + href.gsub("&amp;", "&") : nil }
635
+ end
636
+ inflections.sort_by! { |i| -i[:freq] }
637
+ if inflections.any?
638
+ rows = inflections.map { |i|
639
+ link = i[:href] ? %(<a href="#{h(i[:href])}" target="_blank" rel="noopener">#{h(i[:word])}</a>) : h(i[:word])
640
+ %(<tr><td>#{link}</td><td class="blb-right">#{i[:freq]}x</td></tr>)
641
+ }.join
642
+ infl_html = %(<h4 class="blb-heading">Inflections</h4>) +
643
+ %(<table class="blb-table"><thead><tr><th class="blb-th">Form</th>) +
644
+ %(<th class="blb-th blb-right">Count</th></tr></thead><tbody>#{rows}</tbody></table>)
645
+ end
646
+ end
647
+
648
+ # ── Biblical Usage ────────────────────────────────────────────────────
649
+ usage_html = ""
650
+ if (um = html.match(/<div[^>]+id="outlineBiblical"[^>]*>/im))
651
+ after_usage = html[um.end(0)..]
652
+ if (inner = after_usage.match(/\A\s*<div>([\s\S]*?)<\/div>\s*<\/div>/im))
653
+ cleaned = inner[1]
654
+ .gsub(/<(\/?)(\w+)[^>]*>/) { "<#{$1}#{$2.downcase}>" }
655
+ .gsub(/[ \t]+/, " ")
656
+ .strip
657
+ usage_html = %(<h4 class="blb-heading">Biblical Usage</h4><div class="blb-usage">#{cleaned}</div>)
658
+ end
659
+ end
660
+
661
+ # ── Concordance ───────────────────────────────────────────────────────
662
+ conc_html = ""
663
+ trans_name = html[/id="bibleTable"[^>]+data-translation="([^"]+)"/i, 1] || ""
664
+ verses = []
665
+ html.split(/<div\s[^>]*id="bVerse_\d+"[^>]*>/).drop(1).each do |chunk|
666
+ cite_href = chunk[/tablet-order-2[^>]*>[\s\S]{0,400}?href="([^"]+)"/im, 1] || ""
667
+ cite = chunk[/tablet-order-2[^>]*>[\s\S]{0,400}?<a[^>]*>(.*?)<\/a>/im, 1]
668
+ &.gsub(/<[^>]+>/, "")&.strip || ""
669
+
670
+ # Process verse HTML: highlight the matched word, strip all Strong's refs
671
+ raw_html = chunk[/class="EngBibleText[^"]*"[^>]*>([\s\S]*?)<\/div>/im, 1] || ""
672
+ raw_html.gsub!(/<img[^>]*>/, "")
673
+ raw_html.gsub!(/<a[^>]*class="hide-for-tablet"[^>]*>[\s\S]*?<\/a>/im, "")
674
+ raw_html.gsub!(/<span[^>]*class="hide-for-tablet"[^>]*>[\s\S]*?<\/span>/im, "")
675
+ verse_html = raw_html.gsub(/<span\s[^>]*class="word-phrase"[^>]*>([\s\S]*?)<\/span>/im) do
676
+ inner = $1
677
+ word = inner.sub(/<sup[\s\S]*/im, "").gsub(/<[^>]+>/, "")
678
+ .gsub(/&nbsp;/i, " ").strip
679
+ inner.match?(/<sup[^>]*class="[^"]*strongs criteria[^"]*"/i) ?
680
+ %(<span class="blb-match">#{h(word)}</span>) : h(word)
681
+ end
682
+ verse_html.gsub!(/<sup[^>]*>[\s\S]*?<\/sup>/im, "")
683
+ verse_html.gsub!(/<[^>]+>/, "")
684
+ verse_html.gsub!(/&nbsp;/i, " ")
685
+ verse_html.gsub!(/&#(\d+);/) { [$1.to_i].pack("U") rescue " " }
686
+ verse_html.gsub!(/&#x([\da-f]+);/i) { [$1.to_i(16)].pack("U") rescue " " }
687
+ verse_html.gsub!(/&amp;/, "&").gsub!(/&lt;/, "<").gsub!(/&gt;/, ">")
688
+ verse_html.gsub!(/\s+/, " ")
689
+ verse_html.strip!
690
+ # Strip the mobile citation prefix ("Mat 5:17 - ") left by hide-for-tablet removal
691
+ verse_html.sub!(/\A#{Regexp.escape(cite)}\s*-\s*/i, "")
692
+
693
+ next if cite.empty? || verse_html.empty?
694
+ full_href = cite_href.empty? ? nil : (cite_href.start_with?("http") ? cite_href : base + cite_href)
695
+ verses << { cite: cite, verse_html: verse_html, href: full_href }
696
+ end
697
+ if verses.any?
698
+ heading = trans_name.empty? ? "Concordance" : "Concordance (#{h(trans_name)})"
699
+ rows = verses.map { |v|
700
+ link = v[:href] ? %(<a href="#{h(v[:href])}" target="_blank" rel="noopener">#{h(v[:cite])}</a>) : h(v[:cite])
701
+ %(<tr><td class="blb-nowrap">#{link}</td><td>#{v[:verse_html]}</td></tr>)
702
+ }.join
703
+ conc_html = %(<h4 class="blb-heading">#{heading}</h4>) +
704
+ %(<table class="blb-table"><tbody>#{rows}</tbody></table>)
705
+ end
706
+
707
+ info_html + infl_html + usage_html + conc_html
708
+ end
709
+
583
710
  def compile_regexes(query)
584
711
  words = query.split(/\s+/).reject(&:empty?)
585
712
  return nil if words.empty?
@@ -680,8 +807,15 @@ module MarkdownServer
680
807
  html = fetch_external_page(url)
681
808
  halt 502, '{"error":"fetch failed"}' unless html
682
809
 
683
- title = page_title(html).sub(/ [-–] .*/, "").strip
684
- JSON.dump({ title: title, html: page_html(html, url) })
810
+ if url.match?(/blueletterbible\.org\/lexicon\//i)
811
+ raw = page_title(html)
812
+ title = raw.match(/^([GH]\d+ - \w+)/i)&.[](1)&.sub(" - ", " – ") ||
813
+ raw.sub(/ [-–] .*/, "").strip
814
+ JSON.dump({ title: title, html: blueletterbible_html(html, url) })
815
+ else
816
+ title = page_title(html).sub(/ [-–] .*/, "").strip
817
+ JSON.dump({ title: title, html: page_html(html, url) })
818
+ end
685
819
  end
686
820
 
687
821
  get "/search/?*" do
@@ -1,3 +1,3 @@
1
1
  module MarkdownServer
2
- VERSION = "0.5.6"
2
+ VERSION = "0.5.7"
3
3
  end
data/views/layout.erb CHANGED
@@ -909,6 +909,22 @@
909
909
  .link-preview-popup th, .link-preview-popup td { border: 1px solid #ddd; padding: 0.3rem 0.5rem; }
910
910
  .link-ctx-popup-body th, .link-preview-popup th { background: #f5f0e4; }
911
911
 
912
+ /* Blue Letter Bible popup tables */
913
+ .blb-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; margin-bottom: 0.6rem; }
914
+ .blb-table th, .blb-table td { padding: 3px 7px; border: 1px solid #ddd; }
915
+ .blb-th { text-align: left; font-weight: normal; background: #f5f0e4; color: #555; width: 38%; }
916
+ .blb-right { text-align: right; }
917
+ .blb-nowrap { white-space: nowrap; vertical-align: top; }
918
+ .blb-match { color: #b33; font-weight: 600; }
919
+ .blb-heading { font-size: 0.82rem; font-weight: 600; margin: 0.7rem 0 0.25rem; color: #555; text-transform: uppercase; letter-spacing: 0.04em; }
920
+ .blb-usage { font-size: 0.85rem; }
921
+ .blb-usage ol { margin: 0.1rem 0 0.1rem 1.3rem; padding: 0; list-style-type: decimal; }
922
+ .blb-usage ol ol { list-style-type: lower-alpha; }
923
+ .blb-usage ol ol ol { list-style-type: lower-roman; }
924
+ .blb-usage ol ol ol ol { list-style-type: lower-alpha; }
925
+ .blb-usage li { margin-bottom: 0.15rem; }
926
+ .blb-usage p { margin: 0; }
927
+
912
928
  /* Footnote tooltips */
913
929
  .footnote-tooltip {
914
930
  position: absolute;
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdownr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Dunn