goat 0.2.12 → 0.3.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.
- data/README.md +45 -0
- data/bin/channel-srv +150 -0
- data/bin/press +29 -0
- data/bin/state-srv +390 -0
- data/bin/sync +53 -0
- data/bin/yodel +1 -1
- data/goat.gemspec +37 -0
- data/lib/goat.rb +774 -618
- data/lib/goat/common.rb +53 -0
- data/lib/goat/dynamic.rb +91 -0
- data/lib/goat/extn.rb +94 -5
- data/lib/goat/goat.js +348 -119
- data/lib/goat/html.rb +221 -103
- data/lib/goat/js/component.js +18 -15
- data/lib/goat/net-common.rb +38 -0
- data/lib/goat/notifications.rb +51 -46
- data/lib/goat/state-srv.rb +119 -0
- data/lib/goat/yodel.rb +3 -2
- data/lib/views/plain_layout.erb +7 -3
- metadata +18 -10
- data/lib/goat/logger.rb +0 -39
- data/lib/goat/sinatra.rb +0 -11
data/lib/goat/common.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Goat
|
2
|
+
class ComponentSkeleton
|
3
|
+
attr_reader :pgid, :id, :spec
|
4
|
+
attr_accessor :dom, :state
|
5
|
+
|
6
|
+
def cls; @class; end
|
7
|
+
|
8
|
+
def initialize(pgid, cls, id, spec, state, dom)
|
9
|
+
@pgid = pgid
|
10
|
+
@class = cls
|
11
|
+
@id = id
|
12
|
+
@state = state
|
13
|
+
@dom = dom
|
14
|
+
@spec = spec
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_hash(h)
|
18
|
+
self.new(h['pgid'], h['class'], h['id'], h['spec'], h['state'], Marshal.load(h['dom']))
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_hash
|
22
|
+
{
|
23
|
+
'pgid' => @pgid,
|
24
|
+
'class' => @class,
|
25
|
+
'id' => @id,
|
26
|
+
'spec' => @spec,
|
27
|
+
'state' => @state,
|
28
|
+
'dom' => Marshal.dump(@dom),
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class ComponentUpdate
|
34
|
+
attr_reader :skel, :mutations
|
35
|
+
attr_accessor :version
|
36
|
+
|
37
|
+
def initialize(skel, mutations)
|
38
|
+
@skel = skel
|
39
|
+
@mutations = mutations
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_hash
|
43
|
+
{
|
44
|
+
'skel' => @skel.to_hash,
|
45
|
+
'mutations' => @mutations
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.from_hash(h)
|
50
|
+
self.new(ComponentSkeleton.from_hash(h['skel']), h['mutations'])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/goat/dynamic.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# This library was created by Christian Neukirchen in the context of
|
2
|
+
# EuRuKo 2005 and is licensed under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# It provides dynamically scoped variables. It is used within ContextR to
|
5
|
+
# store the current, thread-wide activated layers.
|
6
|
+
#
|
7
|
+
# For more information see the corresponding slides at
|
8
|
+
# http://chneukirchen.org/talks/euruko-2005/chneukirchen-euruko2005-contextr.pdf
|
9
|
+
#
|
10
|
+
# (c) 2005 - Christian Neukirchen - http://chneukirchen.org
|
11
|
+
module Dynamic
|
12
|
+
module ClassMethods #:nodoc:
|
13
|
+
Thread.main[:DYNAMIC] = Hash.new { |hash, key|
|
14
|
+
raise NameError, "no such dynamic variable: #{key}"
|
15
|
+
}
|
16
|
+
|
17
|
+
def here!
|
18
|
+
Thread.current[:DYNAMIC] = Hash.new { |hash, key|
|
19
|
+
raise NameError, "no such dynamic variable: #{key}"
|
20
|
+
}.update Thread.main[:DYNAMIC]
|
21
|
+
end
|
22
|
+
|
23
|
+
def variable?(v)
|
24
|
+
variables.include?(v)
|
25
|
+
end
|
26
|
+
|
27
|
+
def variables
|
28
|
+
Thread.current[:DYNAMIC] or here!
|
29
|
+
end
|
30
|
+
|
31
|
+
def variable(definition)
|
32
|
+
case definition
|
33
|
+
when Symbol
|
34
|
+
#if variables.has_key? definition
|
35
|
+
# raise NameError, "dynamic variable `#{definition}' already exists"
|
36
|
+
#end
|
37
|
+
variables[definition] = nil
|
38
|
+
when Hash
|
39
|
+
definition.each { |key, value|
|
40
|
+
if variables.has_key? key
|
41
|
+
raise NameError, "dynamic variable `#{key}' already exists"
|
42
|
+
end
|
43
|
+
variables[key] = value
|
44
|
+
}
|
45
|
+
else
|
46
|
+
raise ArgumentError,
|
47
|
+
"can't create a new dynamic variable from #{definition.class}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def [](key)
|
52
|
+
variables[key]
|
53
|
+
end
|
54
|
+
|
55
|
+
def []=(key, value)
|
56
|
+
variables[key] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
def undefine(*keys)
|
60
|
+
keys.each { |key|
|
61
|
+
self[key]
|
62
|
+
variables.delete key
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def let(bindings, &block)
|
67
|
+
save = {}
|
68
|
+
save_keys = variables.keys
|
69
|
+
bindings.to_hash.collect { |key, value|
|
70
|
+
self.variable(key)
|
71
|
+
save[key] = self[key]
|
72
|
+
self[key] = value
|
73
|
+
}
|
74
|
+
block.call
|
75
|
+
ensure
|
76
|
+
variables.update save
|
77
|
+
undefine(*(variables.keys - save_keys))
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_missing(name, *args)
|
81
|
+
if match = /=\Z/.match(name.to_s) # setter?
|
82
|
+
raise ArgumentError, "invalid setter call" unless args.size == 1
|
83
|
+
self[match.pre_match.intern] = args.first
|
84
|
+
else
|
85
|
+
raise ArgumentError, "invalid getter call" unless args.empty?
|
86
|
+
self[name]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
extend ClassMethods
|
91
|
+
end
|
data/lib/goat/extn.rb
CHANGED
@@ -16,11 +16,100 @@ class String
|
|
16
16
|
unless self.instance_methods.include?('random')
|
17
17
|
def self.random(len=10, opts={})
|
18
18
|
opts = {:alpha => false}.merge(opts)
|
19
|
-
chars = ["a".."z", "A".."Z"]
|
20
|
-
chars << ("0".."9") unless opts[:alpha]
|
21
19
|
|
22
|
-
|
23
|
-
|
20
|
+
regexp = opts[:alpha] ? /[^A-Za-z]/ : /[^A-Za-z0-9]/
|
21
|
+
|
22
|
+
str = SecureRandom.random_bytes(len * 8).gsub(regexp, '')
|
23
|
+
if str.size >= len
|
24
|
+
str[0..(len-1)]
|
25
|
+
else
|
26
|
+
# should basically never happen
|
27
|
+
random(len, opts)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Object
|
34
|
+
def self.superclasses(upto=Object)
|
35
|
+
cs = []
|
36
|
+
c = self
|
37
|
+
start = self
|
38
|
+
|
39
|
+
while c
|
40
|
+
cs << c
|
41
|
+
return cs if c == upto
|
42
|
+
raise "#{upto} isn't in the hierachy of #{start.inspect}" if c == Object && upto != Object
|
43
|
+
|
44
|
+
c = c.superclass
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def subclass_of?(cls)
|
49
|
+
self == cls || \
|
50
|
+
(self == Object ? \
|
51
|
+
false : \
|
52
|
+
(self.superclass && self.superclass != self && self.superclass.subclass_of?(cls)))
|
53
|
+
end
|
54
|
+
|
55
|
+
def glimpse(n=100)
|
56
|
+
ins = self.inspect
|
57
|
+
if ins =~ />$/ && ins.size > n
|
58
|
+
"#{ins[0..n]}...>"
|
59
|
+
else
|
60
|
+
ins
|
24
61
|
end
|
25
62
|
end
|
26
|
-
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module Kernel
|
66
|
+
# from http://redcorundum.blogspot.com/2006/05/kernelqualifiedconstget.html
|
67
|
+
def fetch_class(str)
|
68
|
+
path = str.to_s.split('::')
|
69
|
+
from_root = path[0].empty?
|
70
|
+
if from_root
|
71
|
+
from_root = []
|
72
|
+
path = path[1..-1]
|
73
|
+
else
|
74
|
+
start_ns = ((Class === self)||(Module === self)) ? self : self.class
|
75
|
+
from_root = start_ns.to_s.split('::')
|
76
|
+
end
|
77
|
+
until from_root.empty?
|
78
|
+
begin
|
79
|
+
return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
|
80
|
+
rescue NameError
|
81
|
+
from_root.delete_at(-1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
path.inject(Object) { |ns,name| ns.const_get(name) }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Set
|
89
|
+
def glimpse(n=100)
|
90
|
+
"#<Set: #{self.map{|x| x.glimpse(n)}.join(', ')}>"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Array
|
95
|
+
def glimpse(n=100)
|
96
|
+
"[" + self.map{|x| x.glimpse(n)}.join(', ') + "]"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Hash
|
101
|
+
def glimpse(n=100)
|
102
|
+
"{" + self.map{|k, v| k.glimpse + "=>" + v.glimpse}.join(', ') + "}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Hash
|
107
|
+
def map_to_hash
|
108
|
+
h = {}
|
109
|
+
self.map do |k, v|
|
110
|
+
nk, nv = yield(k, v)
|
111
|
+
h[nk] = nv
|
112
|
+
end
|
113
|
+
h
|
114
|
+
end
|
115
|
+
end
|
data/lib/goat/goat.js
CHANGED
@@ -1,6 +1,322 @@
|
|
1
|
+
(function(){
|
2
|
+
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
3
|
+
// The base Class implementation (does nothing)
|
4
|
+
this.Class = function(){};
|
5
|
+
|
6
|
+
// Create a new Class that inherits from this class
|
7
|
+
Class.extend = function(prop) {
|
8
|
+
var _super = this.prototype;
|
9
|
+
|
10
|
+
// Instantiate a base class (but only create the instance,
|
11
|
+
// don't run the init constructor)
|
12
|
+
initializing = true;
|
13
|
+
var prototype = new this();
|
14
|
+
initializing = false;
|
15
|
+
|
16
|
+
// Copy the properties over onto the new prototype
|
17
|
+
for (var name in prop) {
|
18
|
+
// Check if we're overwriting an existing function
|
19
|
+
prototype[name] = typeof prop[name] == "function" &&
|
20
|
+
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
|
21
|
+
(function(name, fn){
|
22
|
+
return function() {
|
23
|
+
var tmp = this._super;
|
24
|
+
|
25
|
+
// Add a new ._super() method that is the same method
|
26
|
+
// but on the super-class
|
27
|
+
this._super = _super[name];
|
28
|
+
|
29
|
+
// The method only need to be bound temporarily, so we
|
30
|
+
// remove it when we're done executing
|
31
|
+
var ret = fn.apply(this, arguments);
|
32
|
+
this._super = tmp;
|
33
|
+
|
34
|
+
return ret;
|
35
|
+
};
|
36
|
+
})(name, prop[name]) :
|
37
|
+
prop[name];
|
38
|
+
}
|
39
|
+
|
40
|
+
// The dummy class constructor
|
41
|
+
function Class() {
|
42
|
+
// All construction is actually done in the init method
|
43
|
+
if ( !initializing && this.init )
|
44
|
+
this.init.apply(this, arguments);
|
45
|
+
}
|
46
|
+
|
47
|
+
// Populate our constructed prototype object
|
48
|
+
Class.prototype = prototype;
|
49
|
+
|
50
|
+
// Enforce the constructor to be what we expect
|
51
|
+
Class.constructor = Class;
|
52
|
+
|
53
|
+
// And make this class extendable
|
54
|
+
Class.extend = arguments.callee;
|
55
|
+
|
56
|
+
return Class;
|
57
|
+
};
|
58
|
+
})();
|
59
|
+
|
60
|
+
String.random = function string_random(len, pref) {
|
61
|
+
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
62
|
+
var str = '';
|
63
|
+
for (var i=0; i < len; i++) {
|
64
|
+
var r = Math.floor(Math.random() * chars.length);
|
65
|
+
str += chars.substring(r, r+1);
|
66
|
+
}
|
67
|
+
return pref ? (pref + '_' + str) : str;
|
68
|
+
}
|
69
|
+
|
70
|
+
$(document).ready(function() {
|
71
|
+
Goat.loadComponents();
|
72
|
+
setTimeout(function() { if(Goat.page_id) Goat.openChannel(); }, 1000);
|
73
|
+
});
|
74
|
+
|
75
|
+
var GoatRT = {
|
76
|
+
version: 0,
|
77
|
+
pendingTxns: {},
|
78
|
+
ops: {}
|
79
|
+
};
|
80
|
+
|
81
|
+
(function(rt) {
|
82
|
+
function updateFailed(msg) {
|
83
|
+
console.error(msg);
|
84
|
+
}
|
85
|
+
|
86
|
+
function node(par, pos) {
|
87
|
+
if(pos === null)
|
88
|
+
return $('#' + par);
|
89
|
+
else
|
90
|
+
return $("#" + par + " > :nth-child(" + (pos + 1) + ")");
|
91
|
+
}
|
92
|
+
|
93
|
+
function removeNode(par, pos, html) {
|
94
|
+
node(par, pos).remove();
|
95
|
+
}
|
96
|
+
|
97
|
+
function addNode(par, pos, html) {
|
98
|
+
node(par, pos).before(html);
|
99
|
+
}
|
100
|
+
|
101
|
+
function replaceNode(par, pos, html) {
|
102
|
+
console.log("rep " + par + " " + pos);
|
103
|
+
console.log(node(par, pos));
|
104
|
+
node(par, pos).html(html);
|
105
|
+
if(pos === null)
|
106
|
+
delete Goat.components[par];
|
107
|
+
}
|
108
|
+
|
109
|
+
function applyUpdate(up) {
|
110
|
+
var t = up.type;
|
111
|
+
var par = up.parent;
|
112
|
+
var pos = up.position;
|
113
|
+
var html = up.html;
|
114
|
+
var js = up.js;
|
115
|
+
var css = up.css;
|
116
|
+
|
117
|
+
if(t == "rem") {
|
118
|
+
removeNode(par, pos, html);
|
119
|
+
} else if(t == "add") {
|
120
|
+
addNode(par, pos, html);
|
121
|
+
} else if(t == "rep") {
|
122
|
+
replaceNode(par, pos, html);
|
123
|
+
}
|
124
|
+
|
125
|
+
addCSS(css);
|
126
|
+
addJS(js);
|
127
|
+
}
|
128
|
+
|
129
|
+
function addToHead(elt) {
|
130
|
+
var head = document.getElementsByTagName('head')[0];
|
131
|
+
head.appendChild(elt);
|
132
|
+
}
|
133
|
+
|
134
|
+
function addCSS(css) {
|
135
|
+
var style = document.createElement('style');
|
136
|
+
style.innerHTML = css;
|
137
|
+
addToHead(style);
|
138
|
+
}
|
139
|
+
|
140
|
+
function addJS(js) {
|
141
|
+
var script = document.createElement('script');
|
142
|
+
script.innerHTML = js;
|
143
|
+
addToHead(script);
|
144
|
+
Goat.loadComponents();
|
145
|
+
/*console.log("Eval " + js);
|
146
|
+
eval(js);*/
|
147
|
+
}
|
148
|
+
|
149
|
+
function updateReceived(m) {
|
150
|
+
var id = m.id;
|
151
|
+
var version = m.version;
|
152
|
+
var updates = m.updates;
|
153
|
+
|
154
|
+
if(m.version == rt.version + 1) {
|
155
|
+
$(updates).each(function(_,u) {
|
156
|
+
applyUpdate(u);
|
157
|
+
});
|
158
|
+
rt.version++;
|
159
|
+
} else if(m.version <= rt.version) {
|
160
|
+
// fine, we already have this
|
161
|
+
} else {
|
162
|
+
updateFailed("Tried to apply update " + version + ", but I'm only at version " + rt.version);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
function showSpinner() {
|
167
|
+
$("#menu").prepend(GoatRT.spinner);
|
168
|
+
}
|
169
|
+
|
170
|
+
function hideSpinner() {
|
171
|
+
$("#spinner").remove();
|
172
|
+
}
|
173
|
+
|
174
|
+
function newTxn() {
|
175
|
+
var op = new RTTxn();
|
176
|
+
rt.ops[op.code] = op;
|
177
|
+
return op;
|
178
|
+
}
|
179
|
+
|
180
|
+
function beginTxn(txn) {
|
181
|
+
rt.pendingTxns[txn.code] = true;
|
182
|
+
}
|
183
|
+
|
184
|
+
function pendingCount() {
|
185
|
+
var pending = 0;
|
186
|
+
for(var k in rt.pendingTxns) pending++;
|
187
|
+
return pending;
|
188
|
+
}
|
189
|
+
|
190
|
+
function completeTxn(txn) {
|
191
|
+
if(rt.pendingTxns[txn]) {
|
192
|
+
delete rt.pendingTxns[txn];
|
193
|
+
|
194
|
+
if(pendingCount() == 0) {
|
195
|
+
if(!txn.start) hideSpinner();
|
196
|
+
if(txn.complete) txn.complete();
|
197
|
+
}
|
198
|
+
} else {
|
199
|
+
console.error("Can't complete txn " + txn + ": no such txn was started");
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
function txnCompleteMsg(msg) {
|
204
|
+
console.log('txnCompleteMsg');
|
205
|
+
console.log(msg);
|
206
|
+
completeTxn(msg['txn']);
|
207
|
+
}
|
208
|
+
|
209
|
+
rt.updateReceived = updateReceived;
|
210
|
+
rt.node = node;
|
211
|
+
rt.newTxn = newTxn;
|
212
|
+
rt.beginTxn = beginTxn;
|
213
|
+
rt.txnCompleteMsg = txnCompleteMsg;
|
214
|
+
rt.spinner = $("<img id=\"spinner\" style=\"position: relative; float: left; top: 50px; left: 20px;\" src=\"/static/img/spinner.gif\">");
|
215
|
+
rt.showSpinner = showSpinner;
|
216
|
+
|
217
|
+
return rt;
|
218
|
+
})(GoatRT);
|
219
|
+
|
220
|
+
var RTTxn = Class.extend({
|
221
|
+
init: function() {
|
222
|
+
this.code = String.random(8, 'txn');
|
223
|
+
},
|
224
|
+
|
225
|
+
onStart: function(s) {
|
226
|
+
this.start = s;
|
227
|
+
},
|
228
|
+
|
229
|
+
onComplete: function(c) {
|
230
|
+
this.complete = c;
|
231
|
+
}
|
232
|
+
});
|
233
|
+
|
234
|
+
var GoatRPC = Class.extend({
|
235
|
+
init: function(c, n) {
|
236
|
+
this.component = c;
|
237
|
+
this.name = n;
|
238
|
+
},
|
239
|
+
|
240
|
+
args: function(a) {
|
241
|
+
this.args = a;
|
242
|
+
return this;
|
243
|
+
},
|
244
|
+
|
245
|
+
data: function(d) {
|
246
|
+
this.data = d;
|
247
|
+
return this;
|
248
|
+
},
|
249
|
+
|
250
|
+
onRPCComplete: function(s) {
|
251
|
+
this.rpcComplete = s;
|
252
|
+
return this;
|
253
|
+
},
|
254
|
+
|
255
|
+
onRPCError: function(f) {
|
256
|
+
this.rpcError = f;
|
257
|
+
return this;
|
258
|
+
},
|
259
|
+
|
260
|
+
rpcSuccess: function(data) {
|
261
|
+
if(this.rpcComplete) this.rpcComplete(data, this);
|
262
|
+
},
|
263
|
+
|
264
|
+
rpcFailure: function() {
|
265
|
+
if(this.rpcError) this.rpcError(this);
|
266
|
+
},
|
267
|
+
|
268
|
+
onStart: function(start) {
|
269
|
+
this.start = start;
|
270
|
+
return this;
|
271
|
+
},
|
272
|
+
|
273
|
+
onUpdateComplete: function(c) {
|
274
|
+
this.updateComplete = c;
|
275
|
+
return this;
|
276
|
+
},
|
277
|
+
|
278
|
+
send: function() {
|
279
|
+
var data = this.data;
|
280
|
+
|
281
|
+
data['cls'] = this.component.cls;
|
282
|
+
data['id'] = this.component.id;
|
283
|
+
data['rpc'] = this.name;
|
284
|
+
data['pgid'] = Goat.page_id;
|
285
|
+
data['args'] = JSON.stringify(this.args);
|
286
|
+
|
287
|
+
if(this.rt) {
|
288
|
+
var txn = GoatRT.newTxn();
|
289
|
+
data['rttxn'] = txn.code;
|
290
|
+
txn.onComplete(this.updateComplete);
|
291
|
+
GoatRT.beginTxn(txn);
|
292
|
+
}
|
293
|
+
|
294
|
+
if(this.start) this.start()
|
295
|
+
else if(this.rt) GoatRT.showSpinner();
|
296
|
+
|
297
|
+
$.ajax({
|
298
|
+
type: 'POST',
|
299
|
+
url: '/rpc',
|
300
|
+
data: data,
|
301
|
+
success: closure(this, this.rpcSuccess),
|
302
|
+
error: closure(this, this.rpcFailure),
|
303
|
+
dataType: 'script',
|
304
|
+
async: true
|
305
|
+
});
|
306
|
+
|
307
|
+
return this;
|
308
|
+
},
|
309
|
+
|
310
|
+
rtUpdate: function() {
|
311
|
+
this.rt = true;
|
312
|
+
return this;
|
313
|
+
}
|
314
|
+
});
|
315
|
+
|
1
316
|
var Goat = {
|
2
317
|
channelOpenFails: 0,
|
3
318
|
activeChannel: null,
|
319
|
+
pageDead: false,
|
4
320
|
components: {},
|
5
321
|
page_id: null
|
6
322
|
}
|
@@ -14,33 +330,15 @@ function closure(target, fn) {
|
|
14
330
|
$.extend(Goat, {
|
15
331
|
closure: function(fn) { return closure(this, fn); },
|
16
332
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
var after = m['after'];
|
23
|
-
var html = m['html'];
|
24
|
-
|
25
|
-
if(after) {
|
26
|
-
var anode = this.node(after);
|
27
|
-
if(anode.size() == 0)
|
28
|
-
console.error("Couldn't find node " + after + " to insert after");
|
29
|
-
else
|
30
|
-
anode.after(html);
|
31
|
-
} else {
|
32
|
-
$(document).after(html);
|
33
|
-
}
|
333
|
+
channelURL: function() {
|
334
|
+
if(window.location.host.match(/127\./))
|
335
|
+
return 'http://127.0.0.1:8050/channel';
|
336
|
+
else
|
337
|
+
return '/channel';
|
34
338
|
},
|
35
339
|
|
36
|
-
|
37
|
-
|
38
|
-
var html = m['html'];
|
39
|
-
var rm = this.node(id);
|
40
|
-
if(rm.size() == 0)
|
41
|
-
console.error("Couldn't find node " + id + " to remove");
|
42
|
-
else
|
43
|
-
rm.remove();
|
340
|
+
node: function(str) {
|
341
|
+
return $('#' + str);
|
44
342
|
},
|
45
343
|
|
46
344
|
showAlert: function(m) {
|
@@ -49,19 +347,20 @@ $.extend(Goat, {
|
|
49
347
|
},
|
50
348
|
|
51
349
|
messageReceived: function(m) {
|
350
|
+
console.log(m);
|
351
|
+
|
52
352
|
var t = m['type'];
|
53
|
-
if(t == '
|
54
|
-
|
55
|
-
var html = m['html'];
|
56
|
-
this.node(id).html(html);
|
353
|
+
if(t == 'component_updated') {
|
354
|
+
GoatRT.updateReceived(m);
|
57
355
|
} else if(t == 'redirect') {
|
58
356
|
console.log("Redirecting to " + m['location']);
|
59
357
|
sleep(2);
|
60
358
|
window.location = m['location'];
|
61
|
-
} else if
|
62
|
-
this.
|
63
|
-
|
64
|
-
|
359
|
+
} else if(t == 'page_expired') {
|
360
|
+
alert("Due to inactivity, you'll need to refresh this page.");
|
361
|
+
Goat.pageDead = true;
|
362
|
+
} else if(t == 'txn_complete') {
|
363
|
+
GoatRT.txnCompleteMsg(m);
|
65
364
|
} else if(t == 'alert') {
|
66
365
|
this.showAlert(m);
|
67
366
|
} else {
|
@@ -78,9 +377,6 @@ $.extend(Goat, {
|
|
78
377
|
|
79
378
|
this.channelOpenFails = 0;
|
80
379
|
|
81
|
-
if(this.messageReceivedDelegate)
|
82
|
-
this.messageReceivedDelegate(data);
|
83
|
-
|
84
380
|
if(data['messages']) {
|
85
381
|
$(data['messages']).each(this.closure(function(i, m) { this.messageReceived(m) }));
|
86
382
|
} else {
|
@@ -91,11 +387,16 @@ $.extend(Goat, {
|
|
91
387
|
reopenChannel: function() {
|
92
388
|
console.log("reopenChannel()");
|
93
389
|
var fails = this.channelOpenFails;
|
94
|
-
setTimeout(function() { Goat.openChannel(); }, Math.min(30000, Math.pow(1.5, fails)));
|
390
|
+
setTimeout(function() { Goat.openChannel(); }, Math.min(30000, Math.max(1000, Math.pow(1.5, fails))));
|
95
391
|
this.channelOpenFails++;
|
96
392
|
},
|
97
393
|
|
98
394
|
openChannel: function() {
|
395
|
+
if(Goat.pageDead) {
|
396
|
+
console.log("not opening channel: page dead");
|
397
|
+
return;
|
398
|
+
}
|
399
|
+
|
99
400
|
console.log("opening channel");
|
100
401
|
|
101
402
|
if(this.activeChannel) {
|
@@ -122,8 +423,9 @@ $.extend(Goat, {
|
|
122
423
|
var logError = closure(this, function() {
|
123
424
|
console.error("Goat: error loading " + src);
|
124
425
|
});
|
125
|
-
|
126
|
-
var
|
426
|
+
|
427
|
+
var base = Goat.channelURL();
|
428
|
+
var src = base + '?_id=' + Goat.page_id + '&jsonp=' + jsonp + '&version=' + GoatRT.version;
|
127
429
|
|
128
430
|
scriptTag.addEventListener('load', function() { cleanup(); Goat.reopenChannel(); }, false);
|
129
431
|
scriptTag.addEventListener('error', function() { cleanup(); logError(); Goat.reopenChannel(); }, false);
|
@@ -157,88 +459,15 @@ $.extend(Goat, {
|
|
157
459
|
|
158
460
|
return false;
|
159
461
|
},
|
160
|
-
|
161
|
-
remoteDispatch: function(c, k) {
|
162
|
-
$.ajax({
|
163
|
-
url: '/dispatch',
|
164
|
-
async: true,
|
165
|
-
data: {_id: Goat.page_id, _component: c, _dispatch: k},
|
166
|
-
success: function(data) {},
|
167
|
-
error: function(req, stat, err) {
|
168
|
-
console.log("remote dispatch error");
|
169
|
-
console.log({req: req, status: stat, err: err});
|
170
|
-
}
|
171
|
-
});
|
172
|
-
|
173
|
-
return false;
|
174
|
-
},
|
175
462
|
|
176
463
|
wireComponent: function(id, obj) {
|
177
464
|
this.components[id] = obj;
|
178
|
-
}
|
179
|
-
});
|
180
|
-
|
181
|
-
(function(){
|
182
|
-
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
183
|
-
// The base Class implementation (does nothing)
|
184
|
-
this.Class = function(){};
|
185
|
-
|
186
|
-
// Create a new Class that inherits from this class
|
187
|
-
Class.extend = function(prop) {
|
188
|
-
var _super = this.prototype;
|
189
|
-
|
190
|
-
// Instantiate a base class (but only create the instance,
|
191
|
-
// don't run the init constructor)
|
192
|
-
initializing = true;
|
193
|
-
var prototype = new this();
|
194
|
-
initializing = false;
|
195
|
-
|
196
|
-
// Copy the properties over onto the new prototype
|
197
|
-
for (var name in prop) {
|
198
|
-
// Check if we're overwriting an existing function
|
199
|
-
prototype[name] = typeof prop[name] == "function" &&
|
200
|
-
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
|
201
|
-
(function(name, fn){
|
202
|
-
return function() {
|
203
|
-
var tmp = this._super;
|
204
|
-
|
205
|
-
// Add a new ._super() method that is the same method
|
206
|
-
// but on the super-class
|
207
|
-
this._super = _super[name];
|
208
|
-
|
209
|
-
// The method only need to be bound temporarily, so we
|
210
|
-
// remove it when we're done executing
|
211
|
-
var ret = fn.apply(this, arguments);
|
212
|
-
this._super = tmp;
|
213
|
-
|
214
|
-
return ret;
|
215
|
-
};
|
216
|
-
})(name, prop[name]) :
|
217
|
-
prop[name];
|
218
|
-
}
|
219
|
-
|
220
|
-
// The dummy class constructor
|
221
|
-
function Class() {
|
222
|
-
// All construction is actually done in the init method
|
223
|
-
if ( !initializing && this.init )
|
224
|
-
this.init.apply(this, arguments);
|
225
|
-
}
|
226
|
-
|
227
|
-
// Populate our constructed prototype object
|
228
|
-
Class.prototype = prototype;
|
229
|
-
|
230
|
-
// Enforce the constructor to be what we expect
|
231
|
-
Class.constructor = Class;
|
232
|
-
|
233
|
-
// And make this class extendable
|
234
|
-
Class.extend = arguments.callee;
|
235
|
-
|
236
|
-
return Class;
|
237
|
-
};
|
238
|
-
})();
|
465
|
+
},
|
239
466
|
|
240
|
-
|
241
|
-
|
242
|
-
|
467
|
+
loadComponents: function() {
|
468
|
+
$.each(Goat.components, function(_, c) {
|
469
|
+
if(!c.isLoaded())
|
470
|
+
c.load();
|
471
|
+
});
|
472
|
+
}
|
243
473
|
});
|
244
|
-
|