holder_rails 0.0.3 → 1.7.0
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/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
|