web_resource_bundler 0.0.13
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.
- data/.bundle/config +2 -0
- data/.gitignore +21 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +10 -0
- data/README +118 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/lib/web_resource_bundler.rb +195 -0
- data/lib/web_resource_bundler/content_management/block_data.rb +57 -0
- data/lib/web_resource_bundler/content_management/block_parser.rb +63 -0
- data/lib/web_resource_bundler/content_management/css_url_rewriter.rb +23 -0
- data/lib/web_resource_bundler/content_management/resource_file.rb +35 -0
- data/lib/web_resource_bundler/exceptions.rb +26 -0
- data/lib/web_resource_bundler/file_manager.rb +30 -0
- data/lib/web_resource_bundler/filters.rb +9 -0
- data/lib/web_resource_bundler/filters/base_filter.rb +28 -0
- data/lib/web_resource_bundler/filters/bundle_filter.rb +58 -0
- data/lib/web_resource_bundler/filters/bundle_filter/resource_packager.rb +49 -0
- data/lib/web_resource_bundler/filters/cdn_filter.rb +48 -0
- data/lib/web_resource_bundler/filters/image_encode_filter.rb +56 -0
- data/lib/web_resource_bundler/filters/image_encode_filter/css_generator.rb +85 -0
- data/lib/web_resource_bundler/filters/image_encode_filter/image_data.rb +51 -0
- data/lib/web_resource_bundler/rails_app_helpers.rb +65 -0
- data/lib/web_resource_bundler/settings.rb +46 -0
- data/lib/web_resource_bundler/web_resource_bundler_init.rb +17 -0
- data/spec/public/foo.css +4 -0
- data/spec/public/images/good.jpg +0 -0
- data/spec/public/images/logo.jpg +0 -0
- data/spec/public/images/sdfo.jpg +0 -0
- data/spec/public/images/too_big_image.jpg +0 -0
- data/spec/public/marketing.js +14 -0
- data/spec/public/salog20.js +6 -0
- data/spec/public/sample.css +6 -0
- data/spec/public/seal.js +10 -0
- data/spec/public/set_cookies.js +8 -0
- data/spec/public/styles/boo.css +4 -0
- data/spec/public/styles/for_import.css +7 -0
- data/spec/public/temp.css +1 -0
- data/spec/public/test.css +2 -0
- data/spec/sample_block_helper.rb +81 -0
- data/spec/spec_helper.rb +82 -0
- data/spec/web_resource_bundler/content_management/block_data_spec.rb +33 -0
- data/spec/web_resource_bundler/content_management/block_parser_spec.rb +100 -0
- data/spec/web_resource_bundler/content_management/css_url_rewriter_spec.rb +27 -0
- data/spec/web_resource_bundler/content_management/resource_file_spec.rb +37 -0
- data/spec/web_resource_bundler/file_manager_spec.rb +60 -0
- data/spec/web_resource_bundler/filters/bundle_filter/filter_spec.rb +40 -0
- data/spec/web_resource_bundler/filters/bundle_filter/resource_packager_spec.rb +41 -0
- data/spec/web_resource_bundler/filters/cdn_filter_spec.rb +76 -0
- data/spec/web_resource_bundler/filters/image_encode_filter/css_generator_spec.rb +104 -0
- data/spec/web_resource_bundler/filters/image_encode_filter/filter_spec.rb +73 -0
- data/spec/web_resource_bundler/filters/image_encode_filter/image_data_spec.rb +53 -0
- data/spec/web_resource_bundler/settings_spec.rb +45 -0
- data/spec/web_resource_bundler/web_resource_bundler_spec.rb +90 -0
- data/web_resource_bundler.gemspec +111 -0
- metadata +146 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
module WebResourceBundler
|
2
|
+
class BlockParser
|
3
|
+
CONDITIONAL_BLOCK_PATTERN = /<!--\s*\[\s*if[^>]*IE\s*\d*[^>]*\]\s*>(.*?)<!\s*\[\s*endif\s*\]\s*-->/xmi
|
4
|
+
CONDITION_PATTERN = /<!--\s*(\[[^<]*\])\s*>/
|
5
|
+
LINK_PATTERN = /(<(link|script[^>]*?src\s*=).*?(><\/script>|>))/
|
6
|
+
URL_PATTERN = /(href|src) *= *["']([^"'?]+)/i
|
7
|
+
|
8
|
+
#parsing block content recursively
|
9
|
+
#nested comments NOT supported
|
10
|
+
#result is BlockData with conditional blocks in child_blocks
|
11
|
+
def parse_block_with_childs(block, condition)
|
12
|
+
block_data = BlockData.new(condition)
|
13
|
+
block.gsub!(CONDITIONAL_BLOCK_PATTERN) do |s|
|
14
|
+
new_block = CONDITIONAL_BLOCK_PATTERN.match(s)[1]
|
15
|
+
new_condition = CONDITION_PATTERN.match(s)[1]
|
16
|
+
block_data.child_blocks << parse_block_with_childs(new_block, new_condition)
|
17
|
+
s = ""
|
18
|
+
end
|
19
|
+
files = find_files(block)
|
20
|
+
block_data.files = files
|
21
|
+
block_data.inline_block = remove_links(block)
|
22
|
+
block_data
|
23
|
+
end
|
24
|
+
|
25
|
+
#removing resource links from block
|
26
|
+
#example: "<link href="bla"><script src="bla"></script>my inline content" => "my inline content"
|
27
|
+
def remove_links(block)
|
28
|
+
inline_block = block.gsub(LINK_PATTERN) do |s|
|
29
|
+
extension = File.extname(URL_PATTERN.match(s)[2])
|
30
|
+
if /\.js|\.css/.match(extension) and not s.include?('://')
|
31
|
+
#we should delete link to local css or js resource
|
32
|
+
''
|
33
|
+
else
|
34
|
+
#link to remote resource should be kept
|
35
|
+
s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return inline_block
|
39
|
+
end
|
40
|
+
|
41
|
+
#looking for css and js files included and create BlockFiles with files paths
|
42
|
+
def find_files(block)
|
43
|
+
files = []
|
44
|
+
block.scan(URL_PATTERN).each do |property, value|
|
45
|
+
unless value.include?('://')
|
46
|
+
case property
|
47
|
+
when "src"
|
48
|
+
then files << WebResourceBundler::ResourceFile.new(WebResourceBundler::ResourceFileType::JS, value) if File.extname(value) == '.js'
|
49
|
+
when "href"
|
50
|
+
then files << WebResourceBundler::ResourceFile.new(WebResourceBundler::ResourceFileType::CSS, value) if File.extname(value) == '.css'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
files
|
55
|
+
end
|
56
|
+
|
57
|
+
#just a short method to start parsing passed block
|
58
|
+
def parse(block)
|
59
|
+
parse_block_with_childs(block, "")
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class WebResourceBundler::CssUrlRewriter
|
2
|
+
class << self
|
3
|
+
# rewrites a relative path to an absolute path, removing excess "../" and "./"
|
4
|
+
# rewrite_relative_path("stylesheets/default/global.css", "../image.gif") => "/stylesheets/image.gif"
|
5
|
+
|
6
|
+
def rewrite_relative_path(source_url, relative_url)
|
7
|
+
return relative_url if relative_url.include?('http://')
|
8
|
+
File.expand_path(relative_url, File.dirname(source_url))
|
9
|
+
end
|
10
|
+
|
11
|
+
# rewrite the URL reference paths
|
12
|
+
# url(../../../images/active_scaffold/default/add.gif);
|
13
|
+
# url(/stylesheets/active_scaffold/default/../../../images/active_scaffold/default/add.gif);
|
14
|
+
# url(/stylesheets/active_scaffold/../../images/active_scaffold/default/add.gif);
|
15
|
+
# url(/stylesheets/../images/active_scaffold/default/add.gif);
|
16
|
+
# url('/images/active_scaffold/default/add.gif');
|
17
|
+
def rewrite_content_urls!(filename, content)
|
18
|
+
content.gsub!(/url\s*\(['|"]?([^\)'"]+)['|"]?\)/) { "url('#{rewrite_relative_path(filename, $1)}')" }
|
19
|
+
content
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module WebResourceBundler
|
2
|
+
|
3
|
+
class ResourceFileType
|
4
|
+
CSS = {:value => 1, :name => 'style', :ext => 'css'}
|
5
|
+
JS = {:value => 2, :name => 'script', :ext => 'js'}
|
6
|
+
IE_CSS = {:value => 3, :name => 'style', :ext => 'css'}
|
7
|
+
MHTML = {:value => 4, :name => 'style', :ext => 'mhtml'}
|
8
|
+
end
|
9
|
+
|
10
|
+
class ResourceFile
|
11
|
+
attr_reader :type
|
12
|
+
attr_accessor :path, :content
|
13
|
+
def initialize(type, path, content = "")
|
14
|
+
@type = type
|
15
|
+
@content = content
|
16
|
+
@path = path
|
17
|
+
end
|
18
|
+
def self.new_js_file(path, content = "")
|
19
|
+
ResourceFile.new(ResourceFileType::JS, path, content)
|
20
|
+
end
|
21
|
+
def self.new_css_file(path, content = "")
|
22
|
+
ResourceFile.new(ResourceFileType::CSS, path, content)
|
23
|
+
end
|
24
|
+
def self.new_ie_css_file(path, content ="")
|
25
|
+
ResourceFile.new(ResourceFileType::IE_CSS, path, content)
|
26
|
+
end
|
27
|
+
def self.new_mhtml_file(path, content = "")
|
28
|
+
ResourceFile.new(ResourceFileType::MHTML, path, content)
|
29
|
+
end
|
30
|
+
def clone
|
31
|
+
ResourceFile.new(self.type.dup, self.path.dup, self.content.dup)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module WebResourceBundler::Exceptions
|
2
|
+
class WebResourceBundlerError < Exception
|
3
|
+
|
4
|
+
def initialize(message = "Unknown error occured")
|
5
|
+
@message = message
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
@message
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class ResourceNotFoundError < WebResourceBundlerError
|
15
|
+
def initialize(path)
|
16
|
+
super "Resource #{path} not found"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class NonExistentCssImage < WebResourceBundlerError
|
21
|
+
def initialize(image_path)
|
22
|
+
super "Css has url to incorrect image path: #{image_path}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module WebResourceBundler
|
2
|
+
class FileManager
|
3
|
+
attr_accessor :resource_dir, :cache_dir
|
4
|
+
|
5
|
+
def initialize(resource_dir, cache_dir)
|
6
|
+
@resource_dir, @cache_dir = resource_dir, cache_dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def full_path(relative_path)
|
10
|
+
File.join(@resource_dir, relative_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def exist?(relative_path)
|
14
|
+
File.exist? full_path(relative_path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_content(relative_path)
|
18
|
+
raise Exceptions::ResourceNotFoundError.new(full_path(relative_path)) unless exist?(relative_path)
|
19
|
+
File.read(full_path(relative_path))
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_cache_dir
|
23
|
+
path = File.join(@resource_dir, @cache_dir)
|
24
|
+
unless File.exist?(path)
|
25
|
+
Dir.mkdir(path)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WebResourceBundler
|
2
|
+
module Filters
|
3
|
+
#virtaul base class for filters
|
4
|
+
class BaseFilter
|
5
|
+
attr_reader :settings
|
6
|
+
|
7
|
+
def initialize(settings, file_manager)
|
8
|
+
@settings = Settings.new(settings)
|
9
|
+
@file_manager = file_manager
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_settings(settings)
|
13
|
+
@settings.set(settings)
|
14
|
+
end
|
15
|
+
|
16
|
+
def apply(block_data = nil)
|
17
|
+
#applies filter to block_data
|
18
|
+
end
|
19
|
+
|
20
|
+
#resource is hash {:css => ResourceBundle::Data, :js => ResourceBundle::Data}
|
21
|
+
def change_resulted_files!(resource = nil)
|
22
|
+
#this method changes resource file names to resulted files paths
|
23
|
+
#used to determine if resulted files exist on disk
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "/bundle_filter")
|
2
|
+
require 'bundle_filter/resource_packager'
|
3
|
+
require 'base_filter'
|
4
|
+
require 'digest/md5'
|
5
|
+
module WebResourceBundler::Filters::BundleFilter
|
6
|
+
class Filter < WebResourceBundler::Filters::BaseFilter
|
7
|
+
|
8
|
+
def initialize(settings, file_manager)
|
9
|
+
super(settings, file_manager)
|
10
|
+
@packager = ResourcePackager.new(@settings, @file_manager)
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply!(block_data)
|
14
|
+
new_files = []
|
15
|
+
unless block_data.styles.empty?
|
16
|
+
new_css_filename = css_bundle_filepath(block_data.styles)
|
17
|
+
new_css_content = @packager.bundle_files(block_data.styles)
|
18
|
+
new_css_file = WebResourceBundler::ResourceFile.new_css_file(new_css_filename, new_css_content)
|
19
|
+
new_files << new_css_file
|
20
|
+
end
|
21
|
+
unless block_data.scripts.empty?
|
22
|
+
new_js_filename = js_bundle_filepath(block_data.scripts)
|
23
|
+
new_js_content = @packager.bundle_files(block_data.scripts)
|
24
|
+
new_js_file = WebResourceBundler::ResourceFile.new_js_file(new_js_filename, new_js_content)
|
25
|
+
new_files << new_js_file
|
26
|
+
end
|
27
|
+
block_data.files = new_files
|
28
|
+
block_data
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_md5(files)
|
32
|
+
items = [(files.map {|f| f.path }).sort]
|
33
|
+
items += @settings.md5_additional_data if @settings.md5_additional_data
|
34
|
+
Digest::MD5.hexdigest(items.flatten.join('|'))
|
35
|
+
end
|
36
|
+
|
37
|
+
def bundle_filepath(type, files)
|
38
|
+
unless files.empty?
|
39
|
+
items = [type[:name] + '_' + get_md5(files)]
|
40
|
+
items += @settings.filename_additional_data if @settings.filename_additional_data
|
41
|
+
items << type[:ext]
|
42
|
+
return File.join(@settings.cache_dir, items.join('.'))
|
43
|
+
else
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#just aliases to simplify code
|
49
|
+
def css_bundle_filepath(files)
|
50
|
+
bundle_filepath(WebResourceBundler::ResourceFileType::CSS, files)
|
51
|
+
end
|
52
|
+
|
53
|
+
def js_bundle_filepath(files)
|
54
|
+
bundle_filepath(WebResourceBundler::ResourceFileType::JS, files)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module WebResourceBundler::Filters::BundleFilter
|
2
|
+
class ResourcePackager
|
3
|
+
IMPORT_PTR = /\@import ['|"](.*?)['|"];/
|
4
|
+
|
5
|
+
def initialize(settings, file_manager)
|
6
|
+
@settings = settings
|
7
|
+
@file_manager = file_manager
|
8
|
+
end
|
9
|
+
|
10
|
+
#recursively iterates through all files and imported files
|
11
|
+
def bundle_files(files)
|
12
|
+
output = ""
|
13
|
+
files.select{|f| not f.content.empty? }.each do |file|
|
14
|
+
path = file.path
|
15
|
+
content = file.content
|
16
|
+
output << "/* --------- #{path} --------- */\n"
|
17
|
+
if file.type[:ext] == 'css'
|
18
|
+
imported_files = extract_imported_files!(content, path)
|
19
|
+
#getting imported (@import ...) files contents
|
20
|
+
imported_resource_files = []
|
21
|
+
imported_files.each do |imported_file|
|
22
|
+
imported_resource_files << WebResourceBundler::ResourceFile.new_css_file(imported_file, @file_manager.get_content(imported_file))
|
23
|
+
end
|
24
|
+
#bundling imported files
|
25
|
+
output << bundle_files(imported_resource_files) unless imported_resource_files.empty?
|
26
|
+
end
|
27
|
+
#adding ';' symbol in case javascript developer forget to do this
|
28
|
+
content << ';' if File.extname(path) == '.js'
|
29
|
+
output << content
|
30
|
+
output << "\n/* --------- END #{path} --------- */\n"
|
31
|
+
end
|
32
|
+
output
|
33
|
+
end
|
34
|
+
|
35
|
+
#finds all imported files in css
|
36
|
+
def extract_imported_files!(content, base_file_path)
|
37
|
+
imported_files = []
|
38
|
+
content.gsub!(IMPORT_PTR) do |result|
|
39
|
+
imported_file = IMPORT_PTR.match(result)[1]
|
40
|
+
if imported_file
|
41
|
+
imported_files << File.join(File.dirname(base_file_path), imported_file)
|
42
|
+
end
|
43
|
+
result = ""
|
44
|
+
end
|
45
|
+
return imported_files
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module WebResourceBundler::Filters::CdnFilter
|
2
|
+
class Filter < WebResourceBundler::Filters::BaseFilter
|
3
|
+
def initialize(settings, file_manager)
|
4
|
+
super(settings, file_manager)
|
5
|
+
end
|
6
|
+
|
7
|
+
def apply!(block_data)
|
8
|
+
block_data.styles.each do |file|
|
9
|
+
rewrite_content_urls!(file.path, file.content) unless file.content.empty?
|
10
|
+
file.path = new_filepath(file.path)
|
11
|
+
end
|
12
|
+
block_data
|
13
|
+
end
|
14
|
+
|
15
|
+
def new_filepath(path)
|
16
|
+
File.join(@settings.cache_dir, 'cdn_' + File.basename(path))
|
17
|
+
end
|
18
|
+
|
19
|
+
#insures that image linked to one particular host
|
20
|
+
def host_for_image(image_url)
|
21
|
+
#hosts are different depending on protocol
|
22
|
+
if @settings.protocol == 'https'
|
23
|
+
hosts = @settings.https_hosts
|
24
|
+
else
|
25
|
+
hosts = @settings.http_hosts
|
26
|
+
end
|
27
|
+
#getting host based on image url hash
|
28
|
+
host_index = image_url.hash % hosts.size
|
29
|
+
hosts[host_index]
|
30
|
+
end
|
31
|
+
|
32
|
+
def rewrite_content_urls!(file_path, content)
|
33
|
+
content.gsub!(/url\s*\(['|"]?([^\)'"]+)['|"]?\)/) do |s|
|
34
|
+
matched_url = $1
|
35
|
+
#we shouldn't change url value for base64 encoded images
|
36
|
+
if not (/base64/.match(s) or /mhtml/.match(s)) and matched_url.match(/\.(jpg|gif|png|jpeg|bmp)/)
|
37
|
+
#using CssUrlRewriter method to get image url
|
38
|
+
url = WebResourceBundler::CssUrlRewriter.rewrite_relative_path(file_path, matched_url)
|
39
|
+
host = host_for_image(url)
|
40
|
+
s = "url('#{File.join(host, url)}')"
|
41
|
+
else
|
42
|
+
s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
require 'image_encode_filter/image_data'
|
3
|
+
require 'image_encode_filter/css_generator'
|
4
|
+
module WebResourceBundler::Filters::ImageEncodeFilter
|
5
|
+
class Filter < WebResourceBundler::Filters::BaseFilter
|
6
|
+
FILE_PREFIX = 'base64_'
|
7
|
+
IE_FILE_PREFIX = 'base64_ie_'
|
8
|
+
MHTML_FILE_PREFIX = 'mhtml_'
|
9
|
+
def initialize(settings, file_manager)
|
10
|
+
super settings, file_manager
|
11
|
+
@generator = CssGenerator.new(@settings, @file_manager)
|
12
|
+
end
|
13
|
+
|
14
|
+
def set_settings(settings)
|
15
|
+
super settings
|
16
|
+
@generator.set_settings(settings)
|
17
|
+
end
|
18
|
+
|
19
|
+
def apply!(block_data)
|
20
|
+
added_files = []
|
21
|
+
block_data.styles.each do |file|
|
22
|
+
#creating new css file with content for IE
|
23
|
+
ie_css_file = WebResourceBundler::ResourceFile.new_ie_css_file(encoded_filepath_for_ie(file.path), file.content.dup)
|
24
|
+
#creating new mhtml file with images encoded in base64
|
25
|
+
mhtml_file = WebResourceBundler::ResourceFile.new_mhtml_file(mhtml_filepath(file.path), "")
|
26
|
+
file.path = encoded_filepath(file.path)
|
27
|
+
unless file.content.empty?
|
28
|
+
@generator.encode_images!(file.content)
|
29
|
+
#getting images to construct mhtml file
|
30
|
+
images = @generator.encode_images_for_ie!(ie_css_file.content, mhtml_file.path)
|
31
|
+
mhtml_file.content = @generator.construct_mhtml_content(images)
|
32
|
+
end
|
33
|
+
added_files << ie_css_file
|
34
|
+
added_files << mhtml_file
|
35
|
+
end
|
36
|
+
block_data.files += added_files
|
37
|
+
block_data
|
38
|
+
end
|
39
|
+
|
40
|
+
#path of a new file with images encoded
|
41
|
+
def encoded_filepath(base_file_path)
|
42
|
+
File.join(@settings.cache_dir, FILE_PREFIX + File.basename(base_file_path))
|
43
|
+
end
|
44
|
+
|
45
|
+
#path of a new file for IE with images encoded
|
46
|
+
def encoded_filepath_for_ie(base_file_path)
|
47
|
+
File.join(@settings.cache_dir, IE_FILE_PREFIX + File.basename(base_file_path))
|
48
|
+
end
|
49
|
+
|
50
|
+
#filepath of mhtml file for IE
|
51
|
+
def mhtml_filepath(base_file_path)
|
52
|
+
File.join(@settings.cache_dir, MHTML_FILE_PREFIX + File.basename(base_file_path))
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module WebResourceBundler
|
2
|
+
module Filters
|
3
|
+
module ImageEncodeFilter
|
4
|
+
class CssGenerator
|
5
|
+
TAGS = ['background-image', 'background']
|
6
|
+
SEPARATOR = 'A_SEPARATOR'
|
7
|
+
PATTERN = /((#{TAGS.join('|')})\s*:[^\(]*)url\(\s*['|"]([^\)]*)['|"]\s*\)/
|
8
|
+
|
9
|
+
|
10
|
+
def initialize(settings, file_manager)
|
11
|
+
@settings = settings
|
12
|
+
@file_manager = file_manager
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_settings(settings)
|
16
|
+
@settings.set(settings)
|
17
|
+
end
|
18
|
+
|
19
|
+
#construct mhtml head of css file with definition of image data in base64
|
20
|
+
def construct_mhtml_content(images)
|
21
|
+
result = ""
|
22
|
+
unless images.empty?
|
23
|
+
result << "/* \n"
|
24
|
+
result << 'Content-Type: multipart/related; boundary="' << SEPARATOR << '"' << "\n\n"
|
25
|
+
#each image found in css should be defined in header with base64 encoded content
|
26
|
+
images.each_key do |key|
|
27
|
+
result += images[key].construct_mhtml_image_data('--' + SEPARATOR)
|
28
|
+
end
|
29
|
+
result << "\n" << '--' << SEPARATOR << '--' << "\n"
|
30
|
+
result << "*/"
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
#creates mhtml link to use in css tags instead of image url
|
36
|
+
def construct_mhtml_link(filepath)
|
37
|
+
"#{@settings.protocol}://#{File.join(@settings.domain, filepath)}"
|
38
|
+
end
|
39
|
+
|
40
|
+
#iterates through all tags found in css
|
41
|
+
#if image exist and has proper size - it should be encoded
|
42
|
+
#each tag with this kind of an image is replaced with new one (mhtml link for IE and base64 code for another browser
|
43
|
+
#returns images hash - in case generator can build proper IE css header with base64 images encoded
|
44
|
+
def encode_images_basic!(content)
|
45
|
+
images = {}
|
46
|
+
new_content = content.gsub!(PATTERN) do |s|
|
47
|
+
tag, url = $1, $3
|
48
|
+
#this constructor will write in log if image doesn't exist
|
49
|
+
data = ImageData.new(url, @settings.resource_dir)
|
50
|
+
if !url.empty? and data.exist and data.size <= @settings.max_image_size and block_given?
|
51
|
+
#using image url as key to prevent one image be encoded many times
|
52
|
+
images[data.url] = data unless images[data.path]
|
53
|
+
#changing string using provided block
|
54
|
+
s = yield(data, tag) if block_given?
|
55
|
+
else
|
56
|
+
#returning the same string because user failed with image path - such image non existent
|
57
|
+
s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
images
|
61
|
+
end
|
62
|
+
|
63
|
+
#generates css file for IE with encoded images using mhtml in cache dir
|
64
|
+
#mhtml_filepath - path to file with images encoded in base64
|
65
|
+
def encode_images_for_ie!(content, mhtml_filepath)
|
66
|
+
#creating new css content with images encoded in base64
|
67
|
+
images = encode_images_basic!(content) do |image_data, tag|
|
68
|
+
"*#{tag}url(mhtml:#{construct_mhtml_link(mhtml_filepath)}!#{image_data.id})"
|
69
|
+
end
|
70
|
+
images
|
71
|
+
end
|
72
|
+
|
73
|
+
#generates css file with encoded images in cache dir
|
74
|
+
def encode_images!(content)
|
75
|
+
#encoding images in content
|
76
|
+
images = encode_images_basic!(content) do |image_data, tag|
|
77
|
+
"#{tag}url('data:image/#{image_data.extension};base64,#{image_data.encoded}')"
|
78
|
+
end
|
79
|
+
images
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|