marlene 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/README.markdown +17 -0
- data/Rakefile +19 -0
- data/bin/bm.pl +33 -0
- data/bin/marlene +112 -0
- data/lib/marlene.rb +60 -0
- data/lib/templates/loader.js +6 -0
- data/lib/templates/page.html +10 -0
- data/spec/marlene_spec.rb +105 -0
- metadata +107 -0
data/.gitignore
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Marlene - A bookmarklet builder
|
2
|
+
===============================
|
3
|
+
|
4
|
+
Marlene is inspired by John Grubers Perl based [JavaScript Bookmarklet Builder](http://daringfireball.net/2007/03/javascript_bookmarklet_builder).
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
Specs
|
9
|
+
-----
|
10
|
+
|
11
|
+
run specs with
|
12
|
+
|
13
|
+
spec spec/marlene_spec.rb --format specdoc
|
14
|
+
|
15
|
+
or
|
16
|
+
|
17
|
+
autospec
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/lib/marlene.rb"
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |gemspec|
|
6
|
+
gemspec.name = "marlene"
|
7
|
+
gemspec.version = Marlene::VERSION
|
8
|
+
gemspec.summary = "Bookmarklet Generator from a local or remote JavaScript file"
|
9
|
+
gemspec.description = "Marlene generates bookmarklet files or bookmarklet-pages from existing local javascript files or from remote javascript files represented by a URL."
|
10
|
+
gemspec.email = "thomduerr@gmail.com"
|
11
|
+
gemspec.homepage = "http://github.com/thomd/marlene/"
|
12
|
+
gemspec.authors = ["Thomas Duerr"]
|
13
|
+
gemspec.add_dependency "yui-compressor", ">= 0.9.1"
|
14
|
+
gemspec.add_dependency "mustache", ">= 0.11.2"
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
19
|
+
end
|
data/bin/bm.pl
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env perl
|
2
|
+
#
|
3
|
+
# http://daringfireball.net/2007/03/javascript_bookmarklet_builder
|
4
|
+
|
5
|
+
use strict;
|
6
|
+
use warnings;
|
7
|
+
use URI::Escape qw(uri_escape_utf8);
|
8
|
+
use open IO => ":utf8", # UTF8 by default
|
9
|
+
":std"; # Apply to STDIN/STDOUT/STDERR
|
10
|
+
|
11
|
+
my $src = do { local $/; <> };
|
12
|
+
|
13
|
+
# Zap the first line if there's already a bookmarklet comment:
|
14
|
+
$src =~ s{^// ?javascript:.+\n}{};
|
15
|
+
my $bookmarklet = $src;
|
16
|
+
|
17
|
+
for ($bookmarklet) {
|
18
|
+
s{^\s*//.+\n}{}gm; # Kill comments.
|
19
|
+
s{\t}{ }gm; # Tabs to spaces
|
20
|
+
s{[ ]{2,}}{ }gm; # Space runs to one space
|
21
|
+
s{^\s+}{}gm; # Kill line-leading whitespace
|
22
|
+
s{\s+$}{}gm; # Kill line-ending whitespace
|
23
|
+
s{\n}{}gm; # Kill newlines
|
24
|
+
}
|
25
|
+
|
26
|
+
# Escape single- and double-quotes, spaces, control chars, unicode:
|
27
|
+
$bookmarklet = "javascript:" .
|
28
|
+
uri_escape_utf8($bookmarklet, qq('" \x00-\x1f\x7f-\xff));
|
29
|
+
|
30
|
+
print "// $bookmarklet\n" . $src;
|
31
|
+
|
32
|
+
# Put bookmarklet on clipboard:
|
33
|
+
`/bin/echo -n '$bookmarklet' | /usr/bin/pbcopy`;
|
data/bin/marlene
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'trollop'
|
5
|
+
require 'uri'
|
6
|
+
require 'net/http'
|
7
|
+
require File.dirname(__FILE__) + '/../lib/marlene.rb'
|
8
|
+
|
9
|
+
opts = Trollop::options do
|
10
|
+
version "Marlene 0.1 (c) 2010 Thomas Duerr"
|
11
|
+
banner <<-EOS
|
12
|
+
Marlene builds code for bookmarklets from a local or from a remote hoted javascript file.
|
13
|
+
|
14
|
+
Usage:
|
15
|
+
marlene [options] <input>
|
16
|
+
|
17
|
+
where <input> is a file path or an URI to a javascript file.
|
18
|
+
|
19
|
+
[options] are:
|
20
|
+
EOS
|
21
|
+
|
22
|
+
opt :file, "write bookmarklet to file. Otherwise output goes to clipboard (OSX only) and STDOUT", :default => false
|
23
|
+
opt :include, "include bookmarklet code on top of input file as a comment", :default => false
|
24
|
+
# opt :jslint, "analyse javascript code via jslint before", :default => false, :short => "-l"
|
25
|
+
opt :publish, "build html page for bookmarklet and open it in a browser.", :default => false
|
26
|
+
end
|
27
|
+
|
28
|
+
Trollop::die "need at least a javascript file-path or a valid URI as input" if ARGV.empty?
|
29
|
+
|
30
|
+
input = ARGV.to_s
|
31
|
+
|
32
|
+
uri = URI.extract(input, 'http')[0]
|
33
|
+
if uri.nil? then
|
34
|
+
|
35
|
+
#
|
36
|
+
# ===== local javascript ==========
|
37
|
+
#
|
38
|
+
|
39
|
+
# stop if file does not exist
|
40
|
+
Trollop::die "#{input} must exist" unless File.exist?(input) if input
|
41
|
+
|
42
|
+
# read javascript file
|
43
|
+
local_file = File.read(input)
|
44
|
+
|
45
|
+
# build bookmarklet
|
46
|
+
bookmarklet = local_file.compress.to_bookmarklet
|
47
|
+
|
48
|
+
# include bookmarklet code as comment on top of input file
|
49
|
+
if opts[:include]
|
50
|
+
|
51
|
+
# remove first line if there's already a bookmarklet comment and
|
52
|
+
input.gsub!(/^\/\/ javascript:.+\n\n/, '')
|
53
|
+
|
54
|
+
File.open(input, "w") do |f|
|
55
|
+
f.puts "// #{bookmarklet}\n\n#{local_file}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
else
|
60
|
+
|
61
|
+
#
|
62
|
+
# ===== remote javascript ==========
|
63
|
+
#
|
64
|
+
|
65
|
+
# stop if URIs HTTP status is not 200
|
66
|
+
def remote_file_exists?(url)
|
67
|
+
url = URI.parse(url)
|
68
|
+
Net::HTTP.start(url.host, url.port) do |http|
|
69
|
+
return http.head(url.request_uri).code == "200"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
Trollop::die "#{uri} must exist" unless remote_file_exists?(uri) if uri
|
73
|
+
|
74
|
+
# build bookmarklet loadscript
|
75
|
+
bookmarklet = uri.to_loader_script.compress.to_bookmarklet
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
#
|
81
|
+
# ===== output ==========
|
82
|
+
#
|
83
|
+
if opts[:file]
|
84
|
+
|
85
|
+
# write bookmarklet code to a file "filename.bookmarklet"
|
86
|
+
output = input.gsub(Regexp.new(File.extname(input)), ".bookmarklet")
|
87
|
+
File.open(output, "w").write(bookmarklet)
|
88
|
+
else
|
89
|
+
|
90
|
+
# copy bookmarklet code to clipboard (OSX only)
|
91
|
+
IO.popen('pbcopy', 'w').puts bookmarklet
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# generate and open html page with bookmarklet-link in order to drag'n'drop bookmarklet up to bookmarks-bar
|
96
|
+
if opts[:publish]
|
97
|
+
begin
|
98
|
+
require 'bcat'
|
99
|
+
IO.popen('bcat', 'w').puts bookmarklet.to_bookmarklet_page
|
100
|
+
rescue LoadError => e
|
101
|
+
puts "Error: Publishing requires bcat gem: http://rtomayko.github.com/bcat/"
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# write to stdout
|
108
|
+
if !opts[:publish] && !opts[:file]
|
109
|
+
puts bookmarklet
|
110
|
+
end
|
111
|
+
|
112
|
+
|
data/lib/marlene.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "uri"
|
2
|
+
require "yui/compressor"
|
3
|
+
require "mustache"
|
4
|
+
|
5
|
+
|
6
|
+
module Marlene
|
7
|
+
|
8
|
+
VERSION = "0.1.1"
|
9
|
+
ANCHOR_TEXTNODE = "bookmarklet"
|
10
|
+
PSEUDOCOL = "javascript:"
|
11
|
+
LOADER_SCRIPT = File.join(File.dirname(__FILE__), 'templates', 'loader.js')
|
12
|
+
BOOKMARKLET_PAGE = File.join(File.dirname(__FILE__), 'templates', 'page.html')
|
13
|
+
|
14
|
+
def yui_compressor
|
15
|
+
YUI::JavaScriptCompressor.new(:munge => true)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class String
|
22
|
+
include Marlene
|
23
|
+
|
24
|
+
# transform into bookmarklet
|
25
|
+
def to_bookmarklet
|
26
|
+
js = self.to_s
|
27
|
+
js.gsub!(/^ +/, '') # remove line-leading whitespace
|
28
|
+
js.gsub!(/ +$/, '') # remove line-ending whitespace
|
29
|
+
js.gsub!(/\t/, ' ') # replace tabs with spaces
|
30
|
+
js.gsub!(/ +/, ' ') # replace multiple spaces with a single space
|
31
|
+
js.gsub!(/^ *\/\/.+\n/, '') # remove comment lines starting with a double slash
|
32
|
+
js.gsub!(/\n/, '') # remove all newlines
|
33
|
+
js = URI.escape(js, %Q(''" )) # URI escape: single- and double-quotes, spaces
|
34
|
+
PSEUDOCOL + js # prefix with 'javascript:'
|
35
|
+
end
|
36
|
+
|
37
|
+
# use YUI compressor to minimize javascript code
|
38
|
+
def compress
|
39
|
+
yui_compressor.compress(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
# insert into loader script a javascript URL
|
43
|
+
def to_loader_script
|
44
|
+
Mustache.template_file = LOADER_SCRIPT
|
45
|
+
loader = Mustache.new
|
46
|
+
loader[:script] = self
|
47
|
+
loader.render
|
48
|
+
end
|
49
|
+
|
50
|
+
# insert bookmarklet into html code for drag-drop bookmarklet up to linkbar
|
51
|
+
def to_bookmarklet_page
|
52
|
+
Mustache.template_file = BOOKMARKLET_PAGE
|
53
|
+
page = Mustache.new
|
54
|
+
page[:bookmarklet] = self
|
55
|
+
page[:anchor] = ANCHOR_TEXTNODE
|
56
|
+
page.render
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "spec"
|
3
|
+
require File.dirname(__FILE__) + '/../lib/marlene.rb'
|
4
|
+
|
5
|
+
describe "Marlene bookmark generator" do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@remote_url = "http://example.com/this/is/a/remote/javascript.js"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should transform a local javascript file to a bookmarklet" do
|
12
|
+
input = javascript_local
|
13
|
+
output = bookmarklet_local
|
14
|
+
input.to_bookmarklet.should == output
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should transform and escape a local javascript file to a bookmarklet" do
|
18
|
+
input = javascript_local_unescaped
|
19
|
+
output = bookmarklet_local_escaped
|
20
|
+
input.to_bookmarklet.should == output
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should transform an external javascript into a bookmarklet loaderscript" do
|
24
|
+
input = @remote_url
|
25
|
+
output = bookmarklet_remote
|
26
|
+
input.to_loader_script.to_bookmarklet.should == output
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should compress javascript code with a javascript compressor" do
|
30
|
+
input = javascript_local
|
31
|
+
output = javascript_local_compressed
|
32
|
+
input.compress.should == output
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should transform a local javascript file to a compressed bookmarklet" do
|
36
|
+
input = javascript_local
|
37
|
+
output = bookmarklet_local_compressed
|
38
|
+
input.compress.to_bookmarklet.should == output
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should transform an external javascript into a compressed bookmarklet loaderscript" do
|
42
|
+
input = @remote_url
|
43
|
+
output = bookmarklet_remote_compressed
|
44
|
+
input.to_loader_script.compress.to_bookmarklet.should == output
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should write a html page with a bookmarklet link" do
|
48
|
+
input = javascript_local_compressed
|
49
|
+
output = html_page_compressed_bookmarklet
|
50
|
+
input.compress.to_bookmarklet.to_bookmarklet_page.should == output
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# fixtures
|
56
|
+
|
57
|
+
def javascript_local
|
58
|
+
"// a contains method for arrays
|
59
|
+
Array.prototype.contains = function(item){
|
60
|
+
return this.indexOf(item) > -1;
|
61
|
+
}
|
62
|
+
|
63
|
+
alert([1,2,3,4,5].contains(4));"
|
64
|
+
end
|
65
|
+
|
66
|
+
def javascript_local_unescaped
|
67
|
+
%Q{console.info(indentString + methodName, '-> ', result, "(", new Date - startTime, 'ms', ")");}
|
68
|
+
end
|
69
|
+
|
70
|
+
def javascript_local_compressed
|
71
|
+
"Array.prototype.contains=function(a){return this.indexOf(a)>-1};alert([1,2,3,4,5].contains(4));"
|
72
|
+
end
|
73
|
+
|
74
|
+
def bookmarklet_local
|
75
|
+
"javascript:Array.prototype.contains%20=%20function(item){return%20this.indexOf(item)%20>%20-1;}alert([1,2,3,4,5].contains(4));"
|
76
|
+
end
|
77
|
+
|
78
|
+
def bookmarklet_local_escaped
|
79
|
+
%Q{javascript:console.info(indentString%20+%20methodName,%20%27->%20%27,%20result,%20%22(%22,%20new%20Date%20-%20startTime,%20%27ms%27,%20%22)%22);}
|
80
|
+
end
|
81
|
+
|
82
|
+
def bookmarklet_local_compressed
|
83
|
+
"javascript:Array.prototype.contains=function(a){return%20this.indexOf(a)>-1};alert([1,2,3,4,5].contains(4));"
|
84
|
+
end
|
85
|
+
|
86
|
+
def bookmarklet_remote
|
87
|
+
"javascript:(function(){var%20script%20=%20document.createElement(%27script%27);script.type%20=%20%27text/javascript%27;script.src%20=%20%27http://example.com/this/is/a/remote/javascript.js?%27%20+%20(new%20Date().getTime());document.getElementsByTagName(%27body%27)[0].appendChild(script);})()"
|
88
|
+
end
|
89
|
+
|
90
|
+
def bookmarklet_remote_compressed
|
91
|
+
"javascript:(function(){var%20a=document.createElement(%22script%22);a.type=%22text/javascript%22;a.src=%22http://example.com/this/is/a/remote/javascript.js?%22+(new%20Date().getTime());document.getElementsByTagName(%22body%22)[0].appendChild(a)})();"
|
92
|
+
end
|
93
|
+
|
94
|
+
def html_page_compressed_bookmarklet
|
95
|
+
%Q(<!DOCTYPE html>
|
96
|
+
<html>
|
97
|
+
<head>
|
98
|
+
<meta charset="utf-8" />
|
99
|
+
<title>bookmarklet</title>
|
100
|
+
</head>
|
101
|
+
<body>
|
102
|
+
<a href="javascript:Array.prototype.contains=function(b){return%20this.indexOf(b)>-1};alert([1,2,3,4,5].contains(4));">bookmarklet</a>
|
103
|
+
</body>
|
104
|
+
</html>)
|
105
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: marlene
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Thomas Duerr
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-09 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: yui-compressor
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 57
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 9
|
33
|
+
- 1
|
34
|
+
version: 0.9.1
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: mustache
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 55
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 11
|
49
|
+
- 2
|
50
|
+
version: 0.11.2
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
description: Marlene generates bookmarklet files or bookmarklet-pages from existing local javascript files or from remote javascript files represented by a URL.
|
54
|
+
email: thomduerr@gmail.com
|
55
|
+
executables:
|
56
|
+
- bm.pl
|
57
|
+
- marlene
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README.markdown
|
62
|
+
files:
|
63
|
+
- .gitignore
|
64
|
+
- README.markdown
|
65
|
+
- Rakefile
|
66
|
+
- bin/marlene
|
67
|
+
- lib/marlene.rb
|
68
|
+
- lib/templates/loader.js
|
69
|
+
- lib/templates/page.html
|
70
|
+
- spec/marlene_spec.rb
|
71
|
+
- bin/bm.pl
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/thomd/marlene/
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options:
|
78
|
+
- --charset=UTF-8
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.3.7
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Bookmarklet Generator from a local or remote JavaScript file
|
106
|
+
test_files:
|
107
|
+
- spec/marlene_spec.rb
|