epuber 0.3.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.
- checksums.yaml +7 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/bin/epuber +10 -0
- data/epuber.gemspec +51 -0
- data/lib/epuber.rb +14 -0
- data/lib/epuber/book.rb +267 -0
- data/lib/epuber/book/contributor.rb +90 -0
- data/lib/epuber/book/file_request.rb +68 -0
- data/lib/epuber/book/target.rb +284 -0
- data/lib/epuber/book/toc_item.rb +130 -0
- data/lib/epuber/checker.rb +19 -0
- data/lib/epuber/checker/text_checker.rb +129 -0
- data/lib/epuber/checker_transformer_base.rb +71 -0
- data/lib/epuber/command.rb +65 -0
- data/lib/epuber/command/compile.rb +142 -0
- data/lib/epuber/command/init.rb +145 -0
- data/lib/epuber/command/server.rb +59 -0
- data/lib/epuber/compiler.rb +255 -0
- data/lib/epuber/compiler/compilation_context.rb +86 -0
- data/lib/epuber/compiler/file_finders/abstract.rb +270 -0
- data/lib/epuber/compiler/file_finders/imaginary.rb +167 -0
- data/lib/epuber/compiler/file_finders/normal.rb +22 -0
- data/lib/epuber/compiler/file_resolver.rb +316 -0
- data/lib/epuber/compiler/file_types/abstract_file.rb +119 -0
- data/lib/epuber/compiler/file_types/bade_file.rb +39 -0
- data/lib/epuber/compiler/file_types/container_xml_file.rb +26 -0
- data/lib/epuber/compiler/file_types/generated_file.rb +29 -0
- data/lib/epuber/compiler/file_types/ibooks_display_options_file.rb +31 -0
- data/lib/epuber/compiler/file_types/image_file.rb +42 -0
- data/lib/epuber/compiler/file_types/mime_type_file.rb +20 -0
- data/lib/epuber/compiler/file_types/nav_file.rb +42 -0
- data/lib/epuber/compiler/file_types/opf_file.rb +27 -0
- data/lib/epuber/compiler/file_types/source_file.rb +52 -0
- data/lib/epuber/compiler/file_types/static_file.rb +18 -0
- data/lib/epuber/compiler/file_types/stylus_file.rb +20 -0
- data/lib/epuber/compiler/file_types/xhtml_file.rb +102 -0
- data/lib/epuber/compiler/generator.rb +67 -0
- data/lib/epuber/compiler/meta_inf_generator.rb +41 -0
- data/lib/epuber/compiler/nav_generator.rb +201 -0
- data/lib/epuber/compiler/opf_generator.rb +284 -0
- data/lib/epuber/compiler/xhtml_processor.rb +254 -0
- data/lib/epuber/config.rb +133 -0
- data/lib/epuber/dsl/attribute.rb +248 -0
- data/lib/epuber/dsl/attribute_support.rb +130 -0
- data/lib/epuber/dsl/object.rb +145 -0
- data/lib/epuber/dsl/tree_object.rb +101 -0
- data/lib/epuber/helper.rb +19 -0
- data/lib/epuber/lockfile.rb +55 -0
- data/lib/epuber/plugin.rb +108 -0
- data/lib/epuber/ruby_extensions/match_data.rb +40 -0
- data/lib/epuber/ruby_extensions/thread.rb +13 -0
- data/lib/epuber/server.rb +614 -0
- data/lib/epuber/server/auto_refresh/auto_refresh.coffee +97 -0
- data/lib/epuber/server/auto_refresh/connector.coffee +125 -0
- data/lib/epuber/server/auto_refresh/protocol.coffee +41 -0
- data/lib/epuber/server/auto_refresh/reloader.coffee +261 -0
- data/lib/epuber/server/base.styl +3 -0
- data/lib/epuber/server/basic.styl +211 -0
- data/lib/epuber/server/book_content.styl +39 -0
- data/lib/epuber/server/default_cover.png +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
- data/lib/epuber/server/handlers.rb +67 -0
- data/lib/epuber/server/keyboard_control.coffee +6 -0
- data/lib/epuber/server/pages/book.bade +75 -0
- data/lib/epuber/server/pages/common.bade +59 -0
- data/lib/epuber/server/pages/files.bade +17 -0
- data/lib/epuber/server/pages/toc.bade +29 -0
- data/lib/epuber/server/support.coffee +10 -0
- data/lib/epuber/templates/template.bookspec +143 -0
- data/lib/epuber/third_party/bower.rb +22 -0
- data/lib/epuber/third_party/bower/bower.json +10 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/bower.json +10 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.d.ts +33 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.js +173 -0
- data/lib/epuber/third_party/bower/bower_components/cookies-js/dist/cookies.min.js +6 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/MIT-LICENSE.txt +21 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/bower.json +28 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.js +9210 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/dist/jquery.min.map +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax.js +786 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/jsonp.js +89 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/load.js +75 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseJSON.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/parseXML.js +28 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/script.js +64 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/nonce.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/var/rquery.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/ajax/xhr.js +136 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes.js +11 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/attr.js +141 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/classes.js +158 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/prop.js +94 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/support.js +35 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/attributes/val.js +161 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/callbacks.js +205 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core.js +502 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/access.js +60 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/init.js +123 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/parseHTML.js +39 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/ready.js +97 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/core/var/rsingleTag.js +4 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css.js +450 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/addGetHookIf.js +22 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/curCSS.js +57 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/defaultDisplay.js +70 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/hiddenVisibleSelectors.js +15 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/support.js +96 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/swap.js +28 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/cssExpand.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/getStyles.js +12 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/isHidden.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rmargin.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/css/var/rnumnonpx.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data.js +178 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/Data.js +181 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/accepts.js +20 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_priv.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/data/var/data_user.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/deferred.js +149 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/deprecated.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/dimensions.js +50 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/effects.js +648 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/Tween.js +114 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/effects/animatedSelector.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event.js +868 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event/ajax.js +13 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event/alias.js +39 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/event/support.js +9 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/amd.js +24 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/exports/global.js +32 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/intro.js +44 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/jquery.js +37 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation.js +580 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/_evalUrl.js +18 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/support.js +32 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/manipulation/var/rcheckableType.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/offset.js +207 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/outro.js +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/queue.js +142 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/queue/delay.js +22 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-native.js +172 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/selector-sizzle.js +14 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/selector.js +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/serialize.js +111 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.js +2067 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/sizzle/dist/sizzle.min.map +1 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing.js +199 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/findFilter.js +100 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/traversing/var/rneedsContext.js +6 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/arr.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/class2type.js +4 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/concat.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/hasOwn.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/indexOf.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/pnum.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/push.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/rnotwhite.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/slice.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/strundefined.js +3 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/support.js +4 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/var/toString.js +5 -0
- data/lib/epuber/third_party/bower/bower_components/jquery/src/wrap.js +79 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/MIT-LICENSE +20 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/Makefile +4 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/README.markdown +212 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/bower.json +27 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/keymaster.js +296 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/package.json +11 -0
- data/lib/epuber/third_party/bower/bower_components/keymaster/test.html +93 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/LICENSE.txt +21 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/README.md +21 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/bower.json +18 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/jquery.spin.js +80 -0
- data/lib/epuber/third_party/bower/bower_components/spin.js/spin.js +337 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/LICENSE.txt +21 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/README.md +534 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/bower.json +16 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/contributing.md +19 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/IPv6.js +188 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/SecondLevelDomains.js +241 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentQuery.js +104 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.fragmentURI.js +97 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.js +2115 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URI.min.js +86 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/URITemplate.js +499 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.js +235 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/jquery.URI.min.js +7 -0
- data/lib/epuber/third_party/bower/bower_components/uri.js/src/punycode.js +508 -0
- data/lib/epuber/transformer.rb +19 -0
- data/lib/epuber/transformer/text_transformer.rb +60 -0
- data/lib/epuber/user_interface.rb +186 -0
- data/lib/epuber/vendor/globals_context.rb +26 -0
- data/lib/epuber/vendor/hash_binding.rb +26 -0
- data/lib/epuber/vendor/nokogiri_extensions.rb +30 -0
- data/lib/epuber/vendor/ruby_templater.rb +71 -0
- data/lib/epuber/vendor/size.rb +20 -0
- data/lib/epuber/vendor/version.rb +83 -0
- data/lib/epuber/version.rb +4 -0
- metadata +556 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
|
7
|
+
module Epuber
|
8
|
+
class Compiler
|
9
|
+
require_relative 'file_finders/normal'
|
10
|
+
|
11
|
+
class XHTMLProcessor
|
12
|
+
class UnparseableLinkError < StandardError; end
|
13
|
+
|
14
|
+
# Method for parsing incomplete XML, supports multiple root elements
|
15
|
+
#
|
16
|
+
# @warning Because of nature of XML, when input string don't contain root element, it will create own called `body`, since it will be used in next steps.
|
17
|
+
#
|
18
|
+
# @param [String] text input XHTML text
|
19
|
+
#
|
20
|
+
# @return [Nokogiri::XML::Document] parsed document
|
21
|
+
#
|
22
|
+
def self.xml_document_from_string(text, file_path = nil)
|
23
|
+
if /\A[\n\r ]+(<\?xml)/ =~ text
|
24
|
+
UI.warning('XML header must be at the beginning of document', location: UI::Location.new(file_path, 1))
|
25
|
+
|
26
|
+
text = text.lstrip
|
27
|
+
end
|
28
|
+
|
29
|
+
doc = Nokogiri::XML(text)
|
30
|
+
doc.encoding = 'UTF-8'
|
31
|
+
doc.file_path = file_path
|
32
|
+
|
33
|
+
fragment = Nokogiri::XML.fragment(text)
|
34
|
+
root_elements = fragment.children.select { |el| el.element? }
|
35
|
+
|
36
|
+
if root_elements.count == 1
|
37
|
+
doc.root = root_elements.first
|
38
|
+
elsif fragment.at_css('body').nil?
|
39
|
+
doc.root = doc.create_element('body')
|
40
|
+
|
41
|
+
fragment.children.select do |child|
|
42
|
+
child.element? || child.comment? || child.text?
|
43
|
+
end.each do |child|
|
44
|
+
doc.root.add_child(child)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
doc
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Method to add all missing items in XML root
|
53
|
+
#
|
54
|
+
# Required items:
|
55
|
+
# - html (with all namespaces and other attributes)
|
56
|
+
# - body
|
57
|
+
# - head (with title)
|
58
|
+
#
|
59
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
60
|
+
# @param [String] title title of this document, since this is required by EPUB specification
|
61
|
+
# @param [Epuber::Version] epub_version version of result EPUB
|
62
|
+
#
|
63
|
+
# @return nil
|
64
|
+
#
|
65
|
+
def self.add_missing_root_elements(xhtml_doc, title, epub_version)
|
66
|
+
# add missing body element
|
67
|
+
if xhtml_doc.at_css('body').nil?
|
68
|
+
xhtml_doc.root.surround_with_element('body')
|
69
|
+
end
|
70
|
+
|
71
|
+
# add missing root html element
|
72
|
+
if xhtml_doc.at_css('html').nil?
|
73
|
+
attrs = {}
|
74
|
+
attrs['xmlns'] = 'http://www.w3.org/1999/xhtml'
|
75
|
+
attrs['xmlns:epub'] = 'http://www.idpf.org/2007/ops' if epub_version >= 3
|
76
|
+
xhtml_doc.root.surround_with_element('html', attrs)
|
77
|
+
end
|
78
|
+
|
79
|
+
# add missing head in html
|
80
|
+
if xhtml_doc.at_css('html > head').nil?
|
81
|
+
html = xhtml_doc.css('html').first
|
82
|
+
head = xhtml_doc.create_element('head')
|
83
|
+
head << xhtml_doc.create_element('title', title)
|
84
|
+
head << xhtml_doc.create_element('meta', charset: 'utf-8') if epub_version >= 3.0
|
85
|
+
|
86
|
+
html.children.first.before(head)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Method for adding style sheets with links, method will not add duplicate items
|
91
|
+
#
|
92
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
93
|
+
# @param [Array<String>] styles links to files
|
94
|
+
#
|
95
|
+
# @return nil
|
96
|
+
#
|
97
|
+
def self.add_styles(xhtml_doc, styles)
|
98
|
+
head = xhtml_doc.at_css('html > head')
|
99
|
+
old_links = head.css('link[rel="stylesheet"]').map { |node| node['href'] }
|
100
|
+
|
101
|
+
links_to_add = styles - old_links
|
102
|
+
|
103
|
+
links_to_add.each do |path|
|
104
|
+
head << xhtml_doc.create_element('link', href: path, rel: 'stylesheet', type: 'text/css')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Adds viewport meta tag to head of some document, but only if there is not some existing tag
|
109
|
+
#
|
110
|
+
# @param [Nokogiri::XML::Document] xhtml_doc
|
111
|
+
# @param [Epuber::Size] viewport_size
|
112
|
+
#
|
113
|
+
def self.add_viewport(xhtml_doc, viewport_size)
|
114
|
+
head = xhtml_doc.at_css('html > head')
|
115
|
+
return unless head.at_css("meta[name='viewport']").nil?
|
116
|
+
|
117
|
+
s = viewport_size
|
118
|
+
head << xhtml_doc.create_element('meta', name: 'viewport', content: "width=#{s.width},height=#{s.height}")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Method which will resolve path to file from pattern
|
122
|
+
#
|
123
|
+
# @param [String] path pattern or path of the file
|
124
|
+
# @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching for file from tag <img>
|
125
|
+
# @param [String] file_path path to file from which is searching for other file
|
126
|
+
# @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
|
127
|
+
#
|
128
|
+
# @raise UnparseableLinkError, FileFinder::FileNotFoundError, FileFinder::MultipleFilesFoundError
|
129
|
+
#
|
130
|
+
# @return [URI] resolved path to file or remote web page
|
131
|
+
#
|
132
|
+
def self.resolved_link_to_file(path, groups, file_path, file_finder)
|
133
|
+
raise FileFinders::FileNotFoundError.new(path, file_path) if path.empty?
|
134
|
+
|
135
|
+
begin
|
136
|
+
uri = URI(path)
|
137
|
+
rescue URI::InvalidURIError
|
138
|
+
begin
|
139
|
+
uri = URI(URI::encode(path))
|
140
|
+
rescue URI::InvalidURIError
|
141
|
+
# skip not valid uri
|
142
|
+
raise UnparseableLinkError, "Unparseable link `#{path}`"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# skip uri with scheme (links to web pages)
|
147
|
+
return uri unless uri.scheme.nil?
|
148
|
+
|
149
|
+
# skip empty path
|
150
|
+
return uri if uri.path.empty? && !uri.fragment.nil? && !uri.fragment.empty?
|
151
|
+
|
152
|
+
uri.path = file_finder.find_file(uri.path, groups: groups, context_path: file_path)
|
153
|
+
|
154
|
+
uri
|
155
|
+
end
|
156
|
+
|
157
|
+
# Resolves all links to files in XHTML document and returns the valid and resolved versions
|
158
|
+
#
|
159
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
160
|
+
# @param [String] tag_name CSS selector for tag
|
161
|
+
# @param [String] attribute_name name of attribute
|
162
|
+
# @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching for file from tag <img>
|
163
|
+
# @param [String] file_path path to file from which is searching for other file
|
164
|
+
# @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
|
165
|
+
#
|
166
|
+
# @return [Array<URI>] resolved links
|
167
|
+
#
|
168
|
+
def self.resolve_links_for(xhtml_doc, tag_name, attribute_name, groups, file_path, file_finder)
|
169
|
+
founded_links = []
|
170
|
+
|
171
|
+
xhtml_doc.css("#{tag_name}[#{attribute_name}]").each do |node|
|
172
|
+
begin
|
173
|
+
src = node[attribute_name]
|
174
|
+
# @type [String] src
|
175
|
+
|
176
|
+
next if src.nil?
|
177
|
+
|
178
|
+
target_file = resolved_link_to_file(src, groups, file_path, file_finder)
|
179
|
+
founded_links << target_file
|
180
|
+
|
181
|
+
node[attribute_name] = target_file.to_s
|
182
|
+
rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
|
183
|
+
UI.warning(e.to_s, location: node)
|
184
|
+
|
185
|
+
# skip not found files
|
186
|
+
next
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
founded_links
|
191
|
+
end
|
192
|
+
|
193
|
+
# Resolves all links to files in XHTML document and returns the valid and resolved versions
|
194
|
+
#
|
195
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
196
|
+
# @param [String] file_path path to file from which is searching for other file
|
197
|
+
# @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
|
198
|
+
#
|
199
|
+
# @return [Array<URI>] resolved links
|
200
|
+
#
|
201
|
+
def self.resolve_links(xhtml_doc, file_path, file_finder)
|
202
|
+
[
|
203
|
+
resolve_links_for(xhtml_doc, 'a', 'href', :text, file_path, file_finder),
|
204
|
+
resolve_links_for(xhtml_doc, 'map > area', 'href', :text, file_path, file_finder),
|
205
|
+
].flatten
|
206
|
+
end
|
207
|
+
|
208
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
209
|
+
#
|
210
|
+
# @return [Bool]
|
211
|
+
#
|
212
|
+
def self.using_javascript?(xhtml_doc)
|
213
|
+
!xhtml_doc.at_css('script').nil?
|
214
|
+
end
|
215
|
+
|
216
|
+
# @param [Nokogiri::XML::Document] xhtml_doc
|
217
|
+
# @param [String] file_path path of referring file
|
218
|
+
# @param [FileResolver] file_resolver
|
219
|
+
#
|
220
|
+
# @return nil
|
221
|
+
#
|
222
|
+
def self.resolve_images(xhtml_doc, file_path, file_resolver)
|
223
|
+
dirname = File.dirname(file_path)
|
224
|
+
|
225
|
+
xhtml_doc.css('img').each do |img|
|
226
|
+
path = img['src']
|
227
|
+
next if path.nil?
|
228
|
+
|
229
|
+
begin
|
230
|
+
new_path = file_resolver.dest_finder.find_file(path, groups: :image, context_path: dirname)
|
231
|
+
|
232
|
+
rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError
|
233
|
+
begin
|
234
|
+
new_path = resolved_link_to_file(path, :image, dirname, file_resolver.source_finder).to_s
|
235
|
+
pkg_abs_path = File.expand_path(new_path, dirname).unicode_normalize
|
236
|
+
pkg_new_path = Pathname.new(pkg_abs_path).relative_path_from(Pathname.new(file_resolver.source_path)).to_s
|
237
|
+
|
238
|
+
file = FileTypes::ImageFile.new(pkg_new_path)
|
239
|
+
file.path_type = :manifest
|
240
|
+
file_resolver.add_file(file)
|
241
|
+
|
242
|
+
rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
|
243
|
+
UI.warning(e.to_s, location: img)
|
244
|
+
|
245
|
+
next
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
img['src'] = new_path
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative 'lockfile'
|
4
|
+
|
5
|
+
|
6
|
+
module Epuber
|
7
|
+
class Config
|
8
|
+
WORKING_PATH = '.epuber'
|
9
|
+
|
10
|
+
# @return [String]
|
11
|
+
#
|
12
|
+
def project_path
|
13
|
+
@project_path ||= Dir.pwd.unicode_normalize
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param of_file [String] absolute path to file
|
17
|
+
#
|
18
|
+
# @return [String] relative path to file from root of project
|
19
|
+
#
|
20
|
+
def pretty_path_from_project(of_file)
|
21
|
+
Pathname.new(of_file.unicode_normalize).relative_path_from(Pathname.new(project_path))
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
#
|
26
|
+
def working_path
|
27
|
+
@working_path ||= File.join(project_path, WORKING_PATH)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
#
|
32
|
+
def bookspec_path
|
33
|
+
@bookspec_path ||= find_all_bookspecs.first
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String]
|
37
|
+
#
|
38
|
+
def bookspec_lockfile_path
|
39
|
+
"#{bookspec_path}.lock"
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Array<String>]
|
43
|
+
#
|
44
|
+
def find_all_bookspecs
|
45
|
+
Dir.chdir(project_path) do
|
46
|
+
Dir.glob('*.bookspec').map do |path|
|
47
|
+
File.expand_path(path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Epuber::Book]
|
53
|
+
#
|
54
|
+
def bookspec
|
55
|
+
require_relative 'book'
|
56
|
+
@bookspec ||= (
|
57
|
+
book = Epuber::Book.from_file(bookspec_path)
|
58
|
+
book.finish_toc
|
59
|
+
book.validate
|
60
|
+
book.freeze
|
61
|
+
book
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @param [Epuber::Book] bookspec
|
66
|
+
#
|
67
|
+
# @return [Epuber::Book]
|
68
|
+
#
|
69
|
+
def bookspec=(bookspec)
|
70
|
+
@bookspec = bookspec
|
71
|
+
end
|
72
|
+
|
73
|
+
# @return [Epuber::Lockfile]
|
74
|
+
#
|
75
|
+
def bookspec_lockfile
|
76
|
+
@bookspec_lockfile ||= (
|
77
|
+
lockfile = Lockfile.from_file(bookspec_lockfile_path)
|
78
|
+
lockfile.version = Epuber::VERSION
|
79
|
+
lockfile
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return nil
|
84
|
+
#
|
85
|
+
def save_lockfile
|
86
|
+
bookspec_lockfile.write_to_file
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param target [Epuber::Book::Target]
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
#
|
93
|
+
def build_path(target)
|
94
|
+
File.join(working_path, 'build', target.name.to_s)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param target [Epuber::Book::Target]
|
98
|
+
#
|
99
|
+
# @return [String]
|
100
|
+
#
|
101
|
+
def release_build_path(target)
|
102
|
+
File.join(working_path, 'release_build', target.name.to_s)
|
103
|
+
end
|
104
|
+
|
105
|
+
# ---------------------------------------------------------------------------------------------------------------- #
|
106
|
+
|
107
|
+
# Singleton
|
108
|
+
#
|
109
|
+
# @return [Epuber::Config]
|
110
|
+
#
|
111
|
+
def self.instance
|
112
|
+
@instance ||= new
|
113
|
+
end
|
114
|
+
|
115
|
+
class << self
|
116
|
+
attr_accessor :test
|
117
|
+
|
118
|
+
def test?
|
119
|
+
test
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
self.test = false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# HACK: this should be done with nicer way
|
128
|
+
#
|
129
|
+
at_exit do
|
130
|
+
unless Epuber::Config.test?
|
131
|
+
Epuber::Config.instance.save_lockfile
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
|
7
|
+
module Epuber
|
8
|
+
module DSL
|
9
|
+
# Stores the information of an attribute. It also provides logic to implement any required logic.
|
10
|
+
#
|
11
|
+
class Attribute
|
12
|
+
# @return [Symbol] name of attribute
|
13
|
+
#
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# rubocop:disable Metrics/ParameterLists
|
17
|
+
|
18
|
+
# Returns a new attribute initialized with the given options.
|
19
|
+
#
|
20
|
+
# @param name [Symbol]
|
21
|
+
#
|
22
|
+
# @see #name
|
23
|
+
#
|
24
|
+
# @raise If there are unrecognized options.
|
25
|
+
#
|
26
|
+
def initialize(name, inherited: false,
|
27
|
+
root_only: false,
|
28
|
+
required: false,
|
29
|
+
singularize: false,
|
30
|
+
file_patterns: false,
|
31
|
+
container: nil,
|
32
|
+
keys: nil,
|
33
|
+
default_value: nil,
|
34
|
+
auto_convert: {},
|
35
|
+
types: nil)
|
36
|
+
|
37
|
+
@name = name
|
38
|
+
|
39
|
+
@inherited = inherited
|
40
|
+
@root_only = root_only
|
41
|
+
@required = required
|
42
|
+
@singularize = singularize
|
43
|
+
@file_patterns = file_patterns
|
44
|
+
@container = container
|
45
|
+
@keys = keys
|
46
|
+
@default_value = default_value
|
47
|
+
@auto_convert = auto_convert
|
48
|
+
@types = if !types.nil?
|
49
|
+
types
|
50
|
+
elsif @default_value
|
51
|
+
[@default_value.class]
|
52
|
+
else
|
53
|
+
[String]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# rubocop:enable Metrics/ParameterLists
|
58
|
+
|
59
|
+
# @return [String] A string representation suitable for UI.
|
60
|
+
#
|
61
|
+
def to_s
|
62
|
+
"Attribute `#{name}`"
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [String] A string representation suitable for debugging.
|
66
|
+
#
|
67
|
+
def inspect
|
68
|
+
"<#{self.class} name=#{name} types=#{types}>"
|
69
|
+
end
|
70
|
+
|
71
|
+
#---------------------------------------------------------------------#
|
72
|
+
|
73
|
+
# @!group Options
|
74
|
+
|
75
|
+
# @return [Array<Class>] the list of the classes of the values supported by the attribute writer.
|
76
|
+
# If not specified defaults to [String].
|
77
|
+
#
|
78
|
+
attr_reader :types
|
79
|
+
|
80
|
+
# @return [Array<Class>] the list of the classes of the values supported by the attribute, including
|
81
|
+
# the container.
|
82
|
+
#
|
83
|
+
def supported_types
|
84
|
+
@supported_types ||= @types.dup.push(container).compact
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Class] if defined it can be [Array] or [Hash]. It is used as default initialization value
|
88
|
+
# and to automatically wrap other values to arrays.
|
89
|
+
#
|
90
|
+
attr_reader :container
|
91
|
+
|
92
|
+
# @return [Array, Hash] the list of the accepted keys for an attribute wrapped by a Hash.
|
93
|
+
#
|
94
|
+
# @note A hash is accepted to group the keys associated only with certain keys (see the source
|
95
|
+
# attribute of a Book).
|
96
|
+
#
|
97
|
+
attr_reader :keys
|
98
|
+
|
99
|
+
# @return [Object] if the attribute follows configuration over convention it can specify a default value.
|
100
|
+
#
|
101
|
+
# @note The default value is not automatically wrapped and should be specified within the container
|
102
|
+
# if any.
|
103
|
+
#
|
104
|
+
attr_reader :default_value
|
105
|
+
|
106
|
+
# @return [Bool] whether the specification should be considered invalid if a value for the attribute
|
107
|
+
# is not specified.
|
108
|
+
#
|
109
|
+
attr_reader :required
|
110
|
+
alias_method :required?, :required
|
111
|
+
|
112
|
+
# @return [Bool] whether the attribute should be specified only on the root specification.
|
113
|
+
#
|
114
|
+
attr_reader :root_only
|
115
|
+
alias_method :root_only?, :root_only
|
116
|
+
|
117
|
+
|
118
|
+
# @return [Bool] whether there should be a singular alias for the attribute writer.
|
119
|
+
#
|
120
|
+
attr_reader :singularize
|
121
|
+
alias_method :singularize?, :singularize
|
122
|
+
|
123
|
+
# @return [Bool] whether the attribute describes file patterns.
|
124
|
+
#
|
125
|
+
# @note This is mostly used by the linter.
|
126
|
+
#
|
127
|
+
attr_reader :file_patterns
|
128
|
+
alias_method :file_patterns?, :file_patterns
|
129
|
+
|
130
|
+
# @return [Bool] defines whether the attribute reader should join the values with the parent.
|
131
|
+
#
|
132
|
+
# @note Attributes stored in wrappers are always inherited.
|
133
|
+
#
|
134
|
+
def inherited?
|
135
|
+
!root_only? && @inherited
|
136
|
+
end
|
137
|
+
|
138
|
+
#---------------------------------------------------------------------#
|
139
|
+
|
140
|
+
# @return [String] the name of the setter method for the attribute.
|
141
|
+
#
|
142
|
+
def writer_name
|
143
|
+
"#{name}="
|
144
|
+
end
|
145
|
+
|
146
|
+
# @return [String] an aliased attribute writer offered for convenience on the DSL.
|
147
|
+
#
|
148
|
+
def writer_singular_form
|
149
|
+
"#{name.to_s.singularize}=" if singularize?
|
150
|
+
end
|
151
|
+
|
152
|
+
#---------------------------------------------------------------------#
|
153
|
+
|
154
|
+
# @!group Values validation
|
155
|
+
|
156
|
+
# Validates the value for an attribute. This validation should be performed before the value is
|
157
|
+
# prepared or wrapped.
|
158
|
+
#
|
159
|
+
# @note The this is called before preparing the value.
|
160
|
+
#
|
161
|
+
# @raise If the type is not in the allowed list.
|
162
|
+
#
|
163
|
+
# @return [void]
|
164
|
+
#
|
165
|
+
def validate_type(value)
|
166
|
+
return if value.nil?
|
167
|
+
return if supported_types.any? { |klass| value.class == klass }
|
168
|
+
|
169
|
+
raise StandardError, "Non acceptable type `#{value.class}` for #{self}. Allowed types: `#{types.inspect}`"
|
170
|
+
end
|
171
|
+
|
172
|
+
# Validates a value before storing.
|
173
|
+
#
|
174
|
+
# @raise If a root only attribute is set in a subspec.
|
175
|
+
#
|
176
|
+
# @raise If a unknown key is added to a hash.
|
177
|
+
#
|
178
|
+
# @return [void]
|
179
|
+
#
|
180
|
+
def validate_for_writing(spec, value)
|
181
|
+
if root_only? && !spec.root?
|
182
|
+
raise StandardError, "Can't set `#{name}` attribute for subspecs (in `#{spec.name}`)."
|
183
|
+
end
|
184
|
+
|
185
|
+
if keys
|
186
|
+
value.keys.each do |key|
|
187
|
+
unless allowed_keys.include?(key)
|
188
|
+
raise StandardError, "Unknown key `#{key}` for #{self}. Allowed keys: `#{allowed_keys.inspect}`"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# @return [Array] the flattened list of the allowed keys for the hash of a given specification.
|
194
|
+
#
|
195
|
+
def allowed_keys
|
196
|
+
if keys.is_a?(Hash)
|
197
|
+
keys.keys.concat(keys.values.flatten.compact)
|
198
|
+
else
|
199
|
+
keys
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
#---------------------------------------------------------------------#
|
206
|
+
|
207
|
+
# @!group Automatic conversion
|
208
|
+
|
209
|
+
# Converts value to compatible type of attribute
|
210
|
+
#
|
211
|
+
# Can be configured with option :auto_convert
|
212
|
+
# Supports conversion from type to type, eg `{ String => Fixnum }`
|
213
|
+
# also from types to type eg `{ [String, Date] => Fixnum }`
|
214
|
+
# Supports custom conversion with Proc, eg `{ String => lambda { |value| value.to_s } }`
|
215
|
+
# also with multiple types
|
216
|
+
#
|
217
|
+
def converted_value(value)
|
218
|
+
begin
|
219
|
+
validate_type(value)
|
220
|
+
rescue StandardError
|
221
|
+
raise if @auto_convert.nil?
|
222
|
+
dest_class = @auto_convert[value.class]
|
223
|
+
|
224
|
+
if dest_class.nil?
|
225
|
+
array_keys = @auto_convert.select { |k, _v| k.is_a?(Array) }
|
226
|
+
array_keys_with_type = array_keys.select { |k, _v| k.include?(value.class) }
|
227
|
+
|
228
|
+
if array_keys_with_type.count > 0
|
229
|
+
dest_class = array_keys_with_type.values.first
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
if dest_class.respond_to?(:call)
|
234
|
+
return dest_class.call(value)
|
235
|
+
elsif dest_class.respond_to?(:parse)
|
236
|
+
return dest_class.parse(value)
|
237
|
+
elsif dest_class.respond_to?(:new)
|
238
|
+
return dest_class.new(value)
|
239
|
+
else
|
240
|
+
raise StandardError, "Object/class #{dest_class} doesn't support any convert method (#call, .parse or implicit .new)"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
value
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|