vines-services 0.1.2 → 0.1.3

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 CHANGED
@@ -11,7 +11,7 @@ defined as: platform is 'mac_os_x' and platform_version starts with '10.7'.
11
11
  As machines update to Lion, they will join this service automatically and may
12
12
  be managed as one group.
13
13
 
14
- Additional documentation can be found at www.getvines.com.
14
+ Additional documentation can be found at www.getvines.org.
15
15
 
16
16
  == Usage
17
17
 
@@ -21,15 +21,9 @@ Additional documentation can be found at www.getvines.com.
21
21
 
22
22
  == Dependencies
23
23
 
24
- * bcrypt-ruby >= 3.0.1
25
- * blather >= 0.5.8
26
- * citrus >= 2.4.0
27
- * eventmachine >= 0.12.10
28
- * sqlite3 >= 1.3.4
29
- * ruby >= 1.9.2
30
-
31
- == Ubuntu setup
32
- $ sudo apt-get install build-essential ruby1.9.1 ruby1.9.1-dev libxml2-dev libxslt-dev
24
+ Vines Services requires Ruby 1.9.2 or better. Instructions for installing the
25
+ needed OS packages, as well as Ruby itself, are available at
26
+ http://www.getvines.org/ruby.
33
27
 
34
28
  == Contact
35
29
 
data/Rakefile CHANGED
@@ -19,19 +19,19 @@ and permissions are managed via the bundled web application."
19
19
 
20
20
  s.authors = ["David Graham"]
21
21
  s.email = %w[david@negativecode.com]
22
- s.homepage = "http://www.getvines.com"
22
+ s.homepage = "http://www.getvines.org"
23
23
 
24
24
  s.test_files = FileList["test/**/*"]
25
25
  s.executables = %w[vines-services]
26
26
  s.require_path = "lib"
27
27
 
28
28
  s.add_dependency "bcrypt-ruby", "~> 3.0.1"
29
- s.add_dependency "blather", "~> 0.5.8"
29
+ s.add_dependency "blather", "~> 0.5.12"
30
30
  s.add_dependency "citrus", "~> 2.4.0"
31
31
  s.add_dependency "couchrest_model", "~> 1.1.2"
32
- s.add_dependency "em-http-request", "~> 0.3.0"
33
- s.add_dependency "sqlite3", "~> 1.3.4"
34
- s.add_dependency "vines", "~> 0.3"
32
+ s.add_dependency "em-http-request", "~> 1.0.1"
33
+ s.add_dependency "sqlite3", "~> 1.3.5"
34
+ s.add_dependency "vines", ">= 0.4.0"
35
35
 
36
36
  s.add_development_dependency "minitest"
37
37
  s.add_development_dependency "rake"
@@ -38,7 +38,7 @@ module Vines
38
38
  `vines init #{@domain}`
39
39
  FileUtils.mv(Dir.glob("#{@domain}/*"), '.')
40
40
  FileUtils.remove_dir(@domain)
41
- FileUtils.remove_dir('data/users')
41
+ FileUtils.remove_dir('data')
42
42
  web = File.expand_path("../../../../../web", __FILE__)
43
43
  FileUtils.cp_r(Dir.glob("#{web}/*"), 'web')
44
44
  update_server_config('conf/config.rb')
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Vines
4
4
  module Services
5
- VERSION = '0.1.2'
5
+ VERSION = '0.1.3'
6
6
  end
7
7
  end
@@ -84,12 +84,12 @@ class FilesPage
84
84
 
85
85
  new Button $('.file-icon', node).get(0), ICONS.page2,
86
86
  scale: 1.0
87
- translation: '-2 0'
87
+ translation: '-2,0'
88
88
  'stroke-width': 0.1
89
89
  opacity: 1.0
90
90
 
91
91
  new Button $('.add-label-button', node).get(0), ICONS.plus,
92
- translation: '-10 -10'
92
+ translation: '-20,-20'
93
93
  scale: 0.5
94
94
 
95
95
  $('form.file-form', node).submit => this.deleteFile node
@@ -112,7 +112,7 @@ class FilesPage
112
112
  item.attr 'data-name', label
113
113
 
114
114
  new Button $('.remove', item).get(0), ICONS.cross,
115
- translation: '-8 -8'
115
+ translation: '-16,-16'
116
116
  scale: 0.5
117
117
 
118
118
  $('.remove', item).click =>
@@ -332,7 +332,7 @@ class FilesPage
332
332
  node.attr 'data-file', file.name
333
333
 
334
334
  new Button $('.cancel', node).get(0), ICONS.cross,
335
- translation: '-8 -8'
335
+ translation: '-16,-16'
336
336
  scale: 0.5
337
337
  $('.cancel', node).click => this.cancel file
338
338
  node
@@ -92,29 +92,35 @@ class SetupPage
92
92
  this.drawUserBlankSlate()
93
93
  else
94
94
  this.drawSystemBlankSlate()
95
- false
96
95
 
97
96
  selectTask: (event) ->
98
97
  @selected = null
99
98
  $('#setup li').removeClass 'selected secondary'
100
99
  $(event.currentTarget).addClass 'selected secondary'
100
+ $('form.overlay').fadeOut 100, => @layout.resize()
101
101
  switch $(event.currentTarget).attr('id')
102
102
  when 'users-nav'
103
103
  $('#beta-header').text 'Users'
104
+ $('#remove-user-form h2').text 'Remove User'
105
+ $('#remove-user-msg').html "Select a user to delete."
106
+ $('#remove-user-form .buttons').hide()
104
107
  this.drawUsers()
105
108
  this.drawUserBlankSlate()
106
- if @api.user.permissions.users
107
- $('#beta-controls div').show()
108
- else
109
- $('#beta-controls div').hide()
109
+ this.toggleBetaControls @api.user.permissions.users
110
110
  when 'systems-nav'
111
111
  $('#beta-header').text 'Systems'
112
+ $('#remove-user-form h2').text 'Remove System'
113
+ $('#remove-user-msg').html "Select a system to delete."
114
+ $('#remove-user-form .buttons').hide()
112
115
  this.drawUsers()
113
116
  this.drawSystemBlankSlate()
114
- if @api.user.permissions.systems
115
- $('#beta-controls div').show()
116
- else
117
- $('#beta-controls div').hide()
117
+ this.toggleBetaControls @api.user.permissions.systems
118
+
119
+ toggleBetaControls: (show) ->
120
+ if show
121
+ $('#beta-controls div').show()
122
+ else
123
+ $('#beta-controls div').hide()
118
124
 
119
125
  toggleForm: (form, fn) ->
120
126
  form = $(form)
@@ -124,8 +130,9 @@ class SetupPage
124
130
  fn() if fn
125
131
  form.fadeIn 100
126
132
  else
127
- form.fadeOut 100, ->
133
+ form.fadeOut 100, =>
128
134
  form[0].reset()
135
+ @layout.resize()
129
136
  fn() if fn
130
137
 
131
138
  validateUser: ->
@@ -133,11 +140,10 @@ class SetupPage
133
140
  $('#password-error').empty()
134
141
  valid = true
135
142
 
136
- node = $.trim $('#user-name').val()
137
- password1 = $.trim $('#password1').val()
138
- password2 = $.trim $('#password2').val()
143
+ password1 = $('#password1').val().trim()
144
+ password2 = $('#password2').val().trim()
139
145
 
140
- if @selected # exisiting user
146
+ if @selected # existing user
141
147
  if password2.length > 0 && password2.length < 8
142
148
  $('#password-error').text 'Password must be at least 8 characters.'
143
149
  valid = false
@@ -149,6 +155,7 @@ class SetupPage
149
155
  valid = false
150
156
 
151
157
  else # new user
158
+ node = $('#user-name').val().trim()
152
159
  if node == ''
153
160
  $('#user-name-error').text 'User name is required.'
154
161
  valid = false
@@ -195,13 +202,15 @@ class SetupPage
195
202
  node = this.userNode result
196
203
  this.selectUser node
197
204
  else
198
- $('.text', node).text this.userName(result)
205
+ selected = (u for u in @users when u.jid == result.jid)[0]
206
+ selected.name = this.userName result
207
+ $('.text', node).text this.userName result
199
208
  false
200
209
 
201
210
  validateSystem: ->
202
211
  $('#user-name-error').empty()
203
212
  valid = true
204
- node = $.trim $('#user-name').val()
213
+ node = $('#user-name').val().trim()
205
214
  unless @selected # new user
206
215
  if node == ''
207
216
  $('#user-name-error').text 'Hostname is required.'
@@ -230,6 +239,7 @@ class SetupPage
230
239
  node = this.userNode result
231
240
  this.selectUser node
232
241
  else
242
+ @selected.name = result.name
233
243
  $('.text', node).text result.name
234
244
  false
235
245
 
@@ -337,7 +347,9 @@ class SetupPage
337
347
 
338
348
  $('#remove-user').click => this.toggleForm '#remove-user-form'
339
349
  $('#remove-user-cancel').click => this.toggleForm '#remove-user-form'
340
- $('#remove-user-form').submit => this.removeUser()
350
+ $('#remove-user-form').submit =>
351
+ this.removeUser()
352
+ false
341
353
 
342
354
  fn = =>
343
355
  @layout.resize()
@@ -1,2 +1,2 @@
1
- var Api,__bind=function(a,b){return function(){return a.apply(b,arguments)}};Api=function(){function b(b){this.session=b,this.user=null,this.session.onRoster(__bind(function(){return this.get(a,{jid:this.session.bareJid()},__bind(function(a){return this.user=a},this))},this))}var a;return a="http://getvines.com/protocol/users",b.prototype.jid=function(){return"vines."+this.session.bareJid().split("@")[1]},b.prototype.get=function(a,b,c){var d,e,f;e=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="get">\n <query xmlns="'+a+'"/>\n</iq>');for(d in b)f=b[d],$("query",e).attr(d,f);return this.session.sendIQ(e,__bind(function(a){var b;b=$(a).attr("type")==="result";if(!b)return;return c(JSON.parse($("query",a).text()))},this))},b.prototype.get2=function(a,b,c){var d;return d=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="get">\n <query xmlns="'+a+'"/>\n</iq>'),$("query",d).text(b),this.session.sendIQ(d,__bind(function(a){var b;b=$(a).attr("type")==="result";if(!b)return;return c(JSON.parse($("query",a).text()))},this))},b.prototype.remove=function(a,b,c){var d;return d=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="set">\n <query xmlns="'+a+'" action="delete" id=""/>\n</iq>'),$("query",d).attr("id",b),this.session.sendIQ(d,c)},b.prototype.save=function(a,b,c){var d;return d=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="set">\n <query xmlns="'+a+'"/>\n</iq>'),$("query",d).text(JSON.stringify(b)),this.session.sendIQ(d,__bind(function(a){var b;b=$(a).attr("type")==="result";if(!b)return;return c(JSON.parse($("query",a).text()))},this))},b}();var Commands;Commands=function(){function a(){this.buf=[],this.index=0}return a.prototype.prev=function(){var a;return a=this.buf[--this.index],a||(this.index=-1),a||""},a.prototype.next=function(){var a;return a=this.buf[++this.index],a||(this.index=this.buf.length),a||""},a.prototype.push=function(a){return this.buf.push(a),this.index=this.buf.length},a}();var SystemsPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};SystemsPage=function(){function a(a){this.session=a,this.session.onRoster(__bind(function(){return this.roster()},this)),this.session.onMessage(__bind(function(a){return this.message(a)},this)),this.session.onPresence(__bind(function(a){return this.presence(a)},this)),this.commands=new Commands,this.chats={},this.currentContact=null,this.layout=null}return a.prototype.datef=function(a){var b,c,d,e;return b=new Date(a),d=b.getHours()<12?" am":" pm",c=b.getHours()>12?b.getHours()-12:b.getHours(),c===0&&(c=12),e=b.getMinutes()+"",e.length===1&&(e="0"+e),c+":"+e+d},a.prototype.groupContacts=function(){var a,b,c,d,e,f,g,h;c={},g=this.session.roster;for(d in g){a=g[d],h=a.groups;for(e=0,f=h.length;e<f;e++)b=h[e],(c[b]||(c[b]=[])).push(a)}return c},a.prototype.roster=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n;d=this.groupContacts(),k=function(){var a;a=[];for(c in d)b=d[c],a.push(c);return a}(),k=k.sort(function(a,b){return a=a.toLowerCase(),b=b.toLowerCase(),a>b?1:a<b?-1:0}),f=$("#roster-items").empty(),n=[];for(l=0,m=k.length;l<m;l++)c=k[l],b=d[c],h=$('<li class="group"></li>').appendTo(f),h.text(c),h.attr("data-group",c),n.push(function(){var d,h,k;k=[];for(d=0,h=b.length;d<h;d++)a=b[d],i=$('<li data-jid="'+a.jid+'">\n <span class="icon"></span>\n <span class="text"></span>\n <span class="unread" style="display:none;"></span>\n</li>').appendTo(f),a.offline()&&i.addClass("offline"),i.click(__bind(function(a){return this.selectContact(a)},this)),g=a.name||a.jid.split("@")[0],i.attr("data-name",g),i.attr("data-group",c),$(".text",i).text(g),j={fill:"#fff",stroke:"#404040","stroke-width":.3,opacity:1,scale:.65},e=function(){switch(c){case"People":return ICONS.man;case"Services":return ICONS.magic;default:return ICONS.run}}(),k.push(new Button($(".icon",i),e,j));return k}.call(this));return n},a.prototype.message=function(a){var b,c,d,e;this.queueMessage(a),e=a.from===this.session.jid(),d=a.from.split("/")[0];if(!e&&d!==this.currentContact)return c=this.chat(a.from),c.unread++,this.eachContact(d,function(a){return $(".unread",a).text(c.unread).show()});b=this.atBottom(),this.appendMessage(a);if(b||e)return this.scroll({animate:!0})},a.prototype.eachContact=function(a,b){var c,d,e,f,g;f=$("#roster-items li[data-jid='"+a+"']").get(),g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(b($(c)));return g},a.prototype.appendMessage=function(a){var b,c,d,e,f,g,h;d=a.from===this.session.jid(),h=$("jid",a.node).text(),c=(h||a.from).split("/")[0],b=this.session.roster[c],e=b?b.name||c:c,f=$('<li data-jid="'+c+'"><pre></pre></li>').appendTo("#messages"),g=d?"$ ":"",$("pre",f).text(g+a.text),$("#message-form").css("top","0px");if(!d)return f.append('<footer>\n <span class="author"></span> @\n <span class="time">'+this.datef(a.received)+"</span>\n</footer>"),$(".author",f).text(e)},a.prototype.queueMessage=function(a){var b,c,d;return d=a.from===this.session.jid(),c=a[d?"to":"from"],b=this.chat(c),b.jid=c,b.messages.push(a)},a.prototype.chat=function(a){var b,c;return b=a.split("/")[0],c=this.chats[b],c||(c={jid:a,messages:[],unread:0},this.chats[b]=c),c},a.prototype.presence=function(a){var b,c;c=a.from.split("/")[0];if(c===this.session.bareJid())return;if(!a.type||a.offline)b=this.session.roster[c],this.eachContact(c,function(a){return b.offline()?a.addClass("offline"):a.removeClass("offline")});if(a.offline)return this.chat(c).jid=c},a.prototype.selectContact=function(a){var b,c,d;$("#blank-slate").fadeOut(200,function(){return $(this).remove()}),$("#roster").hide(),$("#message").focus(),d=$(a.currentTarget),c=d.attr("data-jid"),b=this.session.roster[c];if(this.currentContact===c)return;return this.currentContact=c,$("#message-label").text($(".text",d).text()),$("#messages").empty(),$("#message-form").css("top","10px"),this.layout.resize(),this.restoreChat(c)},a.prototype.restoreChat=function(a){var b,c,d,e,f;b=this.chats[a],c=[],b&&(c=b.messages,b.unread=0,this.eachContact(a,function(a){return $(".unread",a).text("").hide()}));for(e=0,f=c.length;e<f;e++)d=c[e],this.appendMessage(d);return this.scroll()},a.prototype.scroll=function(a){var b;return a||(a={}),b=$("#messages"),a.animate?b.animate({scrollTop:b.prop("scrollHeight")},400):b.scrollTop(b.prop("scrollHeight"))},a.prototype.atBottom=function(){var a,b;return b=$("#messages"),a=b.prop("scrollHeight")-b.outerHeight(),b.scrollTop()>=a},a.prototype.send=function(){var a,b,c,d;return this.currentContact?(b=$("#message"),d=b.val().trim(),d&&(a=this.chats[this.currentContact],c=a?a.jid:this.currentContact,this.message({from:this.session.jid(),text:d,to:c,received:new Date}),this.session.sendMessage(c,d),this.commands.push(d)),b.val(""),!1):!1},a.prototype.showRoster=function(){var a,b,c,d,e,f,g;return a=$("#container"),b=$("#message-form"),f=$("#roster"),d=$("#roster-items"),e=$("#roster-form"),g=a.height()-b.position().top<a.height()/2,g?(f.css("top",""),f.css("bottom",b.outerHeight()+5+"px"),c=a.height()-b.outerHeight()-20):(f.css("bottom",""),f.css("top",b.position().top+b.outerHeight()+"px"),c=a.height()-b.position().top-30),d.css("max-height",c-e.outerHeight()-40+"px"),f.css("max-height",c+"px"),f.show()},a.prototype.drawBlankSlate=function(){return $('<form id="blank-slate" class="float">\n <p>\n Services, and individual systems, can be controlled by sending\n them shell commands through this terminal. Select a system to chat with\n to get started.\n </p>\n <input type="submit" value="Select System"/>\n</form>').appendTo("#alpha"),$("#blank-slate").submit(__bind(function(){return this.showRoster(),this.layout.resize(),!1},this))},a.prototype.draw=function(){var a,b;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","systems-page"),$("#container").hide().empty(),$('<div id="alpha" class="primary column x-fill y-fill">\n <ul id="messages" class="scroll"></ul>\n <form id="message-form">\n <label id="message-label"></label>\n <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a command and press enter to send"/>\n </form>\n <div id="roster" class="float" style="display:none;">\n <ul id="roster-items"></ul>\n <div id="roster-form"></div>\n </div>\n</div>').appendTo("#container"),$("#message-form").css("top","10px"),$("#message-form").submit(__bind(function(){return this.send()},this)),$("#messages").click(function(){return $("#roster").hide()}),$("#message").focus(function(){return $("#roster").hide()}),$(document).keyup(function(a){if(a.keyCode===27)return $("#roster").hide()}),this.roster(),$("#message-label").click(__bind(function(){var a;return a=$("#roster"),a.is(":visible")?a.hide():this.showRoster()},this)),$("#message").keyup(__bind(function(a){switch(a.keyCode){case 38:return $("#message").val(this.commands.prev());case 40:return $("#message").val(this.commands.next())}},this)),this.currentContact?(this.currentContact&&this.restoreChat(this.currentContact),a=this.session.roster[this.currentContact],b=a.name||a.jid.split("@")[0],$("#message-label").text(b),$("#message").focus()):this.drawBlankSlate(),$("#container").show(),this.layout=this.resize(),this.scroll(),$("#message").focus(),new Filter({list:"#roster-items",form:"#roster-form",attrs:["data-jid","data-name"]}),$("form","#roster-form").show()},a.prototype.resize=function(){var a,b,c,d,e;return a=$("#container"),e=$("#messages"),d=$("#message"),b=$("#message-form"),c=$("#message-label"),new Layout(function(){return d.width(b.width()-c.width()-32),e.css("max-height",a.height()-b.height())})},a}();var ServicesPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};ServicesPage=function(){function f(a){this.session=a,this.api=new Api(this.session),this.selectedService=null,this.validateTimeout=null,this.layout=null,this.users=[]}var a,b,c,d,e;return c="http://getvines.com/protocol/services",b="http://getvines.com/protocol/services/members",d="http://getvines.com/protocol/systems",a="http://getvines.com/protocol/systems/attributes",e="http://getvines.com/protocol/users",f.prototype.deleteService=function(a){var b;return this.drawBlankSlate(),this.toggleForm("#remove-contact-form"),b=$("#services li[data-id='"+this.selectedService.id+"']"),this.api.remove(c,this.selectedService.id,__bind(function(a){return b.fadeOut(200,function(){return b.remove(),this.selectedService=null})},this)),!1},f.prototype.icon=function(a){var b,c;return c={darwin:"mac.png",linux:"linux.png",windows:"windows.png"},b=c[a.os]||"run.png","images/"+b},f.prototype.drawMember=function(a){var b;if(!this.editorVisible())return;return b=$('<li>\n <span class="icon"><img src="'+this.icon(a)+'"/></span>\n <span class="text"></span>\n</li>').appendTo("#members"),$(".text",b).text(a.name)},f.prototype.operators=function(){var a,b,c,d,e,f;e=["like","not like","starts with","ends with","is","is not",">",">=","<","<=","and","or"],f=[];for(c=0,d=e.length;c<d;c++)b=e[c],a=$('<li data-selector="'+b+'">\n '+b+"\n</li>").appendTo("#operators"),f.push(a.click(__bind(function(a){var b;return $("#syntax").focus(),b=$(a.currentTarget).attr("data-selector"),$("#syntax").val($("#syntax").val()+(" "+b+" ")),this.validateIn()},this)));return f},f.prototype.selectService=function(a){var b,d;return b=$(a).attr("data-id"),d=$(a).attr("data-name"),$("#services li").removeClass("selected"),$(a).addClass("selected"),$("#remove-service-msg").html("Are you sure you want to remove the "+("<strong>"+d+"</strong> service?")),$("#remove-service-form .buttons").fadeIn(200),this.api.get(c,{id:b},__bind(function(a){return this.selectedService=a,this.drawEditor(a)},this))},f.prototype.serviceNode=function(a){var b,c;return b=a.size===1?"system":"systems",c=$('<li data-id="'+a.id+'" data-name="" data-size="'+a.size+'">\n <span class="text">'+a.name+'</span>\n <span class="count">'+a.size+" "+b+"</span>\n</li>").appendTo("#services"),c.attr("data-name",a.name),$(".text",c).text(a.name),c.click(__bind(function(a){return this.selectService(a.currentTarget)},this)),c},f.prototype.findServices=function(){return this.api.get(c,{},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.serviceNode(b));return f},this))},f.prototype.findMembers=function(a){return this.api.get(b,{id:a},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.drawMember(b));return f},this))},f.prototype.findUsers=function(a){return this.api.get(e,{},__bind(function(a){var b;return this.users=function(){var c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],b.system||f.push(b);return f}(),this.drawUsers()},this))},f.prototype.attributeNode=function(a){var b;return b=$('<li data-name=""></li>').appendTo("#attributes"),b.text(a),b.attr("data-name",a),b.click(__bind(function(a){var b;return $("#syntax").focus(),b=$(a.currentTarget).attr("data-name"),$("#syntax").val($("#syntax").val()+(" "+b+" ")),this.validateIn()},this))},f.prototype.findAttributes=function(b){return this.api.get(a,{},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.attributeNode(b));return f},this))},f.prototype.validateIn=function(a){return clearTimeout(this.validateTimeout),this.validateTimeout=setTimeout(__bind(function(){return this.validate()},this),a||500)},f.prototype.validate=function(){var a,c;$("#syntax-status").text(""),c=$("#syntax").data("prev"),a=$("#syntax").val().trim(),$("#syntax").data("prev",a);if(!a||a===c)return;return $("#syntax-status").text("Searching . . ."),$("#members").empty(),this.api.get2(b,a,__bind(function(a){var b,c,d,e,f;if(a.ok){$("#syntax-status").text(""),e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.drawMember(b));return f}return $("#syntax-status").text(a.error)},this))},f.prototype.validateForm=function(){var a,b;return $("#name-error").empty(),$("#unix-users-error").empty(),b=!0,a=$("#name").val().trim(),a===""&&($("#name-error").text("Name is required."),b=!1),this.accounts().length===0&&($("#unix-users-error").text("At least one user account is required."),b=!1),b},f.prototype.accounts=function(){var a,b,c,d,e;a=$("#unix-users").val().split(","),e=[];for(c=0,d=a.length;c<d;c++)b=a[c],b.trim().length>0&&e.push(b.trim());return e},f.prototype.save=function(){var a,b;return this.validateForm()?(b=$("#users :checked").map(function(){return $(this).val()}).get(),a={name:$("#name").val(),code:$("#syntax").val(),accounts:this.accounts(),users:b},$("#id").val().length>0&&(a.id=$("#id").val()),this.api.save(c,a,__bind(function(a){var b;return new Notification("Service saved successfully"),a.size=$("#members").length,$("#id").val(a.id),b=$("#services li[data-id='"+a.id+"']"),b.length===0?(b=this.serviceNode(a),this.selectService(b)):$(".text",b).text(a.name)},this)),!1):!1},f.prototype.drawBlankSlate=function(){return $("#beta").empty(),$('<form id="blank-slate">\n <p>\n Services are dynamically updated groups of systems based on\n criteria you define. Send a command to the service and it runs\n on every system in the group.\n </p>\n <input type="submit" id="blank-slate-add" value="Add Service"/>\n</form>').appendTo("#beta"),this.api.user.permissions.services||$("#blank-slate-add").remove(),$("#blank-slate").submit(__bind(function(){return this.drawEditor(),!1},this))},f.prototype.draw=function(){var a;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","services-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Services <div id="search-services-icon"></div></h2>\n <div id="search-services-form"></div>\n <ul id="services" class="selectable scroll y-fill"></ul>\n <div id="alpha-controls" class="controls">\n <div id="add-service"></div>\n <div id="remove-service"></div>\n </div>\n <form id="remove-service-form" class="overlay" style="display:none;">\n <h2>Remove Service</h2>\n <p id="remove-service-msg">Select a service in the list above to remove.</p>\n <fieldset class="buttons" style="display:none;">\n <input id="remove-service-cancel" type="button" value="Cancel"/>\n <input id="remove-service-ok" type="submit" value="Remove"/>\n </fieldset>\n </form>\n</div>\n<div id="beta" class="primary column x-fill y-fill"></div>\n<div id="charlie" class="sidebar column y-fill">\n <h2>Operators</h2>\n <ul id="operators"></ul>\n <h2>Attributes <div id="search-attributes-icon"></div></h2>\n <div id="search-attributes-form"></div>\n <ul id="attributes" class="y-fill scroll"></ul>\n</div>').appendTo("#container"),new Button("#add-service",ICONS.plus),new Button("#remove-service",ICONS.minus),this.api.user.permissions.services||$("#alpha-controls div").remove(),this.drawBlankSlate(),$("#add-service").click(__bind(function(){return this.drawEditor()},this)),$("#remove-service").click(__bind(function(){return this.toggleForm("#remove-service-form")},this)),$("#remove-service-cancel").click(__bind(function(){return this.toggleForm("#remove-service-form")},this)),$("#remove-service-form").submit(__bind(function(){return this.deleteService()},this)),this.operators(),this.findServices(),this.findAttributes(),this.findUsers(),$("#container").show(),this.layout=this.resize(),a=__bind(function(){return this.layout.resize(),this.layout.resize()},this),new Filter({list:"#services",icon:"#search-services-icon",form:"#search-services-form",attrs:["data-name"],open:a,close:a}),new Filter({list:"#attributes",icon:"#search-attributes-icon",form:"#search-attributes-form",attrs:["data-name"],open:a,close:a})},f.prototype.drawEditor=function(a){if(!this.pageVisible())return;return a||(this.selectedService=null,$("#services li").removeClass("selected")),$("#beta").empty(),$('<form id="editor-form" class="sections y-fill scroll">\n <input id="id" type="hidden"/>\n <div>\n <section>\n <h2>Service</h2>\n <fieldset>\n <label for="name">Name</label>\n <input id="name" type="text"/>\n <p id="name-error" class="error"></p>\n <label for="syntax">Criteria</label>\n <textarea id="syntax" placeholder="fqdn starts with \'www.\' and platform is \'mac_os_x\'"></textarea>\n <p id="syntax-status"></p>\n </fieldset>\n </section>\n <section>\n <h2>Members</h2>\n <fieldset id="service-preview">\n <ul id="members" class="scroll"></ul>\n </fieldset>\n </section>\n <section>\n <h2>Permissions</h2>\n <fieldset>\n <label>Users</label>\n <ul id="users" class="scroll"></ul>\n <label for="unix-users">Unix Accounts</label>\n <input id="unix-users" type="text"/>\n <p id="unix-users-error" class="error"></p>\n <p class="hint">Comma separated user names like: apache, postgres, root, etc.</p>\n </fieldset>\n </section>\n </div>\n</form>\n<form id="editor-buttons">\n <input id="save" type="submit" value="Save"/>\n</form>').appendTo("#beta"),a&&(this.findMembers(a.id),$("#id").val(a.id),$("#name").val(a.name),$("#syntax").val(a.code),$("#unix-users").val(a.accounts.join(", "))),this.users.length>0&&this.drawUsers(),this.layout.resize(),$("#name").focus(),$("#syntax").change(__bind(function(){return this.validateIn()},this)),$("#syntax").keyup(__bind(function(){return this.validateIn()},this)),$("#editor-form").submit(__bind(function(){return this.save()},this)),$("#editor-buttons").submit(__bind(function(){return this.save()},this))},f.prototype.drawUsers=function(){var a,b,c,d,e;if(!this.editorVisible())return;$("#users").empty(),e=this.users;for(c=0,d=e.length;c<d;c++)b=e[c],a=$("<li>\n <input id='user-"+b.jid+"' type='checkbox' value='"+b.jid+"'/>\n <label for='user-"+b.jid+"'>"+b.jid+"</label>\n</li>").appendTo("#users"),b.jid===this.session.bareJid()&&$("input",a).prop("checked",!0);if(this.selectedService)return $('#users input[type="checkbox"]').val(this.selectedService.users)},f.prototype.toggleForm=function(a,b){return a=$(a),$("form.overlay").each(function(){if(this.id!==a.attr("id"))return $(this).hide()}),a.is(":hidden")?(b&&b(),a.fadeIn(100)):a.fadeOut(100,function(){a[0].reset();if(b)return b()})},f.prototype.pageVisible=function(){return $("#services-page").length>0},f.prototype.editorVisible=function(){return $("#services-page #editor-form").length>0},f.prototype.resize=function(){var a,b,c;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),new Layout(function(){return c.css("left",a.width()+b.width())})},f}();var FilesPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};FilesPage=function(){function d(a){this.session=a,this.api=new Api(this.session),this.uploads=new c({session:this.session,jid:this.api.jid,size:this.size,complete:__bind(function(a){return this.findFile(a.name)},this)})}var a,b,c;return a="http://getvines.com/protocol/files",b="http://getvines.com/protocol/files/labels",d.prototype.findLabels=function(){return $("#labels").empty(),this.api.get(b,{},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.labelNodeList(b));return f},this))},d.prototype.labelNodeList=function(a){var b,c;return c=a.size===1?"file":"files",b=$('<li data-name="">\n <span class="text"></span>\n <span class="count">'+a.size+" "+c+"</span>\n</li>").appendTo("#labels"),$(".text",b).text(a.name),b.attr("data-name",a.name),b.click(__bind(function(a){return this.selectLabel(a)},this))},d.prototype.selectLabel=function(a){var b;return b=$(a.currentTarget).attr("data-name"),$("#labels li").removeClass("selected"),$(a.currentTarget).addClass("selected"),$("#files").empty(),this.findFiles({label:$(a.currentTarget).attr("data-name")})},d.prototype.findFiles=function(b){return this.api.get(a,b,__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.fileNode(b));return f},this))},d.prototype.findFile=function(b){return this.api.get(a,{name:b},__bind(function(a){return this.fileNode(a)},this))},d.prototype.updateFileNode=function(a){var b,c,d;return b=$("#files li[data-id='"+a.id+"']"),c=this.size(a.size),d=this.date(a.created_at),b.data("file",a),$("h2",b).text(a.name),$(".size",b).text(c),$(".time",b).text(d),b.attr("data-name",a.name),b.attr("data-size",c),b.attr("data-created",d)},d.prototype.fileNode=function(a){var b,c,d,e,f,g;if($("#files li[data-id='"+a.id+"']").length>0){this.updateFileNode(a);return}c=$('<li data-id="'+a.id+'">\n <div class="file-icon">\n <span class="size"></span>\n </div>\n <h2></h2>\n <footer>\n <span class="time"></span>\n <ul class="labels"></ul>\n <form class="add-label">\n <div class="add-label-button"></div>\n <input type="text" placeholder="Label" style="display:none;"/>\n </form>\n </footer>\n <form class="file-form">\n <fieldset>\n <input class="cancel" type="submit" value="Delete"/>\n </fieldset>\n </form>\n</li>').appendTo("#files"),new Button($(".file-icon",c).get(0),ICONS.page2,{scale:1,translation:"-2 0","stroke-width":.1,opacity:1}),new Button($(".add-label-button",c).get(0),ICONS.plus,{translation:"-10 -10",scale:.5}),$("form.file-form",c).submit(__bind(function(){return this.deleteFile(c)},this)),$("form.add-label",c).submit(__bind(function(){return this.addLabel(c)},this)),$(".add-label-button",c).click(function(){return $('form.add-label input[type="text"]',c).show()}),this.updateFileNode(a),f=a.labels,g=[];for(d=0,e=f.length;d<e;d++)b=f[d],g.push(this.labelNode(c,b));return g},d.prototype.labelNode=function(a,b){var c,d;return d=$(".labels",a),c=$('<li data-name="">\n <span class="text"></span>\n <div class="remove"></div>\n</li>').appendTo(d),$(".text",c).text(b),c.attr("data-name",b),new Button($(".remove",c).get(0),ICONS.cross,{translation:"-8 -8",scale:.5}),$(".remove",c).click(__bind(function(){return this.removeLabel(a,c)},this))},d.prototype.addLabel=function(b){var c,d,e,f,g,h,i,j,k;d=$('form.add-label input[type="text"]',b),d.hide(),f=function(){var a,b,c,e;c=d.val().split(/,/),e=[];for(a=0,b=c.length;a<b;a++)g=c[a],g&&e.push(g);return e}(),d.val(""),c=b.data("file");for(h=0,j=f.length;h<j;h++)e=f[h],c.labels.push(e);this.api.save(a,c,function(a){});for(i=0,k=f.length;i<k;i++)e=f[i],this.labelNode(b,e),this.updateLabelCount(e,1);return!1},d.prototype.removeLabel=function(b,c){var d,e,f;return d=b.data("file"),f=c.attr("data-name"),d.labels=function(){var a,b,c,g;c=d.labels,g=[];for(a=0,b=c.length;a<b;a++)e=c[a],e!==f&&g.push(e);return g}(),this.api.save(a,d,function(a){}),c.fadeOut(200,function(){return c.remove()}),this.updateLabelCount(f,-1)},d.prototype.updateLabelCount=function(a,b){var c,d,e;return d=$("#labels li[data-name='"+a+"'] .count"),d.length>0?(c=parseInt(d.text().split(" ")[0])+b,e=c===1?"file":"files",d.text(""+c+" "+e)):this.labelNodeList({name:a,size:1})},d.prototype.deleteFile=function(b){return this.api.remove(a,b.attr("data-id"),__bind(function(a){var c,d,e,f;f=b.data("file").labels;for(d=0,e=f.length;d<e;d++)c=f[d],this.updateLabelCount(c,-1);return b.fadeOut(200,function(){return b.remove()})},this)),!1},d.prototype.date=function(a){var b,c;return a=new Date(a),b="Sun Mon Tue Wed Thu Fri Sat".split(" ")[a.getDay()],c="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[a.getMonth()],""+b+", "+a.getDate()+" "+c+", "+a.getFullYear()+" @ "+a.getHours()+":"+a.getMinutes()},d.prototype.size=function(a){var b,c,d,e;return d=a/1024,e=d/1024,c=e/1024,b=function(a){return a<100?a.toFixed(1).replace(".0",""):Math.round(a)},d<1?""+a+" b":e<1?""+b(d)+" k":c<1?""+b(e)+" m":""+b(c)+" g"},d.prototype.draw=function(){var a,b;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","files-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Labels</h2>\n <ul id="labels" class="selectable scroll y-fill"></ul>\n <div id="alpha-controls" class="controls"></div>\n</div>\n<div id="beta" class="primary column x-fill y-fill">\n <h2 id="files-title">Files <div id="search-files-icon"></div></h2>\n <div id="search-files-form"></div>\n <ul id="files" class="scroll y-fill"></ul>\n</div>\n<div id="charlie" class="sidebar column y-fill">\n <h2>Uploads</h2>\n <div id="upload-dnd" class="float">Drag files here to upload.</div>\n <ul id="uploads" class="scroll y-fill"></ul>\n <div id="charlie-controls" class="controls">\n <form id="file-form">\n <input id="open-file-chooser" type="submit" value="Select files to upload"/>\n <input id="file-chooser" type="file" multiple="true" />\n </form>\n </div>\n</div>').appendTo("#container"),this.api.user.permissions.files?($("#file-chooser").change(__bind(function(a){return this.uploads.queue(a.target.files),$("#file-chooser").val("")},this)),$("#file-form").submit(function(){return $("#file-chooser").click(),!1}),$("#upload-dnd").bind("dragenter",function(a){return a.stopPropagation(),a.preventDefault(),$("#upload-dnd").css("color","#444")}),$("#upload-dnd").bind("dragleave",function(a){return $("#upload-dnd").css("color","#ababab")}),$("#upload-dnd").bind("dragover",function(a){return a.stopPropagation(),a.preventDefault()}),$("#upload-dnd").bind("drop",__bind(function(a){return a.stopPropagation(),a.preventDefault(),$("#upload-dnd").css("color","#ababab"),this.uploads.queue(a.originalEvent.dataTransfer.files)},this))):($("#upload-dnd").text("Your account cannot upload files."),$("#file-form").remove()),this.findLabels(),this.findFiles(),$("#container").show(),b=this.resize(),a=function(){return b.resize(),b.resize()},new Filter({list:"#files",icon:"#search-files-icon",form:"#search-files-form",attrs:["data-name","data-created"],open:a,close:a})},d.prototype.resize=function(){var a,b,c,d,e;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),e=$("#uploads"),d=$("#upload-dnd"),new Layout(function(){return c.css("left",a.width()+b.width()),d.height(e.height()),d.css("line-height",e.height()+"px")})},c=function(){function a(a){this.session=a.session,this.serviceJid=a.jid,this.size=a.size,this.complete=a.complete,this.uploads=[],this.sending=null}return a.prototype.queue=function(a){var b,c,d;for(c=0,d=a.length;c<d;c++)b=a[c],this.find(b)||this.add(b);return this.process()},a.prototype.add=function(a){var b,c;return c=this.node(a),b=$(".meter",c),this.uploads.push(new Transfer({to:this.serviceJid(),file:a,session:this.session,progress:function(a){return b.css("width",a+"%")},complete:__bind(function(){return this.remove(a),this.complete&&this.complete(a),a.name===this.sending.name&&(this.sending=null),this.process()},this)}))},a.prototype.process=function(){var a;if(this.sending)return;return(a=this.uploads[0])?(this.sending=a.file,a.start()):this.sending=null},a.prototype.find=function(a){var b;return function(){var c,d,e,f;e=this.uploads,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],b.file.name===a.name&&f.push(b);return f}.call(this).shift()},a.prototype.remove=function(a){var b,c;return this.uploads=function(){var b,d,e,f;e=this.uploads,f=[];for(b=0,d=e.length;b<d;b++)c=e[b],c.file.name!==a.name&&f.push(c);return f}.call(this),b=$("#uploads li[data-file='"+a.name+"']"),b.fadeOut(200,function(){return b.remove()})},a.prototype.node=function(a){var b;return b=$('<li data-file="" style="display:none;">\n <form class="inset">\n <h2></h2>\n <div class="progress">\n <div class="meter"></div>\n <span class="text">'+this.size(a.size)+'</span>\n <div class="cancel"></div>\n </div>\n </form>\n</li>').appendTo("#uploads"),b.fadeIn(200),$("h2",b).text(a.name),b.attr("data-file",a.name),new Button($(".cancel",b).get(0),ICONS.cross,{translation:"-8 -8",scale:.5}),$(".cancel",b).click(__bind(function(){return this.cancel(a)},this)),b},a.prototype.cancel=function(a){var b;if(b=this.find(a))return b.stop()},a}(),d}();var SetupPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};SetupPage=function(){function d(a){this.session=a,this.api=new Api(this.session),this.layout=null,this.selected=null,this.services=[],this.users=[]}var a,b,c;return a="http://getvines.com/protocol/services",b="http://getvines.com/protocol/systems",c="http://getvines.com/protocol/users",d.prototype.findSystem=function(a){return this.api.get(b,{name:a},__bind(function(a){return this.drawSystemInfo(a)},this))},d.prototype.findServices=function(){return this.api.get(a,{},__bind(function(a){return this.services=a.rows,this.drawServices()},this))},d.prototype.drawServices=function(){var a,b,c,d,e;if($("#setup-page #services").length===0)return;$("#services").empty(),e=this.services;for(c=0,d=e.length;c<d;c++)b=e[c],a=$("<li>\n <input id='service-"+b.id+"' type='checkbox' value='"+b.id+"'/>\n <label for='service-"+b.id+"'></label>\n</li>").appendTo("#services"),$("label",a).text(b.name);this.selected&&$('#services input[type="checkbox"]').val(this.selected.services);if(this.selected&&!this.api.user.permissions.services)return $('#services input[type="checkbox"]').prop("disabled",!0)},d.prototype.findUsers=function(){return this.api.get(c,{},__bind(function(a){return this.users=a.rows,this.drawUsers()},this))},d.prototype.drawUsers=function(){var a,b,c,d,e,f;if($("#setup-page #users").length===0)return;$("#users").empty(),a=$("#systems-nav").hasClass("selected"),e=this.users,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(b.system===a?this.userNode(b):void 0);return f},d.prototype.userNode=function(a){var b,c;return c=$('<li data-name="" data-jid="'+a.jid+'" id="'+a.jid+'">\n <span class="text"></span>\n <span class="jid">'+a.jid+"</span>\n</li>").appendTo("#users"),b=this.userName(a),$(".text",c).text(b),c.attr("data-name",b),c.click(__bind(function(a){return this.selectUser(a.currentTarget)},this)),c},d.prototype.userName=function(a){return a.name||a.jid.split("@")[0]},d.prototype.selectUser=function(a){var b,d;return b=$(a).attr("data-jid"),d=$(a).attr("data-name"),$("#users li").removeClass("selected"),$(a).addClass("selected"),$("#remove-user-msg").html("Are you sure you want to remove "+("<strong>"+d+"</strong>?")),$("#remove-user-form .buttons").fadeIn(200),this.api.get(c,{jid:b},__bind(function(a){return this.selected=a,a.system?this.drawSystemEditor(a):this.drawUserEditor(a)},this))},d.prototype.removeUser=function(){var a;return this.toggleForm("#remove-user-form"),a=$("#users li[data-jid='"+this.selected.jid+"']"),this.api.remove(c,this.selected.jid,__bind(function(b){return a.fadeOut(200,__bind(function(){var b;return this.users=function(){var a,c,d,e;d=this.users,e=[];for(a=0,c=d.length;a<c;a++)b=d[a],b.jid!==this.selected.jid&&e.push(b);return e}.call(this),a.remove(),this.selected=null,$("#users-nav").hasClass("selected")?this.drawUserBlankSlate
2
- ():this.drawSystemBlankSlate()},this))},this)),!1},d.prototype.selectTask=function(a){this.selected=null,$("#setup li").removeClass("selected secondary"),$(a.currentTarget).addClass("selected secondary");switch($(a.currentTarget).attr("id")){case"users-nav":return $("#beta-header").text("Users"),this.drawUsers(),this.drawUserBlankSlate(),this.api.user.permissions.users?$("#beta-controls div").show():$("#beta-controls div").hide();case"systems-nav":return $("#beta-header").text("Systems"),this.drawUsers(),this.drawSystemBlankSlate(),this.api.user.permissions.systems?$("#beta-controls div").show():$("#beta-controls div").hide()}},d.prototype.toggleForm=function(a,b){return a=$(a),$("form.overlay").each(function(){if(this.id!==a.attr("id"))return $(this).hide()}),a.is(":hidden")?(b&&b(),a.fadeIn(100)):a.fadeOut(100,function(){a[0].reset();if(b)return b()})},d.prototype.validateUser=function(){var a,b,c,d;$("#user-name-error").empty(),$("#password-error").empty(),d=!0,a=$.trim($("#user-name").val()),b=$.trim($("#password1").val()),c=$.trim($("#password2").val());if(this.selected)c.length>0&&c.length<8&&($("#password-error").text("Password must be at least 8 characters."),d=!1),this.session.bareJid()!==this.selected.jid&&b!==c&&($("#password-error").text("Passwords must match."),d=!1);else{a===""&&($("#user-name-error").text("User name is required."),d=!1),a.match(/[\s"&'\/:<>@]/)&&($("#user-name-error").text("User name contains forbidden characters."),d=!1);if(b.length===0||c.length===0)$("#password-error").text("Password is required."),d=!1;b!==c&&($("#password-error").text("Passwords must match."),d=!1),c.length<8&&($("#password-error").text("Password must be at least 8 characters."),d=!1)}return d},d.prototype.saveUser=function(){var a;return this.validateUser()?(a={jid:$("#jid").val(),username:$("#user-name").val(),name:$("#name").val(),password1:$("#password1").val(),password2:$("#password2").val(),services:$("#services :checked").map(function(){return $(this).val()}).get(),permissions:{systems:$("#perm-systems").prop("checked"),services:$("#perm-services").prop("checked"),files:$("#perm-files").prop("checked"),users:$("#perm-users").prop("checked")}},this.api.save(c,a,__bind(function(a){var b;return new Notification("User saved successfully"),$("#jid").val(a.jid),b=$("#users li[data-jid='"+a.jid+"']"),b.length===0?(this.users.push(a),b=this.userNode(a),this.selectUser(b)):$(".text",b).text(this.userName(a))},this)),!1):!1},d.prototype.validateSystem=function(){var a,b;return $("#user-name-error").empty(),b=!0,a=$.trim($("#user-name").val()),this.selected||(a===""&&($("#user-name-error").text("Hostname is required."),b=!1),a.match(/[\s"&'\/:<>@]/)&&($("#user-name-error").text("Hostname contains forbidden characters."),b=!1)),b},d.prototype.saveSystem=function(){var a;return this.validateSystem()?(a={jid:$("#jid").val(),username:$("#user-name").val(),password1:$("#password1").val(),password2:$("#password1").val(),system:!0},this.api.save(c,a,__bind(function(a){var b;return new Notification("System saved successfully"),$("#jid").val(a.jid),b=$("#users li[data-jid='"+a.jid+"']"),b.length===0?(this.users.push(a),b=this.userNode(a),this.selectUser(b)):$(".text",b).text(a.name)},this)),!1):!1},d.prototype.rand=function(){return Math.floor(Math.random()*16)},d.prototype.token=function(){var a;return function(){var b;b=[];for(a=0;a<=127;a++)b.push(this.rand().toString(16));return b}.call(this).join("")},d.prototype.drawUserBlankSlate=function(){var a;return $("#charlie").empty(),a=this.api.user.permissions.users?"Select a user account to update or add a new user.":"Select a user account to update.",$('<form id="blank-slate">\n <p>'+a+'</p>\n <input type="submit" id="blank-slate-add" value="Add User"/>\n</form>').appendTo("#charlie"),this.api.user.permissions.users||$("#blank-slate-add").remove(),$("#blank-slate").submit(__bind(function(){return this.drawUserEditor(),!1},this))},d.prototype.drawSystemBlankSlate=function(){return $("#charlie").empty(),$('<form id="blank-slate">\n <p>\n Systems need a user account before they can connect and\n authenticate with the chat server.\n </p>\n <input type="submit" id="blank-slate-add" value="Add System"/>\n</form>').appendTo("#charlie"),this.api.user.permissions.systems||$("#blank-slate-add").remove(),$("#blank-slate").submit(__bind(function(){return this.drawSystemEditor(),!1},this))},d.prototype.draw=function(){var a;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","setup-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Setup</h2>\n <ul id="setup" class="selectable scroll y-fill">\n <li id="users-nav" class=\'selected secondary\'>\n <span class="text">Users</span>\n </li>\n <li id="systems-nav">\n <span class="text">Systems</span>\n </li>\n </ul>\n <div id="alpha-controls" class="controls"></div>\n</div>\n<div id="beta" class="sidebar column y-fill">\n <h2><span id="beta-header">Users</span> <div id="search-users-icon"></div></h2>\n <div id="search-users-form"></div>\n <ul id="users" class="selectable scroll y-fill"></ul>\n <form id="remove-user-form" class="overlay" style="display:none;">\n <h2>Remove User</h2>\n <p id="remove-user-msg">Select a user to delete.</p>\n <fieldset class="buttons" style="display:none;">\n <input id="remove-user-cancel" type="button" value="Cancel"/>\n <input id="remove-user-ok" type="submit" value="Remove"/>\n </fieldset>\n </form>\n <div id="beta-controls" class="controls">\n <div id="add-user"></div>\n <div id="remove-user"></div>\n </div>\n</div>\n<div id="charlie" class="primary column x-fill y-fill"></div>').appendTo("#container"),this.drawUserBlankSlate(),$("#setup li").click(__bind(function(a){return this.selectTask(a)},this)),this.findUsers(),this.findServices(),$("#container").show(),this.layout=this.resize(),new Button("#add-user",ICONS.plus),new Button("#remove-user",ICONS.minus),this.api.user.permissions.users||$("#beta-controls div").hide(),this.api.user.permissions.systems||$("#systems-nav").hide(),$("#add-user").click(__bind(function(){return $("#users-nav").hasClass("selected")?this.drawUserEditor():this.drawSystemEditor()},this)),$("#remove-user").click(__bind(function(){return this.toggleForm("#remove-user-form")},this)),$("#remove-user-cancel").click(__bind(function(){return this.toggleForm("#remove-user-form")},this)),$("#remove-user-form").submit(__bind(function(){return this.removeUser()},this)),a=__bind(function(){return this.layout.resize(),this.layout.resize()},this),new Filter({list:"#users",icon:"#search-users-icon",form:"#search-users-form",attrs:["data-jid","data-name"],open:a,close:a})},d.prototype.drawUserEditor=function(a){var b,c,d,e;a||(this.selected=null,$("#users li").removeClass("selected")),$("#charlie").empty(),$('<form id="editor-form" class="sections y-fill scroll">\n <div>\n <section>\n <h2>User</h2>\n <fieldset id="jid-fields">\n <input id="jid" type="hidden" value=""/>\n <label for="name">Real Name</label>\n <input id="name" type="text" maxlength="1024"/>\n </fieldset>\n </section>\n <section>\n <h2>Password</h2>\n <fieldset>\n <label id="password1-label" for="password1">Current Password</label>\n <input id="password1" type="password" maxlength="1024"/>\n <label id="password2-label" for="password2">New Password</label>\n <input id="password2" type="password" maxlength="1024"/>\n <p id="password-error" class="error"></p>\n </fieldset>\n </section>\n <section>\n <h2>Permissions</h2>\n <fieldset>\n <label>Manage</label>\n <ul id="permissions">\n <li>\n <input id="perm-systems" type="checkbox" value="systems"/>\n <label for="perm-systems">Systems</label>\n </li>\n <li>\n <input id="perm-services" type="checkbox" value="services"/>\n <label for="perm-services">Services</label>\n </li>\n <li>\n <input id="perm-users" type="checkbox" value="users"/>\n <label for="perm-users">Users</label>\n </li>\n <li>\n <input id="perm-files" type="checkbox" value="files"/>\n <label for="perm-files">Files</label>\n </li>\n </ul>\n </fieldset>\n </section>\n <section>\n <h2>Services</h2>\n <fieldset>\n <label>Access To</label>\n <ul id="services" class="scroll"></ul>\n </fieldset>\n </section>\n </div>\n</form>\n<form id="editor-buttons">\n <input id="save" type="submit" value="Save"/>\n</form>').appendTo("#charlie");if(a){$("<label>Account Name</label>\n<p>"+a.jid+"</p>").prependTo("#jid-fields"),$("#name").focus(),this.session.bareJid()!==a.jid&&($("#password1-label").text("Password"),$("#password2-label").text("Password Again")),$("#jid").val(a.jid),$("#name").val(a.name),$("#user-name").val(a.jid.split("@")[0]),e="services systems files users".split(" ");for(c=0,d=e.length;c<d;c++)b=e[c],a.permissions[b]&&$("#perm-"+b).prop("checked",!0),this.session.bareJid()===a.jid&&$("#perm-"+b).prop("disabled",!0)}else $('<label for="user-name">User Name</label>\n<input id="user-name" type="text" maxlength="1023"/>\n<p id="user-name-error" class="error"></p>').prependTo("#jid-fields"),$("#password1-label").text("Password"),$("#password2-label").text("Password Again"),$("#user-name").focus();return this.services.length>0&&this.drawServices(),this.layout.resize(),$("#editor-form").submit(__bind(function(){return this.saveUser()},this)),$("#editor-buttons").submit(__bind(function(){return this.saveUser()},this))},d.prototype.drawSystemEditor=function(a){return a||(this.selected=null,$("#users li").removeClass("selected")),$("#charlie").empty(),$('<form id="editor-form" class="sections y-fill scroll">\n <div>\n <section>\n <h2>System</h2>\n <fieldset id="jid-fields">\n <input id="jid" type="hidden" value=""/>\n <label id="password1-label" for="password1">Authentication Token</label>\n <div id="token-container">\n <input id="password1" type="text" readonly placeholder="Press Generate to create a new token"/>\n <input id="new-token" type="button" value="Generate"/>\n </div>\n </fieldset>\n </section>\n <section id="info">\n <h2>Info</h2>\n <fieldset>\n <label>Platform</label>\n <p id="info-platform">-</p>\n <label>Hostname</label>\n <p id="info-fqdn">-</p>\n <label>IP Address</label>\n <p id="info-ip">-</p>\n <label>MAC Address</label>\n <p id="info-mac">-</p>\n </fieldset>\n </section>\n </div>\n</form>\n<form id="editor-buttons">\n <input id="save" type="submit" value="Save"/>\n</form>').appendTo("#charlie"),$("#new-token").click(__bind(function(){return $("#password1").val(this.token())},this)),a&&this.findSystem(a.jid.split("@")[0]),a?($("<label>Account Name</label>\n<p>"+a.jid+"</p>").prependTo("#jid-fields"),$("#jid").val(a.jid),$("#user-name").val(a.jid.split("@")[0])):($('<label for="user-name">Hostname</label>\n<input id="user-name" type="text" maxlength="1023"/>\n<p id="user-name-error" class="error"></p>').prependTo("#jid-fields"),$("#user-name").focus(),$("#password1").val(this.token())),this.layout.resize(),$("#editor-form").submit(__bind(function(){return this.saveSystem()},this)),$("#editor-buttons").submit(__bind(function(){return this.saveSystem()},this))},d.prototype.drawSystemInfo=function(a){return $("#info-platform").text(a.platform),$("#info-fqdn").text(a.fqdn),$("#info-ip").text(a.ipaddress),$("#info-mac").text(a.macaddress)},d.prototype.resize=function(){var a,b,c;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),new Layout(function(){return c.css("left",a.outerWidth()+b.outerWidth())})},d}(),$(function(){var a,b,c,d,e,f;f=new Session,d=new NavBar(f),d.draw(),a={Systems:ICONS.commandline,Services:ICONS.magic,Files:ICONS.page2,Setup:ICONS.gear2,Logout:ICONS.power};for(c in a)b=a[c],d.addButton(c,b);return e={"/systems":new SystemsPage(f),"/services":new ServicesPage(f),"/files":new FilesPage(f),"/setup":new SetupPage(f),"/logout":new LogoutPage(f),"default":new LoginPage(f,"/systems/")},(new Router(e)).draw(),d.select($("#nav-link-systems").parent())})
1
+ var Api,__bind=function(a,b){return function(){return a.apply(b,arguments)}};Api=function(){function b(b){this.session=b,this.user=null,this.session.onRoster(__bind(function(){return this.get(a,{jid:this.session.bareJid()},__bind(function(a){return this.user=a},this))},this))}var a;return a="http://getvines.com/protocol/users",b.prototype.jid=function(){return"vines."+this.session.bareJid().split("@")[1]},b.prototype.get=function(a,b,c){var d,e,f;e=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="get">\n <query xmlns="'+a+'"/>\n</iq>');for(d in b)f=b[d],$("query",e).attr(d,f);return this.session.sendIQ(e,__bind(function(a){var b;b=$(a).attr("type")==="result";if(!b)return;return c(JSON.parse($("query",a).text()))},this))},b.prototype.get2=function(a,b,c){var d;return d=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="get">\n <query xmlns="'+a+'"/>\n</iq>'),$("query",d).text(b),this.session.sendIQ(d,__bind(function(a){var b;b=$(a).attr("type")==="result";if(!b)return;return c(JSON.parse($("query",a).text()))},this))},b.prototype.remove=function(a,b,c){var d;return d=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="set">\n <query xmlns="'+a+'" action="delete" id=""/>\n</iq>'),$("query",d).attr("id",b),this.session.sendIQ(d,c)},b.prototype.save=function(a,b,c){var d;return d=this.session.xml('<iq id="'+this.session.uniqueId()+'" to="'+this.jid()+'" type="set">\n <query xmlns="'+a+'"/>\n</iq>'),$("query",d).text(JSON.stringify(b)),this.session.sendIQ(d,__bind(function(a){var b;b=$(a).attr("type")==="result";if(!b)return;return c(JSON.parse($("query",a).text()))},this))},b}();var Commands;Commands=function(){function a(){this.buf=[],this.index=0}return a.prototype.prev=function(){var a;return a=this.buf[--this.index],a||(this.index=-1),a||""},a.prototype.next=function(){var a;return a=this.buf[++this.index],a||(this.index=this.buf.length),a||""},a.prototype.push=function(a){return this.buf.push(a),this.index=this.buf.length},a}();var SystemsPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};SystemsPage=function(){function a(a){this.session=a,this.session.onRoster(__bind(function(){return this.roster()},this)),this.session.onMessage(__bind(function(a){return this.message(a)},this)),this.session.onPresence(__bind(function(a){return this.presence(a)},this)),this.commands=new Commands,this.chats={},this.currentContact=null,this.layout=null}return a.prototype.datef=function(a){var b,c,d,e;return b=new Date(a),d=b.getHours()<12?" am":" pm",c=b.getHours()>12?b.getHours()-12:b.getHours(),c===0&&(c=12),e=b.getMinutes()+"",e.length===1&&(e="0"+e),c+":"+e+d},a.prototype.groupContacts=function(){var a,b,c,d,e,f,g,h;c={},g=this.session.roster;for(d in g){a=g[d],h=a.groups;for(e=0,f=h.length;e<f;e++)b=h[e],(c[b]||(c[b]=[])).push(a)}return c},a.prototype.roster=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n;d=this.groupContacts(),k=function(){var a;a=[];for(c in d)b=d[c],a.push(c);return a}(),k=k.sort(function(a,b){return a=a.toLowerCase(),b=b.toLowerCase(),a>b?1:a<b?-1:0}),f=$("#roster-items").empty(),n=[];for(l=0,m=k.length;l<m;l++)c=k[l],b=d[c],h=$('<li class="group"></li>').appendTo(f),h.text(c),h.attr("data-group",c),n.push(function(){var d,h,k;k=[];for(d=0,h=b.length;d<h;d++)a=b[d],i=$('<li data-jid="'+a.jid+'">\n <span class="icon"></span>\n <span class="text"></span>\n <span class="unread" style="display:none;"></span>\n</li>').appendTo(f),a.offline()&&i.addClass("offline"),i.click(__bind(function(a){return this.selectContact(a)},this)),g=a.name||a.jid.split("@")[0],i.attr("data-name",g),i.attr("data-group",c),$(".text",i).text(g),j={fill:"#fff",stroke:"#404040","stroke-width":.3,opacity:1,scale:.65},e=function(){switch(c){case"People":return ICONS.man;case"Services":return ICONS.magic;default:return ICONS.run}}(),k.push(new Button($(".icon",i),e,j));return k}.call(this));return n},a.prototype.message=function(a){var b,c,d,e;this.queueMessage(a),e=a.from===this.session.jid(),d=a.from.split("/")[0];if(!e&&d!==this.currentContact)return c=this.chat(a.from),c.unread++,this.eachContact(d,function(a){return $(".unread",a).text(c.unread).show()});b=this.atBottom(),this.appendMessage(a);if(b||e)return this.scroll({animate:!0})},a.prototype.eachContact=function(a,b){var c,d,e,f,g;f=$("#roster-items li[data-jid='"+a+"']").get(),g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(b($(c)));return g},a.prototype.appendMessage=function(a){var b,c,d,e,f,g,h;d=a.from===this.session.jid(),h=$("jid",a.node).text(),c=(h||a.from).split("/")[0],b=this.session.roster[c],e=b?b.name||c:c,f=$('<li data-jid="'+c+'"><pre></pre></li>').appendTo("#messages"),g=d?"$ ":"",$("pre",f).text(g+a.text),$("#message-form").css("top","0px");if(!d)return f.append('<footer>\n <span class="author"></span> @\n <span class="time">'+this.datef(a.received)+"</span>\n</footer>"),$(".author",f).text(e)},a.prototype.queueMessage=function(a){var b,c,d;return d=a.from===this.session.jid(),c=a[d?"to":"from"],b=this.chat(c),b.jid=c,b.messages.push(a)},a.prototype.chat=function(a){var b,c;return b=a.split("/")[0],c=this.chats[b],c||(c={jid:a,messages:[],unread:0},this.chats[b]=c),c},a.prototype.presence=function(a){var b,c;c=a.from.split("/")[0];if(c===this.session.bareJid())return;if(!a.type||a.offline)b=this.session.roster[c],this.eachContact(c,function(a){return b.offline()?a.addClass("offline"):a.removeClass("offline")});if(a.offline)return this.chat(c).jid=c},a.prototype.selectContact=function(a){var b,c,d;$("#blank-slate").fadeOut(200,function(){return $(this).remove()}),$("#roster").hide(),$("#message").focus(),d=$(a.currentTarget),c=d.attr("data-jid"),b=this.session.roster[c];if(this.currentContact===c)return;return this.currentContact=c,$("#message-label").text($(".text",d).text()),$("#messages").empty(),$("#message-form").css("top","10px"),this.layout.resize(),this.restoreChat(c)},a.prototype.restoreChat=function(a){var b,c,d,e,f;b=this.chats[a],c=[],b&&(c=b.messages,b.unread=0,this.eachContact(a,function(a){return $(".unread",a).text("").hide()}));for(e=0,f=c.length;e<f;e++)d=c[e],this.appendMessage(d);return this.scroll()},a.prototype.scroll=function(a){var b;return a||(a={}),b=$("#messages"),a.animate?b.animate({scrollTop:b.prop("scrollHeight")},400):b.scrollTop(b.prop("scrollHeight"))},a.prototype.atBottom=function(){var a,b;return b=$("#messages"),a=b.prop("scrollHeight")-b.outerHeight(),b.scrollTop()>=a},a.prototype.send=function(){var a,b,c,d;return this.currentContact?(b=$("#message"),d=b.val().trim(),d&&(a=this.chats[this.currentContact],c=a?a.jid:this.currentContact,this.message({from:this.session.jid(),text:d,to:c,received:new Date}),this.session.sendMessage(c,d),this.commands.push(d)),b.val(""),!1):!1},a.prototype.showRoster=function(){var a,b,c,d,e,f,g;return a=$("#container"),b=$("#message-form"),f=$("#roster"),d=$("#roster-items"),e=$("#roster-form"),g=a.height()-b.position().top<a.height()/2,g?(f.css("top",""),f.css("bottom",b.outerHeight()+5+"px"),c=a.height()-b.outerHeight()-20):(f.css("bottom",""),f.css("top",b.position().top+b.outerHeight()+"px"),c=a.height()-b.position().top-30),d.css("max-height",c-e.outerHeight()-40+"px"),f.css("max-height",c+"px"),f.show()},a.prototype.drawBlankSlate=function(){return $('<form id="blank-slate" class="float">\n <p>\n Services, and individual systems, can be controlled by sending\n them shell commands through this terminal. Select a system to chat with\n to get started.\n </p>\n <input type="submit" value="Select System"/>\n</form>').appendTo("#alpha"),$("#blank-slate").submit(__bind(function(){return this.showRoster(),this.layout.resize(),!1},this))},a.prototype.draw=function(){var a,b;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","systems-page"),$("#container").hide().empty(),$('<div id="alpha" class="primary column x-fill y-fill">\n <ul id="messages" class="scroll"></ul>\n <form id="message-form">\n <label id="message-label"></label>\n <input id="message" name="message" type="text" maxlength="1024" placeholder="Type a command and press enter to send"/>\n </form>\n <div id="roster" class="float" style="display:none;">\n <ul id="roster-items"></ul>\n <div id="roster-form"></div>\n </div>\n</div>').appendTo("#container"),$("#message-form").css("top","10px"),$("#message-form").submit(__bind(function(){return this.send()},this)),$("#messages").click(function(){return $("#roster").hide()}),$("#message").focus(function(){return $("#roster").hide()}),$(document).keyup(function(a){if(a.keyCode===27)return $("#roster").hide()}),this.roster(),$("#message-label").click(__bind(function(){var a;return a=$("#roster"),a.is(":visible")?a.hide():this.showRoster()},this)),$("#message").keyup(__bind(function(a){switch(a.keyCode){case 38:return $("#message").val(this.commands.prev());case 40:return $("#message").val(this.commands.next())}},this)),this.currentContact?(this.currentContact&&this.restoreChat(this.currentContact),a=this.session.roster[this.currentContact],b=a.name||a.jid.split("@")[0],$("#message-label").text(b),$("#message").focus()):this.drawBlankSlate(),$("#container").show(),this.layout=this.resize(),this.scroll(),$("#message").focus(),new Filter({list:"#roster-items",form:"#roster-form",attrs:["data-jid","data-name"]}),$("form","#roster-form").show()},a.prototype.resize=function(){var a,b,c,d,e;return a=$("#container"),e=$("#messages"),d=$("#message"),b=$("#message-form"),c=$("#message-label"),new Layout(function(){return d.width(b.width()-c.width()-32),e.css("max-height",a.height()-b.height())})},a}();var ServicesPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};ServicesPage=function(){function f(a){this.session=a,this.api=new Api(this.session),this.selectedService=null,this.validateTimeout=null,this.layout=null,this.users=[]}var a,b,c,d,e;return c="http://getvines.com/protocol/services",b="http://getvines.com/protocol/services/members",d="http://getvines.com/protocol/systems",a="http://getvines.com/protocol/systems/attributes",e="http://getvines.com/protocol/users",f.prototype.deleteService=function(a){var b;return this.drawBlankSlate(),this.toggleForm("#remove-contact-form"),b=$("#services li[data-id='"+this.selectedService.id+"']"),this.api.remove(c,this.selectedService.id,__bind(function(a){return b.fadeOut(200,function(){return b.remove(),this.selectedService=null})},this)),!1},f.prototype.icon=function(a){var b,c;return c={darwin:"mac.png",linux:"linux.png",windows:"windows.png"},b=c[a.os]||"run.png","images/"+b},f.prototype.drawMember=function(a){var b;if(!this.editorVisible())return;return b=$('<li>\n <span class="icon"><img src="'+this.icon(a)+'"/></span>\n <span class="text"></span>\n</li>').appendTo("#members"),$(".text",b).text(a.name)},f.prototype.operators=function(){var a,b,c,d,e,f;e=["like","not like","starts with","ends with","is","is not",">",">=","<","<=","and","or"],f=[];for(c=0,d=e.length;c<d;c++)b=e[c],a=$('<li data-selector="'+b+'">\n '+b+"\n</li>").appendTo("#operators"),f.push(a.click(__bind(function(a){var b;return $("#syntax").focus(),b=$(a.currentTarget).attr("data-selector"),$("#syntax").val($("#syntax").val()+(" "+b+" ")),this.validateIn()},this)));return f},f.prototype.selectService=function(a){var b,d;return b=$(a).attr("data-id"),d=$(a).attr("data-name"),$("#services li").removeClass("selected"),$(a).addClass("selected"),$("#remove-service-msg").html("Are you sure you want to remove the "+("<strong>"+d+"</strong> service?")),$("#remove-service-form .buttons").fadeIn(200),this.api.get(c,{id:b},__bind(function(a){return this.selectedService=a,this.drawEditor(a)},this))},f.prototype.serviceNode=function(a){var b,c;return b=a.size===1?"system":"systems",c=$('<li data-id="'+a.id+'" data-name="" data-size="'+a.size+'">\n <span class="text">'+a.name+'</span>\n <span class="count">'+a.size+" "+b+"</span>\n</li>").appendTo("#services"),c.attr("data-name",a.name),$(".text",c).text(a.name),c.click(__bind(function(a){return this.selectService(a.currentTarget)},this)),c},f.prototype.findServices=function(){return this.api.get(c,{},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.serviceNode(b));return f},this))},f.prototype.findMembers=function(a){return this.api.get(b,{id:a},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.drawMember(b));return f},this))},f.prototype.findUsers=function(a){return this.api.get(e,{},__bind(function(a){var b;return this.users=function(){var c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],b.system||f.push(b);return f}(),this.drawUsers()},this))},f.prototype.attributeNode=function(a){var b;return b=$('<li data-name=""></li>').appendTo("#attributes"),b.text(a),b.attr("data-name",a),b.click(__bind(function(a){var b;return $("#syntax").focus(),b=$(a.currentTarget).attr("data-name"),$("#syntax").val($("#syntax").val()+(" "+b+" ")),this.validateIn()},this))},f.prototype.findAttributes=function(b){return this.api.get(a,{},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.attributeNode(b));return f},this))},f.prototype.validateIn=function(a){return clearTimeout(this.validateTimeout),this.validateTimeout=setTimeout(__bind(function(){return this.validate()},this),a||500)},f.prototype.validate=function(){var a,c;$("#syntax-status").text(""),c=$("#syntax").data("prev"),a=$("#syntax").val().trim(),$("#syntax").data("prev",a);if(!a||a===c)return;return $("#syntax-status").text("Searching . . ."),$("#members").empty(),this.api.get2(b,a,__bind(function(a){var b,c,d,e,f;if(a.ok){$("#syntax-status").text(""),e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.drawMember(b));return f}return $("#syntax-status").text(a.error)},this))},f.prototype.validateForm=function(){var a,b;return $("#name-error").empty(),$("#unix-users-error").empty(),b=!0,a=$("#name").val().trim(),a===""&&($("#name-error").text("Name is required."),b=!1),this.accounts().length===0&&($("#unix-users-error").text("At least one user account is required."),b=!1),b},f.prototype.accounts=function(){var a,b,c,d,e;a=$("#unix-users").val().split(","),e=[];for(c=0,d=a.length;c<d;c++)b=a[c],b.trim().length>0&&e.push(b.trim());return e},f.prototype.save=function(){var a,b;return this.validateForm()?(b=$("#users :checked").map(function(){return $(this).val()}).get(),a={name:$("#name").val(),code:$("#syntax").val(),accounts:this.accounts(),users:b},$("#id").val().length>0&&(a.id=$("#id").val()),this.api.save(c,a,__bind(function(a){var b;return new Notification("Service saved successfully"),a.size=$("#members").length,$("#id").val(a.id),b=$("#services li[data-id='"+a.id+"']"),b.length===0?(b=this.serviceNode(a),this.selectService(b)):$(".text",b).text(a.name)},this)),!1):!1},f.prototype.drawBlankSlate=function(){return $("#beta").empty(),$('<form id="blank-slate">\n <p>\n Services are dynamically updated groups of systems based on\n criteria you define. Send a command to the service and it runs\n on every system in the group.\n </p>\n <input type="submit" id="blank-slate-add" value="Add Service"/>\n</form>').appendTo("#beta"),this.api.user.permissions.services||$("#blank-slate-add").remove(),$("#blank-slate").submit(__bind(function(){return this.drawEditor(),!1},this))},f.prototype.draw=function(){var a;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","services-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Services <div id="search-services-icon"></div></h2>\n <div id="search-services-form"></div>\n <ul id="services" class="selectable scroll y-fill"></ul>\n <div id="alpha-controls" class="controls">\n <div id="add-service"></div>\n <div id="remove-service"></div>\n </div>\n <form id="remove-service-form" class="overlay" style="display:none;">\n <h2>Remove Service</h2>\n <p id="remove-service-msg">Select a service in the list above to remove.</p>\n <fieldset class="buttons" style="display:none;">\n <input id="remove-service-cancel" type="button" value="Cancel"/>\n <input id="remove-service-ok" type="submit" value="Remove"/>\n </fieldset>\n </form>\n</div>\n<div id="beta" class="primary column x-fill y-fill"></div>\n<div id="charlie" class="sidebar column y-fill">\n <h2>Operators</h2>\n <ul id="operators"></ul>\n <h2>Attributes <div id="search-attributes-icon"></div></h2>\n <div id="search-attributes-form"></div>\n <ul id="attributes" class="y-fill scroll"></ul>\n</div>').appendTo("#container"),new Button("#add-service",ICONS.plus),new Button("#remove-service",ICONS.minus),this.api.user.permissions.services||$("#alpha-controls div").remove(),this.drawBlankSlate(),$("#add-service").click(__bind(function(){return this.drawEditor()},this)),$("#remove-service").click(__bind(function(){return this.toggleForm("#remove-service-form")},this)),$("#remove-service-cancel").click(__bind(function(){return this.toggleForm("#remove-service-form")},this)),$("#remove-service-form").submit(__bind(function(){return this.deleteService()},this)),this.operators(),this.findServices(),this.findAttributes(),this.findUsers(),$("#container").show(),this.layout=this.resize(),a=__bind(function(){return this.layout.resize(),this.layout.resize()},this),new Filter({list:"#services",icon:"#search-services-icon",form:"#search-services-form",attrs:["data-name"],open:a,close:a}),new Filter({list:"#attributes",icon:"#search-attributes-icon",form:"#search-attributes-form",attrs:["data-name"],open:a,close:a})},f.prototype.drawEditor=function(a){if(!this.pageVisible())return;return a||(this.selectedService=null,$("#services li").removeClass("selected")),$("#beta").empty(),$('<form id="editor-form" class="sections y-fill scroll">\n <input id="id" type="hidden"/>\n <div>\n <section>\n <h2>Service</h2>\n <fieldset>\n <label for="name">Name</label>\n <input id="name" type="text"/>\n <p id="name-error" class="error"></p>\n <label for="syntax">Criteria</label>\n <textarea id="syntax" placeholder="fqdn starts with \'www.\' and platform is \'mac_os_x\'"></textarea>\n <p id="syntax-status"></p>\n </fieldset>\n </section>\n <section>\n <h2>Members</h2>\n <fieldset id="service-preview">\n <ul id="members" class="scroll"></ul>\n </fieldset>\n </section>\n <section>\n <h2>Permissions</h2>\n <fieldset>\n <label>Users</label>\n <ul id="users" class="scroll"></ul>\n <label for="unix-users">Unix Accounts</label>\n <input id="unix-users" type="text"/>\n <p id="unix-users-error" class="error"></p>\n <p class="hint">Comma separated user names like: apache, postgres, root, etc.</p>\n </fieldset>\n </section>\n </div>\n</form>\n<form id="editor-buttons">\n <input id="save" type="submit" value="Save"/>\n</form>').appendTo("#beta"),a&&(this.findMembers(a.id),$("#id").val(a.id),$("#name").val(a.name),$("#syntax").val(a.code),$("#unix-users").val(a.accounts.join(", "))),this.users.length>0&&this.drawUsers(),this.layout.resize(),$("#name").focus(),$("#syntax").change(__bind(function(){return this.validateIn()},this)),$("#syntax").keyup(__bind(function(){return this.validateIn()},this)),$("#editor-form").submit(__bind(function(){return this.save()},this)),$("#editor-buttons").submit(__bind(function(){return this.save()},this))},f.prototype.drawUsers=function(){var a,b,c,d,e;if(!this.editorVisible())return;$("#users").empty(),e=this.users;for(c=0,d=e.length;c<d;c++)b=e[c],a=$("<li>\n <input id='user-"+b.jid+"' type='checkbox' value='"+b.jid+"'/>\n <label for='user-"+b.jid+"'>"+b.jid+"</label>\n</li>").appendTo("#users"),b.jid===this.session.bareJid()&&$("input",a).prop("checked",!0);if(this.selectedService)return $('#users input[type="checkbox"]').val(this.selectedService.users)},f.prototype.toggleForm=function(a,b){return a=$(a),$("form.overlay").each(function(){if(this.id!==a.attr("id"))return $(this).hide()}),a.is(":hidden")?(b&&b(),a.fadeIn(100)):a.fadeOut(100,function(){a[0].reset();if(b)return b()})},f.prototype.pageVisible=function(){return $("#services-page").length>0},f.prototype.editorVisible=function(){return $("#services-page #editor-form").length>0},f.prototype.resize=function(){var a,b,c;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),new Layout(function(){return c.css("left",a.width()+b.width())})},f}();var FilesPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};FilesPage=function(){function d(a){this.session=a,this.api=new Api(this.session),this.uploads=new c({session:this.session,jid:this.api.jid,size:this.size,complete:__bind(function(a){return this.findFile(a.name)},this)})}var a,b,c;return a="http://getvines.com/protocol/files",b="http://getvines.com/protocol/files/labels",d.prototype.findLabels=function(){return $("#labels").empty(),this.api.get(b,{},__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.labelNodeList(b));return f},this))},d.prototype.labelNodeList=function(a){var b,c;return c=a.size===1?"file":"files",b=$('<li data-name="">\n <span class="text"></span>\n <span class="count">'+a.size+" "+c+"</span>\n</li>").appendTo("#labels"),$(".text",b).text(a.name),b.attr("data-name",a.name),b.click(__bind(function(a){return this.selectLabel(a)},this))},d.prototype.selectLabel=function(a){var b;return b=$(a.currentTarget).attr("data-name"),$("#labels li").removeClass("selected"),$(a.currentTarget).addClass("selected"),$("#files").empty(),this.findFiles({label:$(a.currentTarget).attr("data-name")})},d.prototype.findFiles=function(b){return this.api.get(a,b,__bind(function(a){var b,c,d,e,f;e=a.rows,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(this.fileNode(b));return f},this))},d.prototype.findFile=function(b){return this.api.get(a,{name:b},__bind(function(a){return this.fileNode(a)},this))},d.prototype.updateFileNode=function(a){var b,c,d;return b=$("#files li[data-id='"+a.id+"']"),c=this.size(a.size),d=this.date(a.created_at),b.data("file",a),$("h2",b).text(a.name),$(".size",b).text(c),$(".time",b).text(d),b.attr("data-name",a.name),b.attr("data-size",c),b.attr("data-created",d)},d.prototype.fileNode=function(a){var b,c,d,e,f,g;if($("#files li[data-id='"+a.id+"']").length>0){this.updateFileNode(a);return}c=$('<li data-id="'+a.id+'">\n <div class="file-icon">\n <span class="size"></span>\n </div>\n <h2></h2>\n <footer>\n <span class="time"></span>\n <ul class="labels"></ul>\n <form class="add-label">\n <div class="add-label-button"></div>\n <input type="text" placeholder="Label" style="display:none;"/>\n </form>\n </footer>\n <form class="file-form">\n <fieldset>\n <input class="cancel" type="submit" value="Delete"/>\n </fieldset>\n </form>\n</li>').appendTo("#files"),new Button($(".file-icon",c).get(0),ICONS.page2,{scale:1,translation:"-2,0","stroke-width":.1,opacity:1}),new Button($(".add-label-button",c).get(0),ICONS.plus,{translation:"-20,-20",scale:.5}),$("form.file-form",c).submit(__bind(function(){return this.deleteFile(c)},this)),$("form.add-label",c).submit(__bind(function(){return this.addLabel(c)},this)),$(".add-label-button",c).click(function(){return $('form.add-label input[type="text"]',c).show()}),this.updateFileNode(a),f=a.labels,g=[];for(d=0,e=f.length;d<e;d++)b=f[d],g.push(this.labelNode(c,b));return g},d.prototype.labelNode=function(a,b){var c,d;return d=$(".labels",a),c=$('<li data-name="">\n <span class="text"></span>\n <div class="remove"></div>\n</li>').appendTo(d),$(".text",c).text(b),c.attr("data-name",b),new Button($(".remove",c).get(0),ICONS.cross,{translation:"-16,-16",scale:.5}),$(".remove",c).click(__bind(function(){return this.removeLabel(a,c)},this))},d.prototype.addLabel=function(b){var c,d,e,f,g,h,i,j,k;d=$('form.add-label input[type="text"]',b),d.hide(),f=function(){var a,b,c,e;c=d.val().split(/,/),e=[];for(a=0,b=c.length;a<b;a++)g=c[a],g&&e.push(g);return e}(),d.val(""),c=b.data("file");for(h=0,j=f.length;h<j;h++)e=f[h],c.labels.push(e);this.api.save(a,c,function(a){});for(i=0,k=f.length;i<k;i++)e=f[i],this.labelNode(b,e),this.updateLabelCount(e,1);return!1},d.prototype.removeLabel=function(b,c){var d,e,f;return d=b.data("file"),f=c.attr("data-name"),d.labels=function(){var a,b,c,g;c=d.labels,g=[];for(a=0,b=c.length;a<b;a++)e=c[a],e!==f&&g.push(e);return g}(),this.api.save(a,d,function(a){}),c.fadeOut(200,function(){return c.remove()}),this.updateLabelCount(f,-1)},d.prototype.updateLabelCount=function(a,b){var c,d,e;return d=$("#labels li[data-name='"+a+"'] .count"),d.length>0?(c=parseInt(d.text().split(" ")[0])+b,e=c===1?"file":"files",d.text(""+c+" "+e)):this.labelNodeList({name:a,size:1})},d.prototype.deleteFile=function(b){return this.api.remove(a,b.attr("data-id"),__bind(function(a){var c,d,e,f;f=b.data("file").labels;for(d=0,e=f.length;d<e;d++)c=f[d],this.updateLabelCount(c,-1);return b.fadeOut(200,function(){return b.remove()})},this)),!1},d.prototype.date=function(a){var b,c;return a=new Date(a),b="Sun Mon Tue Wed Thu Fri Sat".split(" ")[a.getDay()],c="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")[a.getMonth()],""+b+", "+a.getDate()+" "+c+", "+a.getFullYear()+" @ "+a.getHours()+":"+a.getMinutes()},d.prototype.size=function(a){var b,c,d,e;return d=a/1024,e=d/1024,c=e/1024,b=function(a){return a<100?a.toFixed(1).replace(".0",""):Math.round(a)},d<1?""+a+" b":e<1?""+b(d)+" k":c<1?""+b(e)+" m":""+b(c)+" g"},d.prototype.draw=function(){var a,b;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","files-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Labels</h2>\n <ul id="labels" class="selectable scroll y-fill"></ul>\n <div id="alpha-controls" class="controls"></div>\n</div>\n<div id="beta" class="primary column x-fill y-fill">\n <h2 id="files-title">Files <div id="search-files-icon"></div></h2>\n <div id="search-files-form"></div>\n <ul id="files" class="scroll y-fill"></ul>\n</div>\n<div id="charlie" class="sidebar column y-fill">\n <h2>Uploads</h2>\n <div id="upload-dnd" class="float">Drag files here to upload.</div>\n <ul id="uploads" class="scroll y-fill"></ul>\n <div id="charlie-controls" class="controls">\n <form id="file-form">\n <input id="open-file-chooser" type="submit" value="Select files to upload"/>\n <input id="file-chooser" type="file" multiple="true" />\n </form>\n </div>\n</div>').appendTo("#container"),this.api.user.permissions.files?($("#file-chooser").change(__bind(function(a){return this.uploads.queue(a.target.files),$("#file-chooser").val("")},this)),$("#file-form").submit(function(){return $("#file-chooser").click(),!1}),$("#upload-dnd").bind("dragenter",function(a){return a.stopPropagation(),a.preventDefault(),$("#upload-dnd").css("color","#444")}),$("#upload-dnd").bind("dragleave",function(a){return $("#upload-dnd").css("color","#ababab")}),$("#upload-dnd").bind("dragover",function(a){return a.stopPropagation(),a.preventDefault()}),$("#upload-dnd").bind("drop",__bind(function(a){return a.stopPropagation(),a.preventDefault(),$("#upload-dnd").css("color","#ababab"),this.uploads.queue(a.originalEvent.dataTransfer.files)},this))):($("#upload-dnd").text("Your account cannot upload files."),$("#file-form").remove()),this.findLabels(),this.findFiles(),$("#container").show(),b=this.resize(),a=function(){return b.resize(),b.resize()},new Filter({list:"#files",icon:"#search-files-icon",form:"#search-files-form",attrs:["data-name","data-created"],open:a,close:a})},d.prototype.resize=function(){var a,b,c,d,e;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),e=$("#uploads"),d=$("#upload-dnd"),new Layout(function(){return c.css("left",a.width()+b.width()),d.height(e.height()),d.css("line-height",e.height()+"px")})},c=function(){function a(a){this.session=a.session,this.serviceJid=a.jid,this.size=a.size,this.complete=a.complete,this.uploads=[],this.sending=null}return a.prototype.queue=function(a){var b,c,d;for(c=0,d=a.length;c<d;c++)b=a[c],this.find(b)||this.add(b);return this.process()},a.prototype.add=function(a){var b,c;return c=this.node(a),b=$(".meter",c),this.uploads.push(new Transfer({to:this.serviceJid(),file:a,session:this.session,progress:function(a){return b.css("width",a+"%")},complete:__bind(function(){return this.remove(a),this.complete&&this.complete(a),a.name===this.sending.name&&(this.sending=null),this.process()},this)}))},a.prototype.process=function(){var a;if(this.sending)return;return(a=this.uploads[0])?(this.sending=a.file,a.start()):this.sending=null},a.prototype.find=function(a){var b;return function(){var c,d,e,f;e=this.uploads,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],b.file.name===a.name&&f.push(b);return f}.call(this).shift()},a.prototype.remove=function(a){var b,c;return this.uploads=function(){var b,d,e,f;e=this.uploads,f=[];for(b=0,d=e.length;b<d;b++)c=e[b],c.file.name!==a.name&&f.push(c);return f}.call(this),b=$("#uploads li[data-file='"+a.name+"']"),b.fadeOut(200,function(){return b.remove()})},a.prototype.node=function(a){var b;return b=$('<li data-file="" style="display:none;">\n <form class="inset">\n <h2></h2>\n <div class="progress">\n <div class="meter"></div>\n <span class="text">'+this.size(a.size)+'</span>\n <div class="cancel"></div>\n </div>\n </form>\n</li>').appendTo("#uploads"),b.fadeIn(200),$("h2",b).text(a.name),b.attr("data-file",a.name),new Button($(".cancel",b).get(0),ICONS.cross,{translation:"-16,-16",scale:.5}),$(".cancel",b).click(__bind(function(){return this.cancel(a)},this)),b},a.prototype.cancel=function(a){var b;if(b=this.find(a))return b.stop()},a}(),d}();var SetupPage,__bind=function(a,b){return function(){return a.apply(b,arguments)}};SetupPage=function(){function d(a){this.session=a,this.api=new Api(this.session),this.layout=null,this.selected=null,this.services=[],this.users=[]}var a,b,c;return a="http://getvines.com/protocol/services",b="http://getvines.com/protocol/systems",c="http://getvines.com/protocol/users",d.prototype.findSystem=function(a){return this.api.get(b,{name:a},__bind(function(a){return this.drawSystemInfo(a)},this))},d.prototype.findServices=function(){return this.api.get(a,{},__bind(function(a){return this.services=a.rows,this.drawServices()},this))},d.prototype.drawServices=function(){var a,b,c,d,e;if($("#setup-page #services").length===0)return;$("#services").empty(),e=this.services;for(c=0,d=e.length;c<d;c++)b=e[c],a=$("<li>\n <input id='service-"+b.id+"' type='checkbox' value='"+b.id+"'/>\n <label for='service-"+b.id+"'></label>\n</li>").appendTo("#services"),$("label",a).text(b.name);this.selected&&$('#services input[type="checkbox"]').val(this.selected.services);if(this.selected&&!this.api.user.permissions.services)return $('#services input[type="checkbox"]').prop("disabled",!0)},d.prototype.findUsers=function(){return this.api.get(c,{},__bind(function(a){return this.users=a.rows,this.drawUsers()},this))},d.prototype.drawUsers=function(){var a,b,c,d,e,f;if($("#setup-page #users").length===0)return;$("#users").empty(),a=$("#systems-nav").hasClass("selected"),e=this.users,f=[];for(c=0,d=e.length;c<d;c++)b=e[c],f.push(b.system===a?this.userNode(b):void 0);return f},d.prototype.userNode=function(a){var b,c;return c=$('<li data-name="" data-jid="'+a.jid+'" id="'+a.jid+'">\n <span class="text"></span>\n <span class="jid">'+a.jid+"</span>\n</li>").appendTo("#users"),b=this.userName(a),$(".text",c).text(b),c.attr("data-name",b),c.click(__bind(function(a){return this.selectUser(a.currentTarget)},this)),c},d.prototype.userName=function(a){return a.name||a.jid.split("@")[0]},d.prototype.selectUser=function(a){var b,d;return b=$(a).attr("data-jid"),d=$(a).attr("data-name"),$("#users li").removeClass("selected"),$(a).addClass("selected"),$("#remove-user-msg").html("Are you sure you want to remove "+("<strong>"+d+"</strong>?")),$("#remove-user-form .buttons").fadeIn(200),this.api.get(c,{jid:b},__bind(function(a){return this.selected=a,a.system?this.drawSystemEditor(a):this.drawUserEditor(a)},this))},d.prototype.removeUser=function(){var a;return this.toggleForm("#remove-user-form"),a=$("#users li[data-jid='"+this.selected.jid+"']"),this.api.remove(c,this.selected.jid,__bind(function(b){return a.fadeOut(200,__bind(function(){var b;return this.users=function(){var a,c,d,e;d=this.users,e=[];for(a=0,c=d.length;a<c;a++)b=d[a],b.jid!==this.selected.jid&&e.push(b);return e}.call(this),a.remove(),this.selected=null,$("#users-nav").hasClass("selected")?this.drawUserBlankSlate
2
+ ():this.drawSystemBlankSlate()},this))},this))},d.prototype.selectTask=function(a){this.selected=null,$("#setup li").removeClass("selected secondary"),$(a.currentTarget).addClass("selected secondary"),$("form.overlay").fadeOut(100,__bind(function(){return this.layout.resize()},this));switch($(a.currentTarget).attr("id")){case"users-nav":return $("#beta-header").text("Users"),$("#remove-user-form h2").text("Remove User"),$("#remove-user-msg").html("Select a user to delete."),$("#remove-user-form .buttons").hide(),this.drawUsers(),this.drawUserBlankSlate(),this.toggleBetaControls(this.api.user.permissions.users);case"systems-nav":return $("#beta-header").text("Systems"),$("#remove-user-form h2").text("Remove System"),$("#remove-user-msg").html("Select a system to delete."),$("#remove-user-form .buttons").hide(),this.drawUsers(),this.drawSystemBlankSlate(),this.toggleBetaControls(this.api.user.permissions.systems)}},d.prototype.toggleBetaControls=function(a){return a?$("#beta-controls div").show():$("#beta-controls div").hide()},d.prototype.toggleForm=function(a,b){return a=$(a),$("form.overlay").each(function(){if(this.id!==a.attr("id"))return $(this).hide()}),a.is(":hidden")?(b&&b(),a.fadeIn(100)):a.fadeOut(100,__bind(function(){a[0].reset(),this.layout.resize();if(b)return b()},this))},d.prototype.validateUser=function(){var a,b,c,d;$("#user-name-error").empty(),$("#password-error").empty(),d=!0,b=$("#password1").val().trim(),c=$("#password2").val().trim();if(this.selected)c.length>0&&c.length<8&&($("#password-error").text("Password must be at least 8 characters."),d=!1),this.session.bareJid()!==this.selected.jid&&b!==c&&($("#password-error").text("Passwords must match."),d=!1);else{a=$("#user-name").val().trim(),a===""&&($("#user-name-error").text("User name is required."),d=!1),a.match(/[\s"&'\/:<>@]/)&&($("#user-name-error").text("User name contains forbidden characters."),d=!1);if(b.length===0||c.length===0)$("#password-error").text("Password is required."),d=!1;b!==c&&($("#password-error").text("Passwords must match."),d=!1),c.length<8&&($("#password-error").text("Password must be at least 8 characters."),d=!1)}return d},d.prototype.saveUser=function(){var a;return this.validateUser()?(a={jid:$("#jid").val(),username:$("#user-name").val(),name:$("#name").val(),password1:$("#password1").val(),password2:$("#password2").val(),services:$("#services :checked").map(function(){return $(this).val()}).get(),permissions:{systems:$("#perm-systems").prop("checked"),services:$("#perm-services").prop("checked"),files:$("#perm-files").prop("checked"),users:$("#perm-users").prop("checked")}},this.api.save(c,a,__bind(function(a){var b,c,d;return new Notification("User saved successfully"),$("#jid").val(a.jid),b=$("#users li[data-jid='"+a.jid+"']"),b.length===0?(this.users.push(a),b=this.userNode(a),this.selectUser(b)):(c=function(){var b,c,e,f;e=this.users,f=[];for(b=0,c=e.length;b<c;b++)d=e[b],d.jid===a.jid&&f.push(d);return f}.call(this)[0],c.name=this.userName(a),$(".text",b).text(this.userName(a)))},this)),!1):!1},d.prototype.validateSystem=function(){var a,b;return $("#user-name-error").empty(),b=!0,a=$("#user-name").val().trim(),this.selected||(a===""&&($("#user-name-error").text("Hostname is required."),b=!1),a.match(/[\s"&'\/:<>@]/)&&($("#user-name-error").text("Hostname contains forbidden characters."),b=!1)),b},d.prototype.saveSystem=function(){var a;return this.validateSystem()?(a={jid:$("#jid").val(),username:$("#user-name").val(),password1:$("#password1").val(),password2:$("#password1").val(),system:!0},this.api.save(c,a,__bind(function(a){var b;return new Notification("System saved successfully"),$("#jid").val(a.jid),b=$("#users li[data-jid='"+a.jid+"']"),b.length===0?(this.users.push(a),b=this.userNode(a),this.selectUser(b)):(this.selected.name=a.name,$(".text",b).text(a.name))},this)),!1):!1},d.prototype.rand=function(){return Math.floor(Math.random()*16)},d.prototype.token=function(){var a;return function(){var b;b=[];for(a=0;a<=127;a++)b.push(this.rand().toString(16));return b}.call(this).join("")},d.prototype.drawUserBlankSlate=function(){var a;return $("#charlie").empty(),a=this.api.user.permissions.users?"Select a user account to update or add a new user.":"Select a user account to update.",$('<form id="blank-slate">\n <p>'+a+'</p>\n <input type="submit" id="blank-slate-add" value="Add User"/>\n</form>').appendTo("#charlie"),this.api.user.permissions.users||$("#blank-slate-add").remove(),$("#blank-slate").submit(__bind(function(){return this.drawUserEditor(),!1},this))},d.prototype.drawSystemBlankSlate=function(){return $("#charlie").empty(),$('<form id="blank-slate">\n <p>\n Systems need a user account before they can connect and\n authenticate with the chat server.\n </p>\n <input type="submit" id="blank-slate-add" value="Add System"/>\n</form>').appendTo("#charlie"),this.api.user.permissions.systems||$("#blank-slate-add").remove(),$("#blank-slate").submit(__bind(function(){return this.drawSystemEditor(),!1},this))},d.prototype.draw=function(){var a;if(!this.session.connected()){window.location.hash="";return}return $("body").attr("id","setup-page"),$("#container").hide().empty(),$('<div id="alpha" class="sidebar column y-fill">\n <h2>Setup</h2>\n <ul id="setup" class="selectable scroll y-fill">\n <li id="users-nav" class=\'selected secondary\'>\n <span class="text">Users</span>\n </li>\n <li id="systems-nav">\n <span class="text">Systems</span>\n </li>\n </ul>\n <div id="alpha-controls" class="controls"></div>\n</div>\n<div id="beta" class="sidebar column y-fill">\n <h2><span id="beta-header">Users</span> <div id="search-users-icon"></div></h2>\n <div id="search-users-form"></div>\n <ul id="users" class="selectable scroll y-fill"></ul>\n <form id="remove-user-form" class="overlay" style="display:none;">\n <h2>Remove User</h2>\n <p id="remove-user-msg">Select a user to delete.</p>\n <fieldset class="buttons" style="display:none;">\n <input id="remove-user-cancel" type="button" value="Cancel"/>\n <input id="remove-user-ok" type="submit" value="Remove"/>\n </fieldset>\n </form>\n <div id="beta-controls" class="controls">\n <div id="add-user"></div>\n <div id="remove-user"></div>\n </div>\n</div>\n<div id="charlie" class="primary column x-fill y-fill"></div>').appendTo("#container"),this.drawUserBlankSlate(),$("#setup li").click(__bind(function(a){return this.selectTask(a)},this)),this.findUsers(),this.findServices(),$("#container").show(),this.layout=this.resize(),new Button("#add-user",ICONS.plus),new Button("#remove-user",ICONS.minus),this.api.user.permissions.users||$("#beta-controls div").hide(),this.api.user.permissions.systems||$("#systems-nav").hide(),$("#add-user").click(__bind(function(){return $("#users-nav").hasClass("selected")?this.drawUserEditor():this.drawSystemEditor()},this)),$("#remove-user").click(__bind(function(){return this.toggleForm("#remove-user-form")},this)),$("#remove-user-cancel").click(__bind(function(){return this.toggleForm("#remove-user-form")},this)),$("#remove-user-form").submit(__bind(function(){return this.removeUser(),!1},this)),a=__bind(function(){return this.layout.resize(),this.layout.resize()},this),new Filter({list:"#users",icon:"#search-users-icon",form:"#search-users-form",attrs:["data-jid","data-name"],open:a,close:a})},d.prototype.drawUserEditor=function(a){var b,c,d,e;a||(this.selected=null,$("#users li").removeClass("selected")),$("#charlie").empty(),$('<form id="editor-form" class="sections y-fill scroll">\n <div>\n <section>\n <h2>User</h2>\n <fieldset id="jid-fields">\n <input id="jid" type="hidden" value=""/>\n <label for="name">Real Name</label>\n <input id="name" type="text" maxlength="1024"/>\n </fieldset>\n </section>\n <section>\n <h2>Password</h2>\n <fieldset>\n <label id="password1-label" for="password1">Current Password</label>\n <input id="password1" type="password" maxlength="1024"/>\n <label id="password2-label" for="password2">New Password</label>\n <input id="password2" type="password" maxlength="1024"/>\n <p id="password-error" class="error"></p>\n </fieldset>\n </section>\n <section>\n <h2>Permissions</h2>\n <fieldset>\n <label>Manage</label>\n <ul id="permissions">\n <li>\n <input id="perm-systems" type="checkbox" value="systems"/>\n <label for="perm-systems">Systems</label>\n </li>\n <li>\n <input id="perm-services" type="checkbox" value="services"/>\n <label for="perm-services">Services</label>\n </li>\n <li>\n <input id="perm-users" type="checkbox" value="users"/>\n <label for="perm-users">Users</label>\n </li>\n <li>\n <input id="perm-files" type="checkbox" value="files"/>\n <label for="perm-files">Files</label>\n </li>\n </ul>\n </fieldset>\n </section>\n <section>\n <h2>Services</h2>\n <fieldset>\n <label>Access To</label>\n <ul id="services" class="scroll"></ul>\n </fieldset>\n </section>\n </div>\n</form>\n<form id="editor-buttons">\n <input id="save" type="submit" value="Save"/>\n</form>').appendTo("#charlie");if(a){$("<label>Account Name</label>\n<p>"+a.jid+"</p>").prependTo("#jid-fields"),$("#name").focus(),this.session.bareJid()!==a.jid&&($("#password1-label").text("Password"),$("#password2-label").text("Password Again")),$("#jid").val(a.jid),$("#name").val(a.name),$("#user-name").val(a.jid.split("@")[0]),e="services systems files users".split(" ");for(c=0,d=e.length;c<d;c++)b=e[c],a.permissions[b]&&$("#perm-"+b).prop("checked",!0),this.session.bareJid()===a.jid&&$("#perm-"+b).prop("disabled",!0)}else $('<label for="user-name">User Name</label>\n<input id="user-name" type="text" maxlength="1023"/>\n<p id="user-name-error" class="error"></p>').prependTo("#jid-fields"),$("#password1-label").text("Password"),$("#password2-label").text("Password Again"),$("#user-name").focus();return this.services.length>0&&this.drawServices(),this.layout.resize(),$("#editor-form").submit(__bind(function(){return this.saveUser()},this)),$("#editor-buttons").submit(__bind(function(){return this.saveUser()},this))},d.prototype.drawSystemEditor=function(a){return a||(this.selected=null,$("#users li").removeClass("selected")),$("#charlie").empty(),$('<form id="editor-form" class="sections y-fill scroll">\n <div>\n <section>\n <h2>System</h2>\n <fieldset id="jid-fields">\n <input id="jid" type="hidden" value=""/>\n <label id="password1-label" for="password1">Authentication Token</label>\n <div id="token-container">\n <input id="password1" type="text" readonly placeholder="Press Generate to create a new token"/>\n <input id="new-token" type="button" value="Generate"/>\n </div>\n </fieldset>\n </section>\n <section id="info">\n <h2>Info</h2>\n <fieldset>\n <label>Platform</label>\n <p id="info-platform">-</p>\n <label>Hostname</label>\n <p id="info-fqdn">-</p>\n <label>IP Address</label>\n <p id="info-ip">-</p>\n <label>MAC Address</label>\n <p id="info-mac">-</p>\n </fieldset>\n </section>\n </div>\n</form>\n<form id="editor-buttons">\n <input id="save" type="submit" value="Save"/>\n</form>').appendTo("#charlie"),$("#new-token").click(__bind(function(){return $("#password1").val(this.token())},this)),a&&this.findSystem(a.jid.split("@")[0]),a?($("<label>Account Name</label>\n<p>"+a.jid+"</p>").prependTo("#jid-fields"),$("#jid").val(a.jid),$("#user-name").val(a.jid.split("@")[0])):($('<label for="user-name">Hostname</label>\n<input id="user-name" type="text" maxlength="1023"/>\n<p id="user-name-error" class="error"></p>').prependTo("#jid-fields"),$("#user-name").focus(),$("#password1").val(this.token())),this.layout.resize(),$("#editor-form").submit(__bind(function(){return this.saveSystem()},this)),$("#editor-buttons").submit(__bind(function(){return this.saveSystem()},this))},d.prototype.drawSystemInfo=function(a){return $("#info-platform").text(a.platform),$("#info-fqdn").text(a.fqdn),$("#info-ip").text(a.ipaddress),$("#info-mac").text(a.macaddress)},d.prototype.resize=function(){var a,b,c;return a=$("#alpha"),b=$("#beta"),c=$("#charlie"),new Layout(function(){return c.css("left",a.outerWidth()+b.outerWidth())})},d}(),$(function(){var a,b,c,d,e,f;f=new Session,d=new NavBar(f),d.draw(),a={Systems:ICONS.commandline,Services:ICONS.magic,Files:ICONS.page2,Setup:ICONS.gear2,Logout:ICONS.power};for(c in a)b=a[c],d.addButton(c,b);return e={"/systems":new SystemsPage(f),"/services":new ServicesPage(f),"/files":new FilesPage(f),"/setup":new SetupPage(f),"/logout":new LogoutPage(f),"default":new LoginPage(f,"/systems/")},(new Router(e)).draw(),d.select($("#nav-link-systems").parent())})
@@ -90,12 +90,12 @@ FilesPage = (function() {
90
90
  node = $("<li data-id=\"" + file.id + "\">\n <div class=\"file-icon\">\n <span class=\"size\"></span>\n </div>\n <h2></h2>\n <footer>\n <span class=\"time\"></span>\n <ul class=\"labels\"></ul>\n <form class=\"add-label\">\n <div class=\"add-label-button\"></div>\n <input type=\"text\" placeholder=\"Label\" style=\"display:none;\"/>\n </form>\n </footer>\n <form class=\"file-form\">\n <fieldset>\n <input class=\"cancel\" type=\"submit\" value=\"Delete\"/>\n </fieldset>\n </form>\n</li>").appendTo('#files');
91
91
  new Button($('.file-icon', node).get(0), ICONS.page2, {
92
92
  scale: 1.0,
93
- translation: '-2 0',
93
+ translation: '-2,0',
94
94
  'stroke-width': 0.1,
95
95
  opacity: 1.0
96
96
  });
97
97
  new Button($('.add-label-button', node).get(0), ICONS.plus, {
98
- translation: '-10 -10',
98
+ translation: '-20,-20',
99
99
  scale: 0.5
100
100
  });
101
101
  $('form.file-form', node).submit(__bind(function() {
@@ -123,7 +123,7 @@ FilesPage = (function() {
123
123
  $('.text', item).text(label);
124
124
  item.attr('data-name', label);
125
125
  new Button($('.remove', item).get(0), ICONS.cross, {
126
- translation: '-8 -8',
126
+ translation: '-16,-16',
127
127
  scale: 0.5
128
128
  });
129
129
  return $('.remove', item).click(__bind(function() {
@@ -404,7 +404,7 @@ FilesPage = (function() {
404
404
  $('h2', node).text(file.name);
405
405
  node.attr('data-file', file.name);
406
406
  new Button($('.cancel', node).get(0), ICONS.cross, {
407
- translation: '-8 -8',
407
+ translation: '-16,-16',
408
408
  scale: 0.5
409
409
  });
410
410
  $('.cancel', node).click(__bind(function() {
@@ -103,7 +103,7 @@ SetupPage = (function() {
103
103
  var selected;
104
104
  this.toggleForm('#remove-user-form');
105
105
  selected = $("#users li[data-jid='" + this.selected.jid + "']");
106
- this.api.remove(USERS, this.selected.jid, __bind(function(result) {
106
+ return this.api.remove(USERS, this.selected.jid, __bind(function(result) {
107
107
  return selected.fadeOut(200, __bind(function() {
108
108
  var u;
109
109
  this.users = (function() {
@@ -127,32 +127,38 @@ SetupPage = (function() {
127
127
  }
128
128
  }, this));
129
129
  }, this));
130
- return false;
131
130
  };
132
131
  SetupPage.prototype.selectTask = function(event) {
133
132
  this.selected = null;
134
133
  $('#setup li').removeClass('selected secondary');
135
134
  $(event.currentTarget).addClass('selected secondary');
135
+ $('form.overlay').fadeOut(100, __bind(function() {
136
+ return this.layout.resize();
137
+ }, this));
136
138
  switch ($(event.currentTarget).attr('id')) {
137
139
  case 'users-nav':
138
140
  $('#beta-header').text('Users');
141
+ $('#remove-user-form h2').text('Remove User');
142
+ $('#remove-user-msg').html("Select a user to delete.");
143
+ $('#remove-user-form .buttons').hide();
139
144
  this.drawUsers();
140
145
  this.drawUserBlankSlate();
141
- if (this.api.user.permissions.users) {
142
- return $('#beta-controls div').show();
143
- } else {
144
- return $('#beta-controls div').hide();
145
- }
146
- break;
146
+ return this.toggleBetaControls(this.api.user.permissions.users);
147
147
  case 'systems-nav':
148
148
  $('#beta-header').text('Systems');
149
+ $('#remove-user-form h2').text('Remove System');
150
+ $('#remove-user-msg').html("Select a system to delete.");
151
+ $('#remove-user-form .buttons').hide();
149
152
  this.drawUsers();
150
153
  this.drawSystemBlankSlate();
151
- if (this.api.user.permissions.systems) {
152
- return $('#beta-controls div').show();
153
- } else {
154
- return $('#beta-controls div').hide();
155
- }
154
+ return this.toggleBetaControls(this.api.user.permissions.systems);
155
+ }
156
+ };
157
+ SetupPage.prototype.toggleBetaControls = function(show) {
158
+ if (show) {
159
+ return $('#beta-controls div').show();
160
+ } else {
161
+ return $('#beta-controls div').hide();
156
162
  }
157
163
  };
158
164
  SetupPage.prototype.toggleForm = function(form, fn) {
@@ -168,12 +174,13 @@ SetupPage = (function() {
168
174
  }
169
175
  return form.fadeIn(100);
170
176
  } else {
171
- return form.fadeOut(100, function() {
177
+ return form.fadeOut(100, __bind(function() {
172
178
  form[0].reset();
179
+ this.layout.resize();
173
180
  if (fn) {
174
181
  return fn();
175
182
  }
176
- });
183
+ }, this));
177
184
  }
178
185
  };
179
186
  SetupPage.prototype.validateUser = function() {
@@ -181,9 +188,8 @@ SetupPage = (function() {
181
188
  $('#user-name-error').empty();
182
189
  $('#password-error').empty();
183
190
  valid = true;
184
- node = $.trim($('#user-name').val());
185
- password1 = $.trim($('#password1').val());
186
- password2 = $.trim($('#password2').val());
191
+ password1 = $('#password1').val().trim();
192
+ password2 = $('#password2').val().trim();
187
193
  if (this.selected) {
188
194
  if (password2.length > 0 && password2.length < 8) {
189
195
  $('#password-error').text('Password must be at least 8 characters.');
@@ -196,6 +202,7 @@ SetupPage = (function() {
196
202
  }
197
203
  }
198
204
  } else {
205
+ node = $('#user-name').val().trim();
199
206
  if (node === '') {
200
207
  $('#user-name-error').text('User name is required.');
201
208
  valid = false;
@@ -241,7 +248,7 @@ SetupPage = (function() {
241
248
  }
242
249
  };
243
250
  this.api.save(USERS, user, __bind(function(result) {
244
- var node;
251
+ var node, selected, u;
245
252
  new Notification('User saved successfully');
246
253
  $('#jid').val(result.jid);
247
254
  node = $("#users li[data-jid='" + result.jid + "']");
@@ -250,6 +257,19 @@ SetupPage = (function() {
250
257
  node = this.userNode(result);
251
258
  return this.selectUser(node);
252
259
  } else {
260
+ selected = ((function() {
261
+ var _i, _len, _ref, _results;
262
+ _ref = this.users;
263
+ _results = [];
264
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
265
+ u = _ref[_i];
266
+ if (u.jid === result.jid) {
267
+ _results.push(u);
268
+ }
269
+ }
270
+ return _results;
271
+ }).call(this))[0];
272
+ selected.name = this.userName(result);
253
273
  return $('.text', node).text(this.userName(result));
254
274
  }
255
275
  }, this));
@@ -259,7 +279,7 @@ SetupPage = (function() {
259
279
  var node, valid;
260
280
  $('#user-name-error').empty();
261
281
  valid = true;
262
- node = $.trim($('#user-name').val());
282
+ node = $('#user-name').val().trim();
263
283
  if (!this.selected) {
264
284
  if (node === '') {
265
285
  $('#user-name-error').text('Hostname is required.');
@@ -294,6 +314,7 @@ SetupPage = (function() {
294
314
  node = this.userNode(result);
295
315
  return this.selectUser(node);
296
316
  } else {
317
+ this.selected.name = result.name;
297
318
  return $('.text', node).text(result.name);
298
319
  }
299
320
  }, this));
@@ -376,7 +397,8 @@ SetupPage = (function() {
376
397
  return this.toggleForm('#remove-user-form');
377
398
  }, this));
378
399
  $('#remove-user-form').submit(__bind(function() {
379
- return this.removeUser();
400
+ this.removeUser();
401
+ return false;
380
402
  }, this));
381
403
  fn = __bind(function() {
382
404
  this.layout.resize();
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vines-services
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-28 00:00:00.000000000Z
12
+ date: 2012-01-30 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bcrypt-ruby
16
- requirement: &70104009216940 !ruby/object:Gem::Requirement
16
+ requirement: &70102931276860 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,21 +21,21 @@ dependencies:
21
21
  version: 3.0.1
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70104009216940
24
+ version_requirements: *70102931276860
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: blather
27
- requirement: &70104009216460 !ruby/object:Gem::Requirement
27
+ requirement: &70102931276300 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
31
31
  - !ruby/object:Gem::Version
32
- version: 0.5.8
32
+ version: 0.5.12
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70104009216460
35
+ version_requirements: *70102931276300
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: citrus
38
- requirement: &70104009216000 !ruby/object:Gem::Requirement
38
+ requirement: &70102931275840 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.4.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70104009216000
46
+ version_requirements: *70102931275840
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: couchrest_model
49
- requirement: &70104009215540 !ruby/object:Gem::Requirement
49
+ requirement: &70102931275380 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,43 +54,43 @@ dependencies:
54
54
  version: 1.1.2
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70104009215540
57
+ version_requirements: *70102931275380
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: em-http-request
60
- requirement: &70104009215080 !ruby/object:Gem::Requirement
60
+ requirement: &70102931274920 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
64
64
  - !ruby/object:Gem::Version
65
- version: 0.3.0
65
+ version: 1.0.1
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70104009215080
68
+ version_requirements: *70102931274920
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: sqlite3
71
- requirement: &70104009214620 !ruby/object:Gem::Requirement
71
+ requirement: &70102931274460 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
75
75
  - !ruby/object:Gem::Version
76
- version: 1.3.4
76
+ version: 1.3.5
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *70104009214620
79
+ version_requirements: *70102931274460
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: vines
82
- requirement: &70104009214160 !ruby/object:Gem::Requirement
82
+ requirement: &70102931274000 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
- - - ~>
85
+ - - ! '>='
86
86
  - !ruby/object:Gem::Version
87
- version: '0.3'
87
+ version: 0.4.0
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *70104009214160
90
+ version_requirements: *70102931274000
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: minitest
93
- requirement: &70104009213780 !ruby/object:Gem::Requirement
93
+ requirement: &70102931273600 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70104009213780
101
+ version_requirements: *70102931273600
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rake
104
- requirement: &70104009213360 !ruby/object:Gem::Requirement
104
+ requirement: &70102931273160 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,7 +109,7 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *70104009213360
112
+ version_requirements: *70102931273160
113
113
  description: ! 'Vines Services are dynamically updated groups of systems based
114
114
 
115
115
  on criteria like hostname, installed software, operating system, etc. Send a
@@ -201,7 +201,7 @@ files:
201
201
  - test/storage/couchdb_test.rb
202
202
  - test/vql/compiler_test.rb
203
203
  - test/vql/vql_test.rb
204
- homepage: http://www.getvines.com
204
+ homepage: http://www.getvines.org
205
205
  licenses: []
206
206
  post_install_message:
207
207
  rdoc_options: []