epuber 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|