rack-livejs 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ language: ruby
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
  gem "rake", "~> 10.0.4"
7
7
 
8
8
  group :test do
9
+ gem "nokogiri"
9
10
  gem "rack-test", "~> 0.6.2"
10
11
  gem "rspec", "~> 2.13.0"
11
12
  end
@@ -0,0 +1 @@
1
+ require "rack/livejs"
@@ -1,5 +1,3 @@
1
- require "rack/livejs/version"
2
-
3
1
  module Rack
4
2
  class Livejs
5
3
 
@@ -10,34 +8,42 @@ module Rack
10
8
  end
11
9
 
12
10
  def call(env)
13
- self.dup._call(env)
14
- end
15
-
16
- protected
17
-
18
- def _call(env)
11
+ return livejs_response if env["PATH_INFO"] == livejs_path
19
12
  status, headers, body = @app.call(env)
20
13
  if headers["Content-Type"] == "text/html"
21
- html = read_all(body)
22
- modified_html = inject_tag_into(html)
23
- headers["Content-Length"] = modified_html.size
24
- body = [modified_html]
14
+ new_html = inject_livejs(read_body(body))
15
+ headers["Content-Length"] = new_html.size.to_s
16
+ body = [new_html]
25
17
  end
26
18
  [status, headers, body]
27
19
  end
28
20
 
29
- def read_all(body)
30
- html = ""
31
- body.each { |fragment| html << fragment }
32
- html
21
+ private
22
+
23
+ def livejs_path
24
+ "/_rack_livejs_/live.js"
25
+ end
26
+
27
+ def read_body(body)
28
+ "".tap do |buffer|
29
+ body.each { |chunk| buffer << chunk }
30
+ end
31
+ end
32
+
33
+ def inject_livejs(html)
34
+ html.sub(%r{(<head( [^>]*)?>)}i) { $1 + %{<script src="#{livejs_path}"/>} }
35
+ end
36
+
37
+ def livejs_javascript
38
+ @@livejs_javascript ||= ::File.read(::File.expand_path("../../../vendor/livejs/live.js", __FILE__))
33
39
  end
34
40
 
35
- def inject_tag_into(html)
36
- doc = Nokogiri::HTML(html)
37
- head = doc.css("html head").first
38
- return html unless head
39
- head.add_child(%{<script src="http://livejs.com/live.js"/>})
40
- doc.to_html
41
+ def livejs_response
42
+ [
43
+ 200,
44
+ { 'Content-Type' => "application/javascript", 'Content-Length' => livejs_javascript.size.to_s },
45
+ [ livejs_javascript ]
46
+ ]
41
47
  end
42
48
 
43
49
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Livejs
3
- VERSION = "0.0.1"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency "nokogiri", ">= 1.4.0"
22
21
  spec.add_development_dependency "bundler", "~> 1.3"
23
22
 
24
23
  end
@@ -2,7 +2,6 @@ require 'nokogiri'
2
2
  require 'rack'
3
3
  require 'rack/livejs'
4
4
  require 'rack/test'
5
- require 'rspec'
6
5
 
7
6
  describe Rack::Livejs do
8
7
 
@@ -13,7 +12,7 @@ describe Rack::Livejs do
13
12
  Rack::Directory.new(fixtures_dir)
14
13
  end
15
14
 
16
- let(:app) { described_class.new(decorated_app) }
15
+ let(:app) { Rack::Lint.new(described_class.new(decorated_app)) }
17
16
 
18
17
  describe "getting an HTML file" do
19
18
 
@@ -31,7 +30,7 @@ describe Rack::Livejs do
31
30
  it "injects the live.js script" do
32
31
  script_tag = html.css("head script").first
33
32
  expect(script_tag).to_not be_nil
34
- expect(script_tag[:src]).to eq("http://livejs.com/live.js")
33
+ expect(script_tag[:src]).to eq("/_rack_livejs_/live.js")
35
34
  end
36
35
 
37
36
  end
@@ -56,4 +55,20 @@ describe Rack::Livejs do
56
55
 
57
56
  end
58
57
 
58
+ describe "getting live.js" do
59
+
60
+ before do
61
+ get "/_rack_livejs_/live.js"
62
+ end
63
+
64
+ it "returns the live.js script" do
65
+ expect(last_response).to be_ok
66
+ expect(last_response.content_type).to eq "application/javascript"
67
+ root = Pathname(__FILE__).parent.parent.parent
68
+ live_js_script = root + "vendor/livejs/live.js"
69
+ expect(last_response.body).to eq(live_js_script.read)
70
+ end
71
+
72
+ end
73
+
59
74
  end
@@ -0,0 +1,22 @@
1
+ MIT License http://en.wikipedia.org/wiki/MIT_License
2
+ IN SHORT: It's free. You can use it commercially. Don't sue us.
3
+
4
+ Copyright (C) 2011 by Martin Kool and Q42
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE
@@ -0,0 +1,233 @@
1
+ /*
2
+ Live.js - One script closer to Designing in the Browser
3
+ Written for Handcraft.com by Martin Kool (@mrtnkl).
4
+
5
+ Version 4.
6
+ Recent change: Made stylesheet and mimetype checks case insensitive.
7
+
8
+ http://livejs.com
9
+ http://livejs.com/license (MIT)
10
+ @livejs
11
+
12
+ Include live.js#css to monitor css changes only.
13
+ Include live.js#js to monitor js changes only.
14
+ Include live.js#html to monitor html changes only.
15
+ Mix and match to monitor a preferred combination such as live.js#html,css
16
+
17
+ By default, just include live.js to monitor all css, js and html changes.
18
+
19
+ Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
20
+ as a page reload due to a change in html or css would not re-include the bookmarklet.
21
+ To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
22
+ */
23
+ (function () {
24
+
25
+ var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
26
+ resources = {},
27
+ pendingRequests = {},
28
+ currentLinkElements = {},
29
+ oldLinkElements = {},
30
+ interval = 1000,
31
+ loaded = false,
32
+ active = { "html": 1, "css": 1, "js": 1 };
33
+
34
+ var Live = {
35
+
36
+ // performs a cycle per interval
37
+ heartbeat: function () {
38
+ if (document.body) {
39
+ // make sure all resources are loaded on first activation
40
+ if (!loaded) Live.loadresources();
41
+ Live.checkForChanges();
42
+ }
43
+ setTimeout(Live.heartbeat, interval);
44
+ },
45
+
46
+ // loads all local css and js resources upon first activation
47
+ loadresources: function () {
48
+
49
+ // helper method to assert if a given url is local
50
+ function isLocal(url) {
51
+ var loc = document.location,
52
+ reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
53
+ return url.match(reg);
54
+ }
55
+
56
+ // gather all resources
57
+ var scripts = document.getElementsByTagName("script"),
58
+ links = document.getElementsByTagName("link"),
59
+ uris = [];
60
+
61
+ // track local js urls
62
+ for (var i = 0; i < scripts.length; i++) {
63
+ var script = scripts[i], src = script.getAttribute("src");
64
+ if (src && isLocal(src))
65
+ uris.push(src);
66
+ if (src && src.match(/\blive.js#/)) {
67
+ for (var type in active)
68
+ active[type] = src.match("[#,|]" + type) != null
69
+ if (src.match("notify"))
70
+ alert("Live.js is loaded.");
71
+ }
72
+ }
73
+ if (!active.js) uris = [];
74
+ if (active.html) uris.push(document.location.href);
75
+
76
+ // track local css urls
77
+ for (var i = 0; i < links.length && active.css; i++) {
78
+ var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
79
+ if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
80
+ uris.push(href);
81
+ currentLinkElements[href] = link;
82
+ }
83
+ }
84
+
85
+ // initialize the resources info
86
+ for (var i = 0; i < uris.length; i++) {
87
+ var url = uris[i];
88
+ Live.getHead(url, function (url, info) {
89
+ resources[url] = info;
90
+ });
91
+ }
92
+
93
+ // add rule for morphing between old and new css files
94
+ var head = document.getElementsByTagName("head")[0],
95
+ style = document.createElement("style"),
96
+ rule = "transition: all .3s ease-out;"
97
+ css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join('');
98
+ style.setAttribute("type", "text/css");
99
+ head.appendChild(style);
100
+ style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));
101
+
102
+ // yep
103
+ loaded = true;
104
+ },
105
+
106
+ // check all tracking resources for changes
107
+ checkForChanges: function () {
108
+ for (var url in resources) {
109
+ if (pendingRequests[url])
110
+ continue;
111
+
112
+ Live.getHead(url, function (url, newInfo) {
113
+ var oldInfo = resources[url],
114
+ hasChanged = false;
115
+ resources[url] = newInfo;
116
+ for (var header in oldInfo) {
117
+ // do verification based on the header type
118
+ var oldValue = oldInfo[header],
119
+ newValue = newInfo[header],
120
+ contentType = newInfo["Content-Type"];
121
+ switch (header.toLowerCase()) {
122
+ case "etag":
123
+ if (!newValue) break;
124
+ // fall through to default
125
+ default:
126
+ hasChanged = oldValue != newValue;
127
+ break;
128
+ }
129
+ // if changed, act
130
+ if (hasChanged) {
131
+ Live.refreshResource(url, contentType);
132
+ break;
133
+ }
134
+ }
135
+ });
136
+ }
137
+ },
138
+
139
+ // act upon a changed url of certain content type
140
+ refreshResource: function (url, type) {
141
+ switch (type.toLowerCase()) {
142
+ // css files can be reloaded dynamically by replacing the link element
143
+ case "text/css":
144
+ var link = currentLinkElements[url],
145
+ html = document.body.parentNode,
146
+ head = link.parentNode,
147
+ next = link.nextSibling,
148
+ newLink = document.createElement("link");
149
+
150
+ html.className = html.className.replace(/\s*livejs\-loading/gi, '') + ' livejs-loading';
151
+ newLink.setAttribute("type", "text/css");
152
+ newLink.setAttribute("rel", "stylesheet");
153
+ newLink.setAttribute("href", url + "?now=" + new Date() * 1);
154
+ next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
155
+ currentLinkElements[url] = newLink;
156
+ oldLinkElements[url] = link;
157
+
158
+ // schedule removal of the old link
159
+ Live.removeoldLinkElements();
160
+ break;
161
+
162
+ // check if an html resource is our current url, then reload
163
+ case "text/html":
164
+ if (url != document.location.href)
165
+ return;
166
+
167
+ // local javascript changes cause a reload as well
168
+ case "text/javascript":
169
+ case "application/javascript":
170
+ case "application/x-javascript":
171
+ document.location.reload();
172
+ }
173
+ },
174
+
175
+ // removes the old stylesheet rules only once the new one has finished loading
176
+ removeoldLinkElements: function () {
177
+ var pending = 0;
178
+ for (var url in oldLinkElements) {
179
+ // if this sheet has any cssRules, delete the old link
180
+ try {
181
+ var link = currentLinkElements[url],
182
+ oldLink = oldLinkElements[url],
183
+ html = document.body.parentNode,
184
+ sheet = link.sheet || link.styleSheet,
185
+ rules = sheet.rules || sheet.cssRules;
186
+ if (rules.length >= 0) {
187
+ oldLink.parentNode.removeChild(oldLink);
188
+ delete oldLinkElements[url];
189
+ setTimeout(function () {
190
+ html.className = html.className.replace(/\s*livejs\-loading/gi, '');
191
+ }, 100);
192
+ }
193
+ } catch (e) {
194
+ pending++;
195
+ }
196
+ if (pending) setTimeout(Live.removeoldLinkElements, 50);
197
+ }
198
+ },
199
+
200
+ // performs a HEAD request and passes the header info to the given callback
201
+ getHead: function (url, callback) {
202
+ pendingRequests[url] = true;
203
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
204
+ xhr.open("HEAD", url, true);
205
+ xhr.onreadystatechange = function () {
206
+ delete pendingRequests[url];
207
+ if (xhr.readyState == 4 && xhr.status != 304) {
208
+ xhr.getAllResponseHeaders();
209
+ var info = {};
210
+ for (var h in headers) {
211
+ var value = xhr.getResponseHeader(h);
212
+ // adjust the simple Etag variant to match on its significant part
213
+ if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, '');
214
+ if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
215
+ info[h] = value;
216
+ }
217
+ callback(url, info);
218
+ }
219
+ }
220
+ xhr.send();
221
+ }
222
+ };
223
+
224
+ // start listening
225
+ if (document.location.protocol != "file:") {
226
+ if (!window.liveJsLoaded)
227
+ Live.heartbeat();
228
+
229
+ window.liveJsLoaded = true;
230
+ }
231
+ else if (window.console)
232
+ console.log("Live.js doesn't support the file protocol. It needs http.");
233
+ })();
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-livejs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,24 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-05 00:00:00.000000000 Z
12
+ date: 2013-06-23 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: nokogiri
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: 1.4.0
22
- type: :runtime
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: 1.4.0
30
14
  - !ruby/object:Gem::Dependency
31
15
  name: bundler
32
16
  requirement: !ruby/object:Gem::Requirement
@@ -52,16 +36,20 @@ extra_rdoc_files: []
52
36
  files:
53
37
  - .gitignore
54
38
  - .rspec
39
+ - .travis.yml
55
40
  - Gemfile
56
41
  - LICENSE.txt
57
42
  - README.md
58
43
  - Rakefile
44
+ - lib/rack-livejs.rb
59
45
  - lib/rack/livejs.rb
60
46
  - lib/rack/livejs/version.rb
61
47
  - rack-livejs.gemspec
62
48
  - spec/fixtures/page.html
63
49
  - spec/fixtures/text.txt
64
50
  - spec/rack/livejs_spec.rb
51
+ - vendor/livejs/LICENSE.txt
52
+ - vendor/livejs/live.js
65
53
  homepage: http://github.com/mdub/rack-livejs
66
54
  licenses:
67
55
  - MIT
@@ -77,7 +65,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
77
65
  version: '0'
78
66
  segments:
79
67
  - 0
80
- hash: -132143474654823442
68
+ hash: 2181008550174536603
81
69
  required_rubygems_version: !ruby/object:Gem::Requirement
82
70
  none: false
83
71
  requirements:
@@ -86,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
74
  version: '0'
87
75
  segments:
88
76
  - 0
89
- hash: -132143474654823442
77
+ hash: 2181008550174536603
90
78
  requirements: []
91
79
  rubyforge_project:
92
80
  rubygems_version: 1.8.23