linner 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 621f08f92c5f1e07d56b8bdef6826837958e39a1
4
- data.tar.gz: 3fa21218b588ab55d3bc9cd1f144b87663b20a2a
3
+ metadata.gz: f40ea1d47371f91db7d8a653f4b8e30b7e2748d4
4
+ data.tar.gz: 422e0dbbc14c7420296ae953b08a89ac2d1a88c4
5
5
  SHA512:
6
- metadata.gz: 89898c38996e5c0fe23eec69eba7bb722232d1beef0200c95a93d89c9e2efa9ee59f180fcb7bd9403bd03b60dc139728468f40b32e25f3e26949432d54620c3c
7
- data.tar.gz: 52183c4e3d0d9d16011490a59cc64863e1576532a1a71a7ecc9b07c7deec2909339257edebd9af0e79d8c42881d008eea0756e74d06f482c2dbeedc12660d8ec
6
+ metadata.gz: c9cbf841d6560337ced7e9fcd4577464e49e5aaa77ad1ba06c8911bbfabcb9afd05dcda48d372a5047dddeeb46a04289d577739a5924f2e9712624775bfc3041
7
+ data.tar.gz: b3f075eb30fb16b459d0ad0ec702cf7a2d8fbbfcc8f3e71a04faa3e23b1534a229660a2558bfe1e43ab0d5e13da71f2112916827f81d5a03487008645c3ac0db
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ v0.6.0
2
+ - add sprites support
3
+
1
4
  v0.5.1
2
5
  - support handlebars partials. templates start with "_" will be treaded as partials/
3
6
 
data/README.md CHANGED
@@ -16,7 +16,8 @@ Linner is a full-featured HTML5 application assembler.
16
16
  * Supports Modular Javascript, All your code will be wrapped by `cmd`.
17
17
  * Supports `concat` code by `config file` not `directive processor`.
18
18
  * Supports `copy` code from `src` to `dest`.
19
- * Supports `precompile` Javascript Templates from `src` to `desc`
19
+ * Supports `precompile` Javascript Templates from `src` to `desc`.
20
+ * Supports `sprite` PNG images from `src` to `desc`.
20
21
  * Supports Real-time `concat` by `$ linner watch`.
21
22
  * Supports `compress` by `$ linner build`.
22
23
  * Supports `LiveReload` with [LiveReload Chrome Extention](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei).
data/docs/config.md CHANGED
@@ -53,6 +53,8 @@ the default configuration defines four groups: `scripts`, `styles`, `images` and
53
53
 
54
54
  `precompile` defines precompile strategy of javascript templates for Linner. The `Dir.glob` of `value` will be concat to `key`
55
55
 
56
+ `sprite` defines sprite strategy of images for Linner. The `Dir.glob` of `value` will be sprite to `key`
57
+
56
58
  `order` defines the order of this group files, and It's very useful when you `concat` your files. for example:
57
59
 
58
60
  ```yaml
@@ -64,6 +66,22 @@ order:
64
66
 
65
67
  In the above example, if a group contains 5 files, `vendor/jquery-1.10.2.js` will be the first, and `vendor/underscore.js` will be the last file.
66
68
 
69
+ ## `sprites`
70
+
71
+ `sprites` defines application sprite stategy. `sprites` support pseudo class of css, if your file's basename end with `_active`, the generated css will be `.active`. if your file's basename end with `_hover`, the generated css will be `:hover`, eg: `arrow_hover.png` will be `.selector-arrow:hover { ... }`
72
+
73
+ Example:
74
+
75
+ ```yaml
76
+ sprites:
77
+ # sprite image output path
78
+ path: "/images/"
79
+ # css selector
80
+ selector: ".icon-"
81
+ # css url background: url(/images/icon.png)
82
+ url: "http://s3.amazonaws.com/"
83
+ ```
84
+
67
85
  ## `modules`
68
86
 
69
87
  `modules` defines application module strategy, The default wrapper is `cmd`.
data/lib/linner.rb CHANGED
@@ -5,6 +5,7 @@ require "linner/command"
5
5
  require "linner/asset"
6
6
  require "linner/cache"
7
7
  require "linner/helper"
8
+ require "linner/sprite"
8
9
  require "linner/bundler"
9
10
  require "linner/reactor"
10
11
  require "linner/wrapper"
@@ -68,6 +69,7 @@ module Linner
68
69
  def perform(*asset)
69
70
  env.groups.each do |config|
70
71
  precompile(config) if config["precompile"]
72
+ sprite(config) if config["sprite"]
71
73
  end
72
74
  env.groups.each do |config|
73
75
  copy(config) if config["copy"]
@@ -106,6 +108,14 @@ module Linner
106
108
  end
107
109
  end
108
110
 
111
+ def sprite(config)
112
+ config["sprite"].each do |dest, pattern|
113
+ matches = Dir.glob(pattern).sort_by(&:downcase)
114
+ next if matches.select { |p| cache.miss? p }.empty?
115
+ paint_sprite(dest, matches)
116
+ end
117
+ end
118
+
109
119
  def revision
110
120
  dump_manifest
111
121
  [env.revision].flatten.each do |rev|
@@ -115,6 +125,27 @@ module Linner
115
125
  end
116
126
  end
117
127
 
128
+ def paint_sprite(dest, images)
129
+ images = images.map do |path|
130
+ ImageProxy.new(path, ChunkyPNG::Image.from_file(path))
131
+ end
132
+ sprite = Sprite.new(images).pack!
133
+ map = ChunkyPNG::Image.new(sprite.root[:w], sprite.root[:h], ChunkyPNG::Color::TRANSPARENT)
134
+
135
+ sprite.images.each do |image|
136
+ map.compose!(image.image, image.left, image.top)
137
+ end
138
+
139
+ name = File.basename(dest).sub(/[^.]+\z/, "png")
140
+ path = File.join(env.public_folder, env.sprites["path"], File.basename(dest).sub(/[^.]+\z/, "png"))
141
+ FileUtils.mkdir_p File.dirname(path)
142
+ map.save path
143
+
144
+ asset = Asset.new(File.join env.public_folder, dest)
145
+ asset.content = sprite.generate_style(env.sprites, name)
146
+ asset.write
147
+ end
148
+
118
149
  def write_template(dest, child_assets)
119
150
  asset = Asset.new(File.join env.public_folder, dest)
120
151
  content = child_assets.inject("") {|s, m| s << cache[m].content}
@@ -35,6 +35,10 @@ module Linner
35
35
  @env["bundles"] || []
36
36
  end
37
37
 
38
+ def sprites
39
+ @env["sprites"] || {}
40
+ end
41
+
38
42
  def modules_ignored
39
43
  Dir.glob(@env["modules"]["ignored"])
40
44
  end
@@ -0,0 +1,124 @@
1
+ module Linner
2
+ ImageProxy = Struct.new(:path, :image, :top, :left) do
3
+ def width
4
+ image.width
5
+ end
6
+
7
+ def height
8
+ image.height
9
+ end
10
+ end
11
+
12
+ class Sprite
13
+
14
+ attr_accessor :root, :images
15
+
16
+ def initialize(images)
17
+ @images = images.sort do |a, b|
18
+ diff = [b.width, b.height].max <=> [a.width, a.height].max
19
+ diff = [b.width, b.height].min <=> [a.width, a.height].min if diff.zero?
20
+ diff = b.height <=> a.height if diff.zero?
21
+ diff = b.width <=> a.width if diff.zero?
22
+ diff
23
+ end
24
+
25
+ @root = { :x => 0, :y => 0, :w => @images.first.width, :h => @images.first.height}
26
+ end
27
+
28
+ def pack!
29
+ @images.each do |image|
30
+ if block = find_block(@root, image)
31
+ place_image(block, image)
32
+ split_block(block, image)
33
+ else
34
+ @root = expand_root(@root, image)
35
+ redo
36
+ end
37
+ end
38
+ self
39
+ end
40
+
41
+ def generate_style(config, name)
42
+ selector = config["selector"] || ".icon-"
43
+ @images.inject("") do |style, image|
44
+ logical_path = Asset.new(image.path).logical_path
45
+ selector_with_pseudo_class = logical_path.chomp(File.extname(logical_path))
46
+ .gsub("/", "-")
47
+ .gsub("_active", ".active")
48
+ .gsub("_hover", ":hover")
49
+ .gsub("_", "-")
50
+ style <<
51
+ "#{selector}#{selector_with_pseudo_class} {
52
+ width: #{image.width}px;
53
+ height: #{image.height}px;
54
+ background: url(#{File.join config['url'], name}) -#{image.left}px -#{image.top}px no-repeat;
55
+ }
56
+ "
57
+ end
58
+ end
59
+
60
+ private
61
+ def find_block(root, image)
62
+ if root[:used]
63
+ find_block(root[:right], image) || find_block(root[:down], image)
64
+ elsif (image.width <= root[:w]) && (image.height <= root[:h])
65
+ root
66
+ end
67
+ end
68
+
69
+ def place_image(block, image)
70
+ image.top = block[:y]
71
+ image.left = block[:x]
72
+ end
73
+
74
+ def split_block(block, image)
75
+ block[:used] = true
76
+ block[:down] = {:x => block[:x], :y => block[:y] + image.width, :w => block[:w], :h => block[:h] - image.height}
77
+ block[:right] = {:x => block[:x] + image.width, :y => block[:y], :w => block[:w] - image.width, :h => image.height}
78
+ end
79
+
80
+ def expand_root(root, image)
81
+ can_expand_down = (image.width <= root[:w])
82
+ can_expand_right = (image.height <= root[:h])
83
+
84
+ should_expand_down = can_expand_down && (root[:w] >= (root[:h] + image.height))
85
+ should_expand_right = can_expand_right && (root[:h] >= (root[:w] + image.width))
86
+
87
+ if should_expand_right
88
+ expand_right(root, image.width)
89
+ elsif should_expand_down
90
+ expand_down(root, image.height)
91
+ elsif can_expand_right
92
+ expand_right(root, image.width)
93
+ elsif can_expand_down
94
+ expand_down(root, image.height)
95
+ else
96
+ raise RuntimeError, "Crashed!"
97
+ end
98
+ end
99
+
100
+ def expand_right(root, width)
101
+ Hash[
102
+ :used => true,
103
+ :x => 0,
104
+ :y => 0,
105
+ :w => root[:w] + width,
106
+ :h => root[:h],
107
+ :down => root,
108
+ :right => { :x => root[:w], :y => 0, :w => width, :h => root[:h] }
109
+ ]
110
+ end
111
+
112
+ def expand_down(root, height)
113
+ Hash[
114
+ :used => true,
115
+ :x => 0,
116
+ :y => 0,
117
+ :w => root[:w],
118
+ :h => root[:h] + height,
119
+ :down => { :x => 0, :y => root[:h], :w => root[:w], :h => height },
120
+ :right => root
121
+ ]
122
+ end
123
+ end # Sprite
124
+ end # Linner
@@ -0,0 +1 @@
1
+ @import "icons";
@@ -12,7 +12,7 @@
12
12
  <!--[if lt IE 7]>
13
13
  <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
14
14
  <![endif]-->
15
-
15
+ <div class="icon-logo"></div>
16
16
  <script src="/scripts/vendor.js"></script>
17
17
  <script src="/scripts/app.js"></script>
18
18
  <script src="/scripts/templates.js"></script>
@@ -2,38 +2,50 @@ paths:
2
2
  public: "public"
3
3
  groups:
4
4
  scripts:
5
+ paths:
6
+ - app/scripts
5
7
  concat:
6
8
  "/scripts/app.js": "app/**/*.{js,coffee}"
7
9
  "/scripts/vendor.js": "vendor/**/*.{js,coffee}"
8
10
  order:
9
- - "vendor/jquery-1.10.2.js"
10
- - "..."
11
- - "app/scripts/app.coffee"
11
+ - vendor/jquery-1.10.2.js
12
+ - ...
13
+ - app/scripts/app.coffee
12
14
  styles:
15
+ paths:
16
+ - app/styles
13
17
  concat:
14
18
  "/styles/app.css": "app/styles/**/[a-z]*.{css,scss,sass}"
15
19
  images:
16
- copy:
17
- "/images/": "app/images/**/*.{png,gif}"
20
+ paths:
21
+ - app/images
22
+ sprite:
23
+ "../app/images/icons.scss": "app/images/**/*.png"
18
24
  views:
25
+ paths:
26
+ - app/views
19
27
  copy:
20
28
  "/": "app/views/**/*.html"
21
29
  templates:
22
30
  paths:
23
- - "app/templates"
31
+ - app/templates
24
32
  precompile:
25
33
  "/scripts/templates.js": "app/templates/**/*.hbs"
26
34
  modules:
27
- wrapper: "cmd"
28
- ignored: "vendor/**/*"
29
- definition: "/scripts/app.js"
30
- revision: "index.html"
35
+ wrapper: cmd
36
+ ignored: vendor/**/*
37
+ definition: /scripts/app.js
38
+ sprites:
39
+ url: /images/
40
+ path: /images/
41
+ selector: .icon-
42
+ revision: index.html
31
43
  notification: true
32
44
  bundles:
33
- "jquery.js":
34
- version: "1.10.2"
35
- url: "http://code.jquery.com/jquery-1.10.2.js"
36
- "handlebars.js":
37
- version: "1.0.0"
38
- url: "https://raw.github.com/wycats/handlebars.js/1.0.0/dist/handlebars.runtime.js"
45
+ jquery.js:
46
+ version: 1.10.2
47
+ url: http://code.jquery.com/jquery-1.10.2.js
48
+ handlebars.js:
49
+ version: 1.0.0
50
+ url: https://raw.github.com/wycats/handlebars.js/1.0.0/dist/handlebars.runtime.js
39
51
 
@@ -1,3 +1,3 @@
1
1
  module Linner
2
- VERSION = "0.5.1"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe Sprite do
4
+ before(:all) do
5
+ MockImage = Struct.new :width, :height, :top, :left
6
+ @sprites = Environment.new(root.join "config.yml").sprites
7
+ image0 = MockImage.new 2, 3
8
+ image1 = MockImage.new 5, 3
9
+ image2 = MockImage.new 6, 2
10
+ image3 = MockImage.new 2, 24
11
+ image4 = MockImage.new 1, 3
12
+ @images = [image0, image1, image2, image3, image4]
13
+ end
14
+
15
+ it "should be an empty hash" do
16
+ @sprites.should eql(Hash.new)
17
+ end
18
+
19
+ it "should be fit in blocks" do
20
+ sprites = Sprite.new(@images)
21
+ sprites.pack!
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saito
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-13 00:00:00.000000000 Z
11
+ date: 2013-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reel
@@ -263,10 +263,12 @@ files:
263
263
  - lib/linner/helper.rb
264
264
  - lib/linner/notifier.rb
265
265
  - lib/linner/reactor.rb
266
+ - lib/linner/sprite.rb
266
267
  - lib/linner/template.rb
267
268
  - lib/linner/templates/app/images/.gitkeep
269
+ - lib/linner/templates/app/images/logo.png
268
270
  - lib/linner/templates/app/scripts/app.coffee
269
- - lib/linner/templates/app/styles/app.sass
271
+ - lib/linner/templates/app/styles/app.scss
270
272
  - lib/linner/templates/app/templates/welcome.hbs
271
273
  - lib/linner/templates/app/views/index.html
272
274
  - lib/linner/templates/bin/server
@@ -283,6 +285,7 @@ files:
283
285
  - spec/linner/bundler_spec.rb
284
286
  - spec/linner/environment_spec.rb
285
287
  - spec/linner/helper_spec.rb
288
+ - spec/linner/sprites_spec.rb
286
289
  - spec/linner/template_spec.rb
287
290
  - spec/linner/wrapper_spec.rb
288
291
  - spec/spec_helper.rb
@@ -309,7 +312,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
309
312
  version: '0'
310
313
  requirements: []
311
314
  rubyforge_project:
312
- rubygems_version: 2.1.0
315
+ rubygems_version: 2.0.14
313
316
  signing_key:
314
317
  specification_version: 4
315
318
  summary: HTML5 Application Assembler
@@ -320,7 +323,7 @@ test_files:
320
323
  - spec/linner/bundler_spec.rb
321
324
  - spec/linner/environment_spec.rb
322
325
  - spec/linner/helper_spec.rb
326
+ - spec/linner/sprites_spec.rb
323
327
  - spec/linner/template_spec.rb
324
328
  - spec/linner/wrapper_spec.rb
325
329
  - spec/spec_helper.rb
326
- has_rdoc:
File without changes