iated 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/Gemfile +3 -0
- data/Guardfile +24 -0
- data/LICENSE +23 -0
- data/Makefile +72 -0
- data/README.md +87 -0
- data/Rakefile +10 -0
- data/bin/iated +13 -0
- data/config.ru +5 -0
- data/extensions/chrome/background.html +5 -0
- data/extensions/chrome/background.js +105 -0
- data/extensions/chrome/contentscript.css +0 -0
- data/extensions/chrome/contentscript.js +110 -0
- data/extensions/chrome/jquery-ui.js +45 -0
- data/extensions/chrome/jquery.js +16 -0
- data/extensions/chrome/jquery.updater.js +46 -0
- data/extensions/chrome/manifest.json +19 -0
- data/extensions/chrome/yaml.js +489 -0
- data/extensions/tests/simple.html +23 -0
- data/features/extension_authenticates.feature +30 -0
- data/features/extension_edits.feature +45 -0
- data/features/step_definitions/extension_steps.rb +134 -0
- data/features/support/env.rb +47 -0
- data/features/support/hooks.rb +11 -0
- data/iated.gemspec +48 -0
- data/lib/iated.rb +111 -0
- data/lib/iated/browser_token_db.rb +76 -0
- data/lib/iated/edit_session.rb +221 -0
- data/lib/iated/helpers.rb +9 -0
- data/lib/iated/mcp.rb +144 -0
- data/lib/iated/page_helpers.rb +33 -0
- data/lib/iated/public/jquery-ui.js +101 -0
- data/lib/iated/public/jquery.js +2 -0
- data/lib/iated/public/robots.txt +5 -0
- data/lib/iated/server.rb +162 -0
- data/lib/iated/sys_pref.rb +201 -0
- data/lib/iated/version.rb +3 -0
- data/lib/iated/views/hello.haml +13 -0
- data/lib/iated/views/preferences.haml +27 -0
- data/lib/iated/views/reference.coffee +79 -0
- data/lib/iated/views/reference.haml +94 -0
- data/lib/iated/views/reference.scss +36 -0
- data/lib/iated/views/root.haml +13 -0
- data/spec/lib/iated/browser_token_db_spec.rb +68 -0
- data/spec/lib/iated/edit_session_spec.rb +157 -0
- data/spec/lib/iated/mcp_spec.rb +86 -0
- data/spec/lib/iated/sys_pref_spec.rb +40 -0
- data/spec/protocol/edit_spec.rb +88 -0
- data/spec/protocol/hello_spec.rb +18 -0
- data/spec/protocol/notfound_spec.rb +11 -0
- data/spec/protocol/ping_spec.rb +10 -0
- data/spec/protocol/preferences_spec.rb +35 -0
- data/spec/spec_helper.rb +21 -0
- metadata +460 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
!!!
|
2
|
+
%meta( charset="utf-8" )
|
3
|
+
%title It's All Text!
|
4
|
+
%link( rel="stylesheet" type="text/css" href="iated.css" )
|
5
|
+
|
6
|
+
%body( class="preferences" )
|
7
|
+
|
8
|
+
%div( id="content" )
|
9
|
+
%h1
|
10
|
+
It's All Text! Preferences
|
11
|
+
|
12
|
+
%form( method="post" action="/preferences" )
|
13
|
+
%input( type="hidden" name="token" value="#{token}" )
|
14
|
+
|
15
|
+
%label( for="editor" )
|
16
|
+
Editor:
|
17
|
+
%input( type="text" name="editor" value="#{editor}")
|
18
|
+
|
19
|
+
%label( for="port" )
|
20
|
+
Port:
|
21
|
+
%input( type="text" name="port" value="#{port}")
|
22
|
+
|
23
|
+
%label( for="config_dir" )
|
24
|
+
Config Directory:
|
25
|
+
%input( type="text" name="config_dir" value="#{config_dir}")
|
26
|
+
|
27
|
+
%input( type="submit" value="Save" )
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# Defaults for ajax requests.
|
2
|
+
jQuery.ajaxSetup cache: true
|
3
|
+
|
4
|
+
# Method to highlight a change.
|
5
|
+
jQuery.fn.highlight = ->
|
6
|
+
this.effect('highlight', 3 * 1000)
|
7
|
+
|
8
|
+
hello = () ->
|
9
|
+
jQuery.getJSON('/hello', (data) ->
|
10
|
+
console.log("GET /hello: %o", data)
|
11
|
+
)
|
12
|
+
|
13
|
+
secret = () ->
|
14
|
+
data=
|
15
|
+
secret: $('#secret').val()
|
16
|
+
jQuery.post('/hello', data, (data, textStatus, jqXHR) ->
|
17
|
+
console.log("POST /hello: %o %o %o", data, textStatus, jqXHR)
|
18
|
+
$('#token').val(data.token)
|
19
|
+
token=data.token
|
20
|
+
)
|
21
|
+
|
22
|
+
edit = () ->
|
23
|
+
tid = "#text"
|
24
|
+
text = $(tid).val()
|
25
|
+
url = window.location.toString()
|
26
|
+
extension = '.txt'
|
27
|
+
|
28
|
+
data=
|
29
|
+
tid: "#text"
|
30
|
+
text: $("#text").val()
|
31
|
+
url: url
|
32
|
+
extension: $('#extension').val()
|
33
|
+
token: $("#token").val()
|
34
|
+
|
35
|
+
jQuery.post('/edit', data, (data, textStatus, jqXHR) ->
|
36
|
+
console.log("POST /edit: %o %o %o", data, textStatus, jqXHR)
|
37
|
+
$('#sid').val(data.sid).highlight()
|
38
|
+
$('#change-id').val( if data.change_id then data.change_id else 0).highlight()
|
39
|
+
)
|
40
|
+
|
41
|
+
edit_prev = () ->
|
42
|
+
tid = "#text"
|
43
|
+
url = window.location.toString()
|
44
|
+
extension = '.text'
|
45
|
+
|
46
|
+
data=
|
47
|
+
tid: "#text"
|
48
|
+
url: url
|
49
|
+
extension: $('#extension').val()
|
50
|
+
token: $('#token').val()
|
51
|
+
|
52
|
+
jQuery.post('/edit', data, (data, textStatus, jqXHR) ->
|
53
|
+
console.log("POST /edit: %o %o %o", data, textStatus, jqXHR)
|
54
|
+
$('#sid').val(data.sid).highlight()
|
55
|
+
$('#change-id').val(if data.change_id then data.change_id else 0).highlight()
|
56
|
+
)
|
57
|
+
|
58
|
+
update = () ->
|
59
|
+
jQuery.getJSON('/edit/' + $('#sid').val() + "/" + $("#change-id").val(), (data) ->
|
60
|
+
console.log("GET /edit/:sid/:change-id: %o", data)
|
61
|
+
if data.change_id && data.change_id.toString() != $('#change-id').val()
|
62
|
+
$('#text').val(data.text).highlight()
|
63
|
+
$('#change-id').val(data.change_id).highlight()
|
64
|
+
)
|
65
|
+
|
66
|
+
preferences = () ->
|
67
|
+
event.preventDefault()
|
68
|
+
event.stopPropagation()
|
69
|
+
window.open('/preferences?token=' + encodeURIComponent($('#token').val()), "iat_prefs")
|
70
|
+
return true
|
71
|
+
|
72
|
+
jQuery ->
|
73
|
+
$("#hello-btn").click(hello);
|
74
|
+
$("#secret-btn").click(secret);
|
75
|
+
$("#edit-btn").click(edit);
|
76
|
+
$("#edit-prev-btn").click(edit_prev);
|
77
|
+
$("#update-btn").click(update);
|
78
|
+
$("#preferences-btn").click(preferences);
|
79
|
+
|
@@ -0,0 +1,94 @@
|
|
1
|
+
!!!
|
2
|
+
%meta( charset="utf-8" )
|
3
|
+
%title Reference Implementation
|
4
|
+
%script( type="text/javascript" src="/jquery.js" )
|
5
|
+
%script( type="text/javascript" src="/jquery-ui.js" )
|
6
|
+
%script( type="text/javascript" src="script.js" )
|
7
|
+
%link( rel="stylesheet" type="text/css" href="style.css" )
|
8
|
+
%body
|
9
|
+
%h1 Reference Implementation of IAT
|
10
|
+
|
11
|
+
%p
|
12
|
+
This is a reference implementation of IAT's browser side code. See the code on
|
13
|
+
%a( href="http://github.com/docwhat/iated" ) GitHub
|
14
|
+
|
15
|
+
%div#content-text
|
16
|
+
%h2 Textarea
|
17
|
+
An example textarea to play in.
|
18
|
+
|
19
|
+
%div#textarea-wrapper
|
20
|
+
%textarea#text="Some example text\nto play with."
|
21
|
+
|
22
|
+
%div#content-data
|
23
|
+
%h2 The data
|
24
|
+
|
25
|
+
%dl
|
26
|
+
%dt
|
27
|
+
Extension:
|
28
|
+
%dd
|
29
|
+
%input#extension( name="extension" type="text" value=".txt" )
|
30
|
+
%br
|
31
|
+
The extension used for the file when opening it up to the editor.
|
32
|
+
This value is checked on the server side against the list of approved
|
33
|
+
extensions.
|
34
|
+
|
35
|
+
%dt
|
36
|
+
Browser Authentication Secret:
|
37
|
+
%dd
|
38
|
+
%input#secret( name="secret" type="text" )
|
39
|
+
%br
|
40
|
+
This is a one-time secret used to verify the browser is actually under
|
41
|
+
control of the user who is running Iated.
|
42
|
+
|
43
|
+
%dt
|
44
|
+
Browser Token:
|
45
|
+
%dd
|
46
|
+
%input#token( name="token" type="text" )
|
47
|
+
%br
|
48
|
+
This is the unique token to be used by this one browser extension instance
|
49
|
+
to validate against Iated.
|
50
|
+
|
51
|
+
%dt
|
52
|
+
Edit Session ID (sid):
|
53
|
+
%dd
|
54
|
+
%input#sid( name="sid" type="text" )
|
55
|
+
%br
|
56
|
+
The session identifier for editing this particular textarea.
|
57
|
+
|
58
|
+
%dt
|
59
|
+
Change ID:
|
60
|
+
%dd
|
61
|
+
%input#change-id( name="change-id" type="text" )
|
62
|
+
%br
|
63
|
+
Every time the file changes on disk, the change id is incremented. It starts
|
64
|
+
at zero when the edit session is created and increments during the lifetime
|
65
|
+
of the session.
|
66
|
+
|
67
|
+
%div#content-requests
|
68
|
+
%h2 The requests
|
69
|
+
|
70
|
+
%ol
|
71
|
+
%li
|
72
|
+
%button#hello-btn GET /hello
|
73
|
+
%br
|
74
|
+
Ask for a secret to authenticate
|
75
|
+
%li
|
76
|
+
%button#secret-btn POST /hello (with secret)
|
77
|
+
%br
|
78
|
+
Send the secret to retrieve a token.
|
79
|
+
%li
|
80
|
+
%button#edit-btn POST /edit
|
81
|
+
%br
|
82
|
+
Request that the text be opened in an editor.
|
83
|
+
%li
|
84
|
+
%button#edit-prev-btn POST /edit
|
85
|
+
%br
|
86
|
+
Request the existing file be opened in an editor.
|
87
|
+
%li
|
88
|
+
%button#update-btn POST /edit/:sid/:change_id
|
89
|
+
%br
|
90
|
+
Check the status of the edit, retrieving changes as needed.
|
91
|
+
%li
|
92
|
+
%button#preferences-btn GET /preferences
|
93
|
+
%br
|
94
|
+
Open the preference window.
|
@@ -0,0 +1,36 @@
|
|
1
|
+
body {
|
2
|
+
font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
|
3
|
+
background-color: #def;
|
4
|
+
}
|
5
|
+
|
6
|
+
dd {
|
7
|
+
font-size: 90%;
|
8
|
+
}
|
9
|
+
|
10
|
+
#content-text {
|
11
|
+
margin: 0 1em;
|
12
|
+
float: left;
|
13
|
+
width: 30%;
|
14
|
+
#textarea-wrapper {
|
15
|
+
textarea {
|
16
|
+
position: fixed;
|
17
|
+
left: inherit;
|
18
|
+
top: inherit;
|
19
|
+
width: 28%;
|
20
|
+
height: 8em;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
#content-data {
|
25
|
+
margin: 0 1em;
|
26
|
+
float: left;
|
27
|
+
width: 30%;
|
28
|
+
input {
|
29
|
+
font-family: Menlo, monospace;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
#content-requests {
|
33
|
+
margin: 0 1em;
|
34
|
+
float: left;
|
35
|
+
width: 30%;
|
36
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pathname'
|
3
|
+
require 'tmpdir'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
describe Iated::BrowserTokenDB do
|
7
|
+
|
8
|
+
it "writes to a specified file" do
|
9
|
+
dir = Dir.mktmpdir
|
10
|
+
fname = Pathname.new(dir) + 'test.yml'
|
11
|
+
db = Iated::BrowserTokenDB.new fname
|
12
|
+
fname.exist?.should be_true
|
13
|
+
FileUtils.rm_rf dir
|
14
|
+
end
|
15
|
+
|
16
|
+
it "doesn't have tokens that don't exist" do
|
17
|
+
dir = Dir.mktmpdir
|
18
|
+
fname = Pathname.new(dir) + 'test.yml'
|
19
|
+
db = Iated::BrowserTokenDB.new fname
|
20
|
+
db.has_token?("random-token").should be_false
|
21
|
+
db.user_agent("random_token").should be_nil
|
22
|
+
FileUtils.rm_rf dir
|
23
|
+
end
|
24
|
+
|
25
|
+
it "stores tokens and reads them back" do
|
26
|
+
dir = Dir.mktmpdir
|
27
|
+
ua = "NCSA_Mosaic/2.7b5 (X11;Linux 2.6.7 i686) libwww/2.12"
|
28
|
+
fname = Pathname.new(dir) + 'test.yml'
|
29
|
+
db = Iated::BrowserTokenDB.new fname
|
30
|
+
tok = db.add ua
|
31
|
+
db.has_token?(tok).should be_true
|
32
|
+
db.user_agent(tok).should == ua
|
33
|
+
FileUtils.rm_rf dir
|
34
|
+
end
|
35
|
+
|
36
|
+
it "stores tokens and reads them back persistantly" do
|
37
|
+
dir = Dir.mktmpdir
|
38
|
+
ua = "NCSA_Mosaic/2.7b5 (X11;Linux 2.6.7 i686) libwww/2.12"
|
39
|
+
fname = Pathname.new(dir) + 'test.yml'
|
40
|
+
db = Iated::BrowserTokenDB.new fname
|
41
|
+
tok = db.add ua
|
42
|
+
db = nil
|
43
|
+
|
44
|
+
db2 = Iated::BrowserTokenDB.new fname
|
45
|
+
db2.has_token?(tok).should be_true
|
46
|
+
db2.user_agent(tok).should == ua
|
47
|
+
FileUtils.rm_rf dir
|
48
|
+
end
|
49
|
+
|
50
|
+
it "tokens are always strings" do
|
51
|
+
dir = Dir.mktmpdir
|
52
|
+
fname = Pathname.new(dir) + 'test.yml'
|
53
|
+
db = Iated::BrowserTokenDB.new fname
|
54
|
+
tok = db.add "user agent"
|
55
|
+
tok.should be_an_instance_of String
|
56
|
+
FileUtils.rm_rf dir
|
57
|
+
end
|
58
|
+
|
59
|
+
it "user agents are always strings" do
|
60
|
+
dir = Dir.mktmpdir
|
61
|
+
fname = Pathname.new(dir) + 'test.yml'
|
62
|
+
db = Iated::BrowserTokenDB.new fname
|
63
|
+
tok = db.add :user_agent
|
64
|
+
db.user_agent(tok).should be_an_instance_of String
|
65
|
+
FileUtils.rm_rf dir
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Iated::EditSession do
|
4
|
+
before(:each) do
|
5
|
+
Iated::reset
|
6
|
+
end
|
7
|
+
after(:each) do
|
8
|
+
Iated::purge
|
9
|
+
end
|
10
|
+
|
11
|
+
context "#find" do
|
12
|
+
|
13
|
+
it "should find existing by params" do
|
14
|
+
params = {:url => 'http://example.com/'}
|
15
|
+
Iated::EditSession.new(params).should == Iated::EditSession.find(params)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should find existing sessions by sid" do
|
19
|
+
params = {:url => 'http://example.com/'}
|
20
|
+
sess1 = Iated::EditSession.new(params)
|
21
|
+
sess1.should == Iated::EditSession.find(sess1.sid)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "#find_or_create" do
|
26
|
+
it "should find or create by exact match" do
|
27
|
+
tid = "fc-tid#{rand(1000)}"
|
28
|
+
|
29
|
+
sess1 = Iated::EditSession.find_or_create(:url => 'http://example.com/', :tid => tid)
|
30
|
+
sess2 = Iated::EditSession.find_or_create(:url => 'http://example.com/', :tid => tid)
|
31
|
+
|
32
|
+
sess1.should == sess2
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "#sid" do
|
37
|
+
it "should be 32 character hex string" do
|
38
|
+
tok = Iated::EditSession.calculate_sid :url => 'http://example.com/'
|
39
|
+
tok.should =~ /^[a-f0-9]{32}$/
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should be different for different extensions" do
|
43
|
+
tok1 = Iated::EditSession.calculate_sid :url => "http://example.com", :extension => '.xml'
|
44
|
+
tok2 = Iated::EditSession.calculate_sid :url => "http://example.com", :extension => '.txt'
|
45
|
+
tok1.should_not == tok2
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should be different for different urls" do
|
49
|
+
tok1 = Iated::EditSession.calculate_sid :url => "http://example.com/a"
|
50
|
+
tok2 = Iated::EditSession.calculate_sid :url => "http://example.com/b"
|
51
|
+
tok1.should_not == tok2
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should be different for different tids" do
|
55
|
+
tok1 = Iated::EditSession.calculate_sid :url => "http://example.com/", :tid => 'a'
|
56
|
+
tok2 = Iated::EditSession.calculate_sid :url => "http://example.com/", :tid => 'b'
|
57
|
+
tok1.should_not == tok2
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "#text" do
|
62
|
+
it "should save the text when assigned" do
|
63
|
+
text = "\t Random Number: #{rand}"
|
64
|
+
sess = Iated::EditSession.new :url => 'http://example.com/', :text => text
|
65
|
+
sess.filename.should be_exist
|
66
|
+
sess.filename.read.should == text
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should read back the text" do
|
70
|
+
text = "\t Random Number: #{rand}"
|
71
|
+
sess = Iated::EditSession.new :url => 'http://example.com/', :text => text
|
72
|
+
sess.text.should == text
|
73
|
+
end
|
74
|
+
|
75
|
+
it "shouldn't touch the change_id on first save" do
|
76
|
+
sess = Iated::EditSession.new :url => 'http://example.com/first_save_check', :tid => "#{rand}"
|
77
|
+
sess.text = "first-save-foo"
|
78
|
+
sess.change_id.should == 0
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should increment the change_id on subsequent saves" do
|
82
|
+
sess = Iated::EditSession.new :url => 'http://example.com/subseq_save_check', :tid => "#{rand}", :text => "sub-foo"
|
83
|
+
sess.text = "sub-bar"
|
84
|
+
sess.change_id.should == 1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should be writen to disk" do
|
88
|
+
sess = Iated::EditSession.new :url => 'http://example.com/writeme', :tid => "#{rand}", :text => "written"
|
89
|
+
sess.filename.open('r') do |f|
|
90
|
+
f.read.should == "written"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "#change_id" do
|
96
|
+
it "should be zero for new sessions" do
|
97
|
+
params = {}
|
98
|
+
params[:text] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
|
99
|
+
params[:url] = "http://example.com/cucumber"
|
100
|
+
session = Iated::EditSession.new params
|
101
|
+
session.change_id.should == 0
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should detect the change_id when the contents change" do
|
105
|
+
sess = Iated::EditSession.new :url => 'http://example.com/changer', :tid => "#{rand}", :text => "change me"
|
106
|
+
sess.change_id.should == 0
|
107
|
+
sess.filename.open('w') do |f|
|
108
|
+
f.write "I'm changed!"
|
109
|
+
end
|
110
|
+
sess.change_id.should == 1
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "#increment_change_id" do
|
115
|
+
it "should increment the #change_id" do
|
116
|
+
sess = Iated::EditSession.new :url => 'http://example.com/should_increment'
|
117
|
+
sess.change_id.should == 0
|
118
|
+
sess.increment_change_id
|
119
|
+
sess.change_id.should == 1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "param normalization" do
|
124
|
+
it "should handle text keys the same as symbol keys" do
|
125
|
+
[:url, :tid, :extension, :text].each do |key|
|
126
|
+
h1 = {}
|
127
|
+
h1[key] = 'stuff'
|
128
|
+
h2 = {}
|
129
|
+
h2[key.to_s] = 'stuff'
|
130
|
+
p1 = Iated::EditSession.normalize_keys h1
|
131
|
+
p2 = Iated::EditSession.normalize_keys h2
|
132
|
+
p1.should == p2
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should not destroy the original" do
|
137
|
+
original = {
|
138
|
+
:url => "http://example.com/",
|
139
|
+
:tid => "some-tid",
|
140
|
+
}
|
141
|
+
normalized = Iated::EditSession.normalize_keys original
|
142
|
+
original.should_not be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should strip :text being passed in" do
|
146
|
+
original = {
|
147
|
+
:url => "http://example.com/",
|
148
|
+
:text => "some text",
|
149
|
+
:tid => "some-tid",
|
150
|
+
}
|
151
|
+
normalized = Iated::EditSession.normalize_keys original
|
152
|
+
normalized[:url].should == original[:url]
|
153
|
+
normalized[:tid].should == original[:tid]
|
154
|
+
normalized[:text].should be_nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|