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