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 CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+
5
+ task :test do
6
+ ruby "test/holder_rails_test.rb"
7
+ end
@@ -1,6 +1,7 @@
1
1
  require "holder_rails/version"
2
2
  require "holder_rails/helpers"
3
3
  require "rails/engine"
4
+ require "action_view"
4
5
 
5
6
  module HolderRails
6
7
  class Engine < ::Rails::Engine; end
@@ -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 =~ /^\d+x\d+$/
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)
@@ -1,3 +1,3 @@
1
1
  module HolderRails
2
- VERSION = "0.0.3"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -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.4 - client side image placeholders
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
- function draw(ctx, dimensions, template) {
37
- var dimension_arr = [dimensions.height, dimensions.width].sort();
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
- canvas.width = dimensions.width;
42
- canvas.height = dimensions.height;
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, dimensions.width, dimensions.height);
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 (Math.round(ctx.measureText(text).width) / dimensions.width > 1) {
51
- text_height = Math.max(minFactor, template.size);
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, (dimensions.width / 2), (dimensions.height / 2), dimensions.width);
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
- if (!canvas.getContext) {
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, theme = holder.theme, text = holder.text;
76
+ var dimensions = holder.dimensions,
77
+ theme = holder.theme,
78
+ text = holder.text;
72
79
  var dimensions_caption = dimensions.width + "x" + dimensions.height;
73
- theme = (text ? extend(theme, { text: text }) : theme);
74
-
75
- if(mode == "image"){
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
- el.style.backgroundColor = theme.background;
81
-
82
- if(!fallback){
83
- el.setAttribute("src", draw(ctx, dimensions, theme));
94
+
95
+ if (fallback) {
96
+ el.style.backgroundColor = theme.background;
84
97
  }
85
- }
86
- else {
87
- if(!fallback){
88
- el.style.backgroundImage = "url("+draw(ctx, dimensions, theme)+")";
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 parse_flags(flags, options){
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
- }, render = false;
99
-
153
+ }, render = false;
154
+
100
155
  for (sl = flags.length, j = 0; j < sl; j++) {
101
- if (app.flags.dimensions.match(flags[j])) {
102
- render = true;
103
- ret.dimensions = app.flags.dimensions.output(flags[j]);
104
- } else if (app.flags.colors.match(flags[j])) {
105
- ret.theme = app.flags.colors.output(flags[j]);
106
- } else if (options.themes[flags[j]]) {
107
- //If a theme is specified, it will override custom colors
108
- ret.theme = options.themes[flags[j]];
109
- } else if (app.flags.text.match(flags[j])) {
110
- ret.text = app.flags.text.output(flags[j]);
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
- "social": {
202
+ "social": {
129
203
  background: "#3a5a97",
130
204
  foreground: "#fff",
131
205
  size: 12
132
206
  },
133
- "industrial": {
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: /([0-9]+)x([0-9]+)/,
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
- size: settings.themes.gray.size,
159
- foreground: "#" + exec[2],
160
- background: "#" + exec[1]
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[flag].match = function (val){
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
- images = selector(options.images),
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 cssregex = new RegExp(options.domain+"\/(.*?)\"?\\)");
202
-
203
- for(var l = elements.length, i = 0; i < l; i++){
204
- var src = window.getComputedStyle(elements[i],null).getPropertyValue("background-image");
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("data-src") || images[i].getAttribute("src");
216
- if (src.indexOf(options.domain)>=0) {
217
- var holder = parse_flags(src.substr(src.lastIndexOf(options.domain) + options.domain.length + 1).split("/"), options);
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
- render("image", images[i], holder, src);
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
- preempted || app.run()
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: 0.0.3
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-10-15 00:00:00.000000000 Z
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: 4343872938476998461
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: 4343872938476998461
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