inline all images into css

* embedded images as data URIs
* rake task to generate multipart js file with embeded images for IE
* move images into a separate directory outside of src or css and
  keep them there for reference
* clean up Rakefile and ruby code
* .gitignore update
* don't penalize IE 8+ with an extra request to the ie-compat.js file
This commit is contained in:
Igor Minar
2010-10-15 21:38:41 -07:00
parent d0e55bf446
commit 7059579c74
14 changed files with 185 additions and 153 deletions

2
.gitignore vendored
View File

@@ -1,8 +1,8 @@
angular-minified.map
externs.js
angular.js
angular-minified.js
angular-debug.js
angular-ie-compat.js
angular-scenario.js
angularjs.netrc
jstd.log

179
Rakefile
View File

@@ -38,36 +38,38 @@ GENERATED_FILES = [
'angular-debug.js',
'angular-minified.js',
'angular-minified.map',
'angular-ie-compat.js',
'angular-scenario.js',
]
task :default => [:compile, :test]
desc 'Generate Externs'
task :compile_externs do
out = File.new("externs.js", "w")
out.write("function jQuery(){};\n")
file = File.new("lib/jquery/jquery-1.4.2.js", "r")
while (line = file.gets)
if line =~ /^\s*(\w+)\s*:\s*function.*$/
out.write("jQuery.#{$1}=function(){};\n")
end
end
file.close
out.write("jQuery.scope=function(){};\n")
out.write("jQuery.controller=function(){};\n")
out.close
end
desc 'Clean Generated Files'
task :clean do
GENERATED_FILES.each do |file|
`rm #{file}`
task :clean do
FileUtils.rm(GENERATED_FILES, :force => true)
end
desc 'Generate Externs'
task :compile_externs do
File.open('externs.js', 'w') do |out|
out.write("function jQuery(){};\n")
File.open('lib/jquery/jquery-1.4.2.js', 'r') do |file|
while (line = file.gets)
if line =~ /^\s*(\w+)\s*:\s*function.*$/
out.write("jQuery.#{$1}=function(){};\n")
end
end
end
out.write("jQuery.scope=function(){};\n")
out.write("jQuery.controller=function(){};\n")
end
end
desc 'Compile Scenario'
task :compile_scenario do
@@ -78,28 +80,89 @@ task :compile_scenario do
ANGULAR_SCENARIO,
'src/scenario/angular.suffix',
]
css = %x(cat css/angular-scenario.css)
concat = 'cat ' + deps.flatten.join(' ')
f = File.new("angular-scenario.js", 'w')
f.write(%x{#{concat}})
f.write('document.write(\'<style type="text/css">\n')
f.write(css.gsub(/'/, "\\'").gsub(/\n/, "\\n"));
f.write('\n</style>\');')
f.close
File.open('angular-scenario.js', 'w') do |f|
f.write(%x{#{concat}})
f.write(gen_css('css/angular.css'))
f.write(gen_css('css/angular-scenario.css'))
end
end
desc 'Generate IE css js patch'
task :generate_ie_compat do
css = File.open('css/angular.css', 'r') {|f| f.read }
# finds all css rules that contain backround images and extracts the rule name(s), content type of
# the image and base64 encoded image data
r = /\n([^\{\n]+)\s*\{[^\}]*background-image:\s*url\("data:([^;]+);base64,([^"]+)"\);[^\}]*\}/
images = css.scan(r)
# create a js file with multipart header containing the extracted images. the entire file *must*
# be CRLF (\r\n) delimited
File.open('angular-ie-compat.js', 'w') do |f|
f.write("/*\r\n" +
"Content-Type: multipart/related; boundary=\"_\"\r\n" +
"\r\n")
images.each_index do |idx|
f.write("--_\r\n" +
"Content-Location:img#{idx}\r\n" +
"Content-Transfer-Encoding:base64\r\n" +
"\r\n" +
images[idx][2] + "\r\n")
end
f.write("--_--\r\n" +
"*/\r\n")
# generate a css string containing *background-image rules for IE that point to the mime type
# images in the header
cssString = ''
images.each_index do |idx|
cssString += "#{images[idx][0]}{*background-image:url(\"mhtml:' + jsUri + '!img#{idx}\")}"
end
# generate a javascript closure that contains a function which will append the generated css
# string as a stylesheet to the current html document
jsString = "(function(){ \r\n" +
" var jsUri = document.location.href.replace(/\\/[^\/]+(#.*)?$/, '/') + " +
" document.getElementById('ng-ie-compat').src; \r\n" +
" var css = '#{cssString}' \r\n" +
" var s = document.createElement('style'); \r\n" +
" s.setAttribute('type', 'text/css'); \r\n" +
" if (s.styleSheet) { \r\n" +
" s.styleSheet.cssText = css; \r\n" +
" } else { \r\n" +
" s.appendChild(document.createTextNode(css)); \r\n" +
" } \r\n" +
" document.getElementsByTagName('head')[0].appendChild(s); \r\n" +
"})();\r\n"
f.write(jsString)
end
end
desc 'Compile JavaScript'
task :compile => [:compile_externs, :compile_scenario] do
task :compile => [:compile_externs, :compile_scenario, :generate_ie_compat] do
deps = [
'src/angular.prefix',
ANGULAR,
'src/angular.suffix',
]
f = File.new("angular-debug.js", 'w')
concat = 'cat ' + deps.flatten.join(' ')
f.write(%x{#{concat}})
f.close
File.open('angular-debug.js', 'w') do |f|
concat = 'cat ' + deps.flatten.join(' ')
f.write(%x{#{concat}})
f.write(gen_css('css/angular.css', true))
end
%x(java -jar lib/compiler-closure/compiler.jar \
--compilation_level SIMPLE_OPTIMIZATIONS \
@@ -109,6 +172,7 @@ task :compile => [:compile_externs, :compile_scenario] do
--js_output_file angular-minified.js)
end
desc 'Create angular distribution'
task :package => :compile do
date = Time.now.strftime('%y%m%d_%H%M')
@@ -117,44 +181,81 @@ task :package => :compile do
%x(cp test/angular-mocks.js ./)
%x(tar -cf #{filename} \
%x(tar -czf #{filename} \
angular-debug.js \
angular-minified.js \
angular-scenario.js \
angular-mocks.js \
css/angular.css \
css/angular_images/ )
angular-ie-compat.js )
%x( rm angular-mocks.js)
puts "Package created: #{filename}"
end
namespace :server do
desc 'Run JsTestDriver Server'
task :start do
sh %x(java -jar lib/jstestdriver/JsTestDriver.jar --browser open --port 9876)
end
desc "Run JavaScript tests against the server"
desc 'Run JavaScript tests against the server'
task :test do
sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all)
end
end
desc "Run JavaScript tests"
desc 'Run JavaScript tests'
task :test do
sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all --browser open --port 9876)
end
desc 'Lint'
task :lint do
out = %x(lib/jsl/jsl -conf lib/jsl/jsl.default.conf)
print out
end
desc 'push_angularjs'
task :push_angularjs do
Rake::Task['compile'].execute 0
task :push_angularjs => :compile do
sh %(cat angularjs.ftp | ftp -N angularjs.netrc angularjs.org)
end
###################
# utility methods #
###################
##
# generates css snippet from a given files and optionally applies simple minification rules
#
def gen_css(cssFile, minify = false)
css = ''
File.open(cssFile, 'r') do |f|
css = f.read
end
if minify
css.gsub! /\n/, ''
css.gsub! /\/\*.*?\*\//, ''
css.gsub! /:\s+/, ':'
css.gsub! /\s*\{\s*/, '{'
css.gsub! /\s*\}\s*/, '}'
css.gsub! /\s*\,\s*/, ','
css.gsub! /\s*\;\s*/, ';'
end
#escape for js
css.gsub! /'/, "\\'"
css.gsub! /\n/, "\\n"
return %Q{document.write('<style type="text/css">#{css}</style>');}
end

View File

@@ -1,32 +1,4 @@
@charset "UTF-8";
/* CSS Document */
#ng-console {
border: thin solid black;
font-family: 'courier';
font-size: x-small;
}
#ng-console .ng-console-error {
color: red;
}
#ng-console .ng-console-info {
color: blue;
}
.ng-upload-widget object {
align:center;
}
.ng-upload-widget a {
margin-right: .3em;
}
.ng-upload-widget span {
color: #999999;
font-size: smaller;
}
.ng-format-negative {
color: red;
@@ -42,29 +14,6 @@
border: 2px solid #FF0000;
}
.ng-hidden {
display:none;
}
/*****************
* DatePicker
*****************/
div.ui-widget {
font-size: 11px;
}
/*****************
* OrderBy
*****************/
.ng-ascend,
.ng-descend {
padding-right: 20px;
background-repeat: no-repeat;
background-position: right;
}
.ng-ascend { background-image: url(angular_images/arrow_ascend.png); }
.ng-descend { background-image: url(angular_images/arrow_descend.png); }
/*****************
* TIP
@@ -83,7 +32,7 @@ div.ui-widget {
}
#ng-callout .ng-arrow-left{
background-image: url(angular_images/arrow_left.gif);
background-image: url("data:image/gif;base64,R0lGODlhCwAXAKIAAMzMzO/v7/f39////////wAAAAAAAAAAACH5BAUUAAQALAAAAAALABcAAAMrSLoc/AG8FeUUIN+sGebWAnbKSJodqqlsOxJtqYooU9vvk+vcJIcTkg+QAAA7");
background-repeat: no-repeat;
background-position: left top;
position: absolute;
@@ -95,7 +44,7 @@ div.ui-widget {
}
#ng-callout .ng-arrow-right{
background-image: url(angular_images/arrow_right.gif);
background-image: url("data:image/gif;base64,R0lGODlhCwAXAKIAAMzMzO/v7/f39////////wAAAAAAAAAAACH5BAUUAAQALAAAAAALABcAAAMrCLTcoM29yN6k9socs91e5X3EyJloipYrO4ohTMqA0Fn2XVNswJe+H+SXAAA7");
background-repeat: no-repeat;
background-position: left top;
position: absolute;
@@ -117,7 +66,6 @@ div.ui-widget {
color:#333333;
}
#ng-callout .ng-title{
background-color: #CCCCCC;
text-align: left;
@@ -128,62 +76,11 @@ div.ui-widget {
}
#ng-spacer {
height: 1.2em;
}
#ng-loading {
position: fixed;
bottom: 0;
height: 1.2em;
width: 100%;
text-align: center;
}
/*****************
* Login
*****************/
#ng-login {
z-index: 2000;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding-top: 100px;
}
#ng-login .ng-login-container {
width: 500px;
height: 380px;
margin: auto;
border-top: 5px solid #FFF;
border-left: 5px solid #DDD;
border-right: 5px solid #777;
border-bottom: 5px solid #555;
padding: 0 3px 3px 0;
}
#ng-login .ng-login-container iframe {
width: 100%;
height: 100%;
border: 2px solid black;
}
/*****************
* indicators
*****************/
.ng-indicator-wait {
display: inline-block;
height: 16px;
width: 16px;
background-image: url("angular_images/indicator-wait.png");
}
.ng-input-indicator-wait {
background-image: url("angular_images/indicator-wait.png");
background-image: url("data:image/png;base64,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");
background-position: right;
background-repeat: no-repeat;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -32,7 +32,8 @@ var _undefined = undefined,
PRIORITY = {'FIRST': PRIORITY_FIRST, 'LAST': PRIORITY_LAST, 'WATCH':PRIORITY_WATCH},
jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy
_ = window['_'],
msie = !!/(msie) ([\w.]+)/.exec(lowercase(navigator.userAgent)),
/** holds major version number for IE or NaN for real browsers */
msie = parseInt((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1], 10),
jqLite = jQuery || jqLiteWrap,
slice = Array.prototype.slice,
push = Array.prototype.push,
@@ -408,25 +409,31 @@ function toKeyValue(obj) {
function angularInit(config){
if (config.autobind) {
// TODO default to the source of angular.js
var scope = compile(window.document, _null, {'$config':config});
var scope = compile(window.document, _null, {'$config':config}),
$browser = scope.$inject('$browser');
if (config.css)
scope.$inject('$browser').addCss(config.base_url + config.css);
$browser.addCss(config.base_url + config.css);
else if(msie<8)
$browser.addJs(config.base_url + config.ie_compat, config.ie_compat_id);
scope.$init();
}
}
function angularJsConfig(document, config) {
var filename = /^(.*)\/angular(-([^\/]*))?.js(\?[^#]*)?(#(.*))?$/,
var filename = /^(.*)angular(-([^\/]*))?.js(\?[^#]*)?(#(.*))?$/,
scripts = document.getElementsByTagName("script"),
match;
config = extend({
base_url: '',
css: '../css/angular.css'
ie_compat: 'angular-ie-compat.js',
ie_compat_id: 'ng-ie-compat'
}, config);
for(var j = 0; j < scripts.length; j++) {
match = (scripts[j].src || "").match(filename);
if (match) {
config.base_url = match[1] + '/';
config.base_url = match[1];
extend(config, parseKeyValue(match[6]));
eachAttribute(jqLite(scripts[j]), function(value, name){
if (/^ng:/.exec(name)) {

View File

@@ -188,11 +188,26 @@ function Browser(location, document, head, XHR, $log) {
};
self.addCss = function(url) {
/**
* Adds a stylesheet tag to the head.
*/
self.addCss = function(/**string*/url) {
var link = jqLite(rawDocument.createElement('link'));
link.attr('rel', 'stylesheet');
link.attr('type', 'text/css');
link.attr('href', url);
head.append(link);
};
/**
* Adds a script tag to the head.
*/
self.addJs = function(/**string*/url, /**string*/dom_id) {
var script = jqLite(rawDocument.createElement('script'));
script.attr('type', 'text/javascript');
script.attr('src', url);
if (dom_id) script.attr('id', dom_id);
head.append(script);
};
}

View File

@@ -37,6 +37,13 @@
document.write('<script type="text/javascript" src="' + serverPath + file +'"></script>');
}
function addCss(file) {
document.write('<link rel="stylesheet" type="text/css" href="' +
serverPath + '/../css' + file + '"/>');
}
addCss("/angular.css");
addScript("/Angular.js");
addScript("/JSON.js");
addScript("/Compiler.js");
@@ -58,10 +65,15 @@
addScript("/markups.js");
addScript("/widgets.js");
window.onload = function(){
try {
if (previousOnLoad) previousOnLoad();
} catch(e) {}
//angular-ie-compat.js needs to be pregenerated for development with IE<8
if (msie<8) addScript('../angular-ie-compat.js');
angularInit(angularJsConfig(document));
};