linner 0.5.1 → 0.6.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.
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