holder_rails 0.0.3 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +6 -0
- data/lib/holder_rails.rb +1 -0
- data/lib/holder_rails/helpers.rb +1 -1
- data/lib/holder_rails/version.rb +1 -1
- data/test/holder_rails_test.rb +25 -0
- data/vendor/assets/javascripts/holder.js +188 -78
- metadata +7 -5
data/Rakefile
CHANGED
data/lib/holder_rails.rb
CHANGED
data/lib/holder_rails/helpers.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module HolderRails
|
2
2
|
module Helpers
|
3
3
|
def holder_tag(size, text='', theme=nil, html_options={})
|
4
|
-
size = "#{size}x#{size}" unless size =~
|
4
|
+
size = "#{size}x#{size}" unless size =~ /\A\d+%?x\d+\z/
|
5
5
|
text = text.to_s.empty? ? size : text
|
6
6
|
options = {:src => '', :data => {:src => "holder.js/#{size}/text:#{text}/#{theme}"}}
|
7
7
|
options = options.merge(html_options)
|
data/lib/holder_rails/version.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'holder_rails'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class HolderRailsTest < ActionView::TestCase
|
5
|
+
include HolderRails::Helpers
|
6
|
+
|
7
|
+
test "size" do
|
8
|
+
assert_equal '<img data-src="holder.js/100x100/text:100x100/" src="" />', holder_tag(100)
|
9
|
+
assert_equal '<img data-src="holder.js/200x300/text:200x300/" src="" />', holder_tag('200x300')
|
10
|
+
assert_equal '<img data-src="holder.js/100%x75/text:100%x75/" src="" />', holder_tag('100%x75')
|
11
|
+
end
|
12
|
+
|
13
|
+
test "text" do
|
14
|
+
assert_equal '<img data-src="holder.js/200x300/text:Lorem ipsum/" src="" />', holder_tag('200x300', 'Lorem ipsum')
|
15
|
+
end
|
16
|
+
|
17
|
+
test "theme" do
|
18
|
+
assert_equal '<img data-src="holder.js/200x300/text:Lorem ipsum/social" src="" />', holder_tag('200x300', 'Lorem ipsum', 'social')
|
19
|
+
end
|
20
|
+
|
21
|
+
test "html_options" do
|
22
|
+
assert_equal '<img class="special" data-src="holder.js/500x800/text:Example text/gray" id="new" src="" />',
|
23
|
+
holder_tag('500x800', 'Example text', 'gray', :id => 'new', :class => 'special')
|
24
|
+
end
|
25
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
|
3
|
-
Holder - 1.
|
3
|
+
Holder - 1.7 - client side image placeholders
|
4
4
|
(c) 2012 Ivan Malopinsky / http://imsky.co
|
5
5
|
|
6
6
|
Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
|
@@ -33,88 +33,162 @@ function selector(a){
|
|
33
33
|
//shallow object property extend
|
34
34
|
function extend(a,b){var c={};for(var d in a)c[d]=a[d];for(var e in b)c[e]=b[e];return c}
|
35
35
|
|
36
|
-
|
37
|
-
|
36
|
+
//hasOwnProperty polyfill
|
37
|
+
if (!Object.prototype.hasOwnProperty)
|
38
|
+
Object.prototype.hasOwnProperty = function(prop) {
|
39
|
+
var proto = this.__proto__ || this.constructor.prototype;
|
40
|
+
return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
|
41
|
+
}
|
42
|
+
|
43
|
+
function text_size(width, height, template) {
|
44
|
+
var dimension_arr = [height, width].sort();
|
38
45
|
var maxFactor = Math.round(dimension_arr[1] / 16),
|
39
46
|
minFactor = Math.round(dimension_arr[0] / 16);
|
40
47
|
var text_height = Math.max(template.size, maxFactor);
|
41
|
-
|
42
|
-
|
48
|
+
return {
|
49
|
+
height: text_height
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
function draw(ctx, dimensions, template, ratio) {
|
54
|
+
var ts = text_size(dimensions.width, dimensions.height, template);
|
55
|
+
var text_height = ts.height;
|
56
|
+
var width = dimensions.width * ratio, height = dimensions.height * ratio;
|
57
|
+
canvas.width = width;
|
58
|
+
canvas.height = height;
|
43
59
|
ctx.textAlign = "center";
|
44
60
|
ctx.textBaseline = "middle";
|
45
61
|
ctx.fillStyle = template.background;
|
46
|
-
ctx.fillRect(0, 0,
|
62
|
+
ctx.fillRect(0, 0, width, height);
|
47
63
|
ctx.fillStyle = template.foreground;
|
48
64
|
ctx.font = "bold " + text_height + "px sans-serif";
|
49
65
|
var text = template.text ? template.text : (dimensions.width + "x" + dimensions.height);
|
50
|
-
if (
|
51
|
-
text_height =
|
66
|
+
if (ctx.measureText(text).width / width > 1) {
|
67
|
+
text_height = template.size / (ctx.measureText(text).width / width);
|
52
68
|
}
|
53
|
-
ctx.font = "bold " + text_height + "px sans-serif";
|
54
|
-
ctx.fillText(text, (
|
69
|
+
ctx.font = "bold " + (text_height * ratio) + "px sans-serif";
|
70
|
+
ctx.fillText(text, (width / 2), (height / 2), width);
|
55
71
|
return canvas.toDataURL("image/png");
|
56
72
|
}
|
57
73
|
|
58
|
-
|
59
|
-
fallback = true;
|
60
|
-
} else {
|
61
|
-
if (canvas.toDataURL("image/png").indexOf("data:image/png") < 0) {
|
62
|
-
//Android doesn't support data URI
|
63
|
-
fallback = true;
|
64
|
-
} else {
|
65
|
-
var ctx = canvas.getContext("2d");
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
function render(mode, el, holder, src){
|
74
|
+
function render(mode, el, holder, src) {
|
70
75
|
|
71
|
-
var dimensions = holder.dimensions,
|
76
|
+
var dimensions = holder.dimensions,
|
77
|
+
theme = holder.theme,
|
78
|
+
text = holder.text;
|
72
79
|
var dimensions_caption = dimensions.width + "x" + dimensions.height;
|
73
|
-
|
74
|
-
|
75
|
-
|
80
|
+
theme = (text ? extend(theme, {
|
81
|
+
text: text
|
82
|
+
}) : theme);
|
83
|
+
|
84
|
+
var ratio = 1;
|
85
|
+
if(window.devicePixelRatio && window.devicePixelRatio > 1){
|
86
|
+
ratio = window.devicePixelRatio;
|
87
|
+
}
|
88
|
+
|
89
|
+
if (mode == "image") {
|
76
90
|
el.setAttribute("data-src", src);
|
77
91
|
el.setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption);
|
78
92
|
el.style.width = dimensions.width + "px";
|
79
93
|
el.style.height = dimensions.height + "px";
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
el.setAttribute("src", draw(ctx, dimensions, theme));
|
94
|
+
|
95
|
+
if (fallback) {
|
96
|
+
el.style.backgroundColor = theme.background;
|
84
97
|
}
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
98
|
+
else{
|
99
|
+
el.setAttribute("src", draw(ctx, dimensions, theme, ratio));
|
100
|
+
}
|
101
|
+
} else {
|
102
|
+
if (!fallback) {
|
103
|
+
el.style.backgroundImage = "url(" + draw(ctx, dimensions, theme, ratio) + ")";
|
104
|
+
el.style.backgroundSize = dimensions.width+"px "+dimensions.height+"px";
|
89
105
|
}
|
90
106
|
}
|
91
|
-
|
92
107
|
};
|
93
108
|
|
94
|
-
function
|
95
|
-
|
109
|
+
function fluid(el, holder, src) {
|
110
|
+
var dimensions = holder.dimensions,
|
111
|
+
theme = holder.theme,
|
112
|
+
text = holder.text;
|
113
|
+
var dimensions_caption = dimensions.width + "x" + dimensions.height;
|
114
|
+
theme = (text ? extend(theme, {
|
115
|
+
text: text
|
116
|
+
}) : theme);
|
117
|
+
|
118
|
+
var fluid = document.createElement("div");
|
119
|
+
|
120
|
+
fluid.style.backgroundColor = theme.background;
|
121
|
+
fluid.style.color = theme.foreground;
|
122
|
+
fluid.className = el.className + " holderjs-fluid";
|
123
|
+
fluid.style.width = holder.dimensions.width + (holder.dimensions.width.indexOf("%")>0?"":"px");
|
124
|
+
fluid.style.height = holder.dimensions.height + (holder.dimensions.height.indexOf("%")>0?"":"px");
|
125
|
+
fluid.id = el.id;
|
126
|
+
|
127
|
+
if (theme.text) {
|
128
|
+
fluid.appendChild(document.createTextNode(theme.text))
|
129
|
+
} else {
|
130
|
+
fluid.appendChild(document.createTextNode(dimensions_caption))
|
131
|
+
fluid_images.push(fluid);
|
132
|
+
setTimeout(fluid_update, 0);
|
133
|
+
}
|
134
|
+
|
135
|
+
el.parentNode.replaceChild(fluid, el);
|
136
|
+
}
|
137
|
+
|
138
|
+
function fluid_update() {
|
139
|
+
for (i in fluid_images) {
|
140
|
+
if(!fluid_images.hasOwnProperty(i)) continue;
|
141
|
+
var el = fluid_images[i],
|
142
|
+
label = el.firstChild;
|
143
|
+
|
144
|
+
el.style.lineHeight = el.offsetHeight+"px";
|
145
|
+
label.data = el.offsetWidth + "x" + el.offsetHeight;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
function parse_flags(flags, options) {
|
150
|
+
|
96
151
|
var ret = {
|
97
152
|
theme: settings.themes.gray
|
98
|
-
|
99
|
-
|
153
|
+
}, render = false;
|
154
|
+
|
100
155
|
for (sl = flags.length, j = 0; j < sl; j++) {
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
156
|
+
var flag = flags[j];
|
157
|
+
if (app.flags.dimensions.match(flag)) {
|
158
|
+
render = true;
|
159
|
+
ret.dimensions = app.flags.dimensions.output(flag);
|
160
|
+
} else if (app.flags.fluid.match(flag)) {
|
161
|
+
render = true;
|
162
|
+
ret.dimensions = app.flags.fluid.output(flag);
|
163
|
+
ret.fluid = true;
|
164
|
+
} else if (app.flags.colors.match(flag)) {
|
165
|
+
ret.theme = app.flags.colors.output(flag);
|
166
|
+
} else if (options.themes[flag]) {
|
167
|
+
//If a theme is specified, it will override custom colors
|
168
|
+
ret.theme = options.themes[flag];
|
169
|
+
} else if (app.flags.text.match(flag)) {
|
170
|
+
ret.text = app.flags.text.output(flag);
|
171
|
+
}
|
112
172
|
}
|
113
|
-
|
173
|
+
|
114
174
|
return render ? ret : false;
|
115
|
-
|
175
|
+
|
116
176
|
};
|
117
177
|
|
178
|
+
if (!canvas.getContext) {
|
179
|
+
fallback = true;
|
180
|
+
} else {
|
181
|
+
if (canvas.toDataURL("image/png")
|
182
|
+
.indexOf("data:image/png") < 0) {
|
183
|
+
//Android doesn't support data URI
|
184
|
+
fallback = true;
|
185
|
+
} else {
|
186
|
+
var ctx = canvas.getContext("2d");
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
var fluid_images = [];
|
191
|
+
|
118
192
|
var settings = {
|
119
193
|
domain: "holder.js",
|
120
194
|
images: "img",
|
@@ -125,24 +199,25 @@ var settings = {
|
|
125
199
|
foreground: "#aaa",
|
126
200
|
size: 12
|
127
201
|
},
|
128
|
-
|
202
|
+
"social": {
|
129
203
|
background: "#3a5a97",
|
130
204
|
foreground: "#fff",
|
131
205
|
size: 12
|
132
206
|
},
|
133
|
-
|
207
|
+
"industrial": {
|
134
208
|
background: "#434A52",
|
135
209
|
foreground: "#C2F200",
|
136
210
|
size: 12
|
137
211
|
}
|
138
|
-
}
|
212
|
+
},
|
213
|
+
stylesheet: ".holderjs-fluid {font-size:16px;font-weight:bold;text-align:center;font-family:sans-serif;margin:0}"
|
139
214
|
};
|
140
215
|
|
141
216
|
|
142
217
|
app.flags = {
|
143
218
|
dimensions: {
|
144
|
-
regex:
|
145
|
-
output: function(val){
|
219
|
+
regex: /^(\d+)x(\d+)$/,
|
220
|
+
output: function (val) {
|
146
221
|
var exec = this.regex.exec(val);
|
147
222
|
return {
|
148
223
|
width: +exec[1],
|
@@ -150,27 +225,38 @@ app.flags = {
|
|
150
225
|
}
|
151
226
|
}
|
152
227
|
},
|
228
|
+
fluid: {
|
229
|
+
regex: /^([0-9%]+)x([0-9%]+)$/,
|
230
|
+
output: function (val) {
|
231
|
+
var exec = this.regex.exec(val);
|
232
|
+
return {
|
233
|
+
width: exec[1],
|
234
|
+
height: exec[2]
|
235
|
+
}
|
236
|
+
}
|
237
|
+
},
|
153
238
|
colors: {
|
154
239
|
regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i,
|
155
|
-
output: function(val){
|
240
|
+
output: function (val) {
|
156
241
|
var exec = this.regex.exec(val);
|
157
242
|
return {
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
243
|
+
size: settings.themes.gray.size,
|
244
|
+
foreground: "#" + exec[2],
|
245
|
+
background: "#" + exec[1]
|
246
|
+
}
|
162
247
|
}
|
163
248
|
},
|
164
249
|
text: {
|
165
250
|
regex: /text\:(.*)/,
|
166
|
-
output: function(val){
|
251
|
+
output: function (val) {
|
167
252
|
return this.regex.exec(val)[1];
|
168
253
|
}
|
169
254
|
}
|
170
255
|
}
|
171
256
|
|
172
|
-
for(var flag in app.flags){
|
173
|
-
app.flags
|
257
|
+
for (var flag in app.flags) {
|
258
|
+
if(!app.flags.hasOwnProperty(flag)) continue;
|
259
|
+
app.flags[flag].match = function (val) {
|
174
260
|
return val.match(this.regex)
|
175
261
|
}
|
176
262
|
}
|
@@ -194,29 +280,43 @@ app.add_image = function (src, el) {
|
|
194
280
|
|
195
281
|
app.run = function (o) {
|
196
282
|
var options = extend(settings, o),
|
197
|
-
|
283
|
+
images_nodes = selector(options.images),
|
198
284
|
elements = selector(options.elements),
|
199
|
-
preempted = true
|
285
|
+
preempted = true,
|
286
|
+
images = [];
|
287
|
+
|
288
|
+
for (i = 0, l = images_nodes.length; i < l; i++) images.push(images_nodes[i]);
|
200
289
|
|
201
|
-
var
|
202
|
-
|
203
|
-
|
204
|
-
|
290
|
+
var holdercss = document.createElement("style");
|
291
|
+
holdercss.type = "text/css";
|
292
|
+
holdercss.styleSheet ? holdercss.styleSheet.cssText = options.stylesheet : holdercss.textContent = options.stylesheet;
|
293
|
+
document.getElementsByTagName("head")[0].appendChild(holdercss);
|
294
|
+
|
295
|
+
var cssregex = new RegExp(options.domain + "\/(.*?)\"?\\)");
|
296
|
+
|
297
|
+
for (var l = elements.length, i = 0; i < l; i++) {
|
298
|
+
var src = window.getComputedStyle(elements[i], null)
|
299
|
+
.getPropertyValue("background-image");
|
205
300
|
var flags = src.match(cssregex);
|
206
|
-
if(flags){
|
301
|
+
if (flags) {
|
207
302
|
var holder = parse_flags(flags[1].split("/"), options);
|
208
|
-
if(holder){
|
303
|
+
if (holder) {
|
209
304
|
render("background", elements[i], holder, src);
|
210
305
|
}
|
211
306
|
}
|
212
307
|
}
|
213
308
|
|
214
309
|
for (var l = images.length, i = 0; i < l; i++) {
|
215
|
-
var src = images[i].getAttribute("
|
216
|
-
if (src.indexOf(options.domain)>=0) {
|
217
|
-
var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1)
|
310
|
+
var src = images[i].getAttribute("src") || images[i].getAttribute("data-src");
|
311
|
+
if (src != null && src.indexOf(options.domain) >= 0) {
|
312
|
+
var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1)
|
313
|
+
.split("/"), options);
|
218
314
|
if (holder) {
|
219
|
-
|
315
|
+
if (holder.fluid) {
|
316
|
+
fluid(images[i], holder, src);
|
317
|
+
} else {
|
318
|
+
render("image", images[i], holder, src);
|
319
|
+
}
|
220
320
|
}
|
221
321
|
}
|
222
322
|
}
|
@@ -224,7 +324,17 @@ app.run = function (o) {
|
|
224
324
|
};
|
225
325
|
|
226
326
|
contentLoaded(win, function () {
|
227
|
-
|
327
|
+
if (window.addEventListener) {
|
328
|
+
window.addEventListener("resize", fluid_update, false);
|
329
|
+
window.addEventListener("orientationchange", fluid_update, false);
|
330
|
+
} else {
|
331
|
+
window.attachEvent("onresize", fluid_update)
|
332
|
+
}
|
333
|
+
preempted || app.run();
|
228
334
|
});
|
229
335
|
|
336
|
+
if ( typeof define === "function" && define.amd ) {
|
337
|
+
define( "Holder", [], function () { return app; } );
|
338
|
+
}
|
339
|
+
|
230
340
|
})(Holder, window);
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: holder_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: railties
|
@@ -44,6 +44,7 @@ files:
|
|
44
44
|
- lib/holder_rails.rb
|
45
45
|
- lib/holder_rails/helpers.rb
|
46
46
|
- lib/holder_rails/version.rb
|
47
|
+
- test/holder_rails_test.rb
|
47
48
|
- vendor/assets/javascripts/holder.js
|
48
49
|
homepage: https://github.com/narkoz/holder_rails
|
49
50
|
licenses: []
|
@@ -59,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
60
|
version: '0'
|
60
61
|
segments:
|
61
62
|
- 0
|
62
|
-
hash:
|
63
|
+
hash: -4350495992017316440
|
63
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
65
|
none: false
|
65
66
|
requirements:
|
@@ -68,11 +69,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
69
|
version: '0'
|
69
70
|
segments:
|
70
71
|
- 0
|
71
|
-
hash:
|
72
|
+
hash: -4350495992017316440
|
72
73
|
requirements: []
|
73
74
|
rubyforge_project:
|
74
75
|
rubygems_version: 1.8.24
|
75
76
|
signing_key:
|
76
77
|
specification_version: 3
|
77
78
|
summary: Holder.js for Rails 3.1 asset pipeline
|
78
|
-
test_files:
|
79
|
+
test_files:
|
80
|
+
- test/holder_rails_test.rb
|