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 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