wackamole 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.bnsignore +16 -0
- data/.gitignore +2 -0
- data/History.txt +4 -0
- data/README.rdoc +88 -0
- data/Rakefile +42 -0
- data/aa.rb +25 -0
- data/aaa.txt +13 -0
- data/bin/wackamole +77 -0
- data/lib/app.rb +63 -0
- data/lib/controllers/dashboard.rb +30 -0
- data/lib/controllers/features.rb +41 -0
- data/lib/controllers/logs.rb +49 -0
- data/lib/controllers/mission.rb +27 -0
- data/lib/controllers/users.rb +41 -0
- data/lib/helpers/dashboard_helper.rb +32 -0
- data/lib/helpers/features_helper.rb +13 -0
- data/lib/helpers/flash_helper.rb +16 -0
- data/lib/helpers/logs_helper.rb +116 -0
- data/lib/helpers/main_helper.rb +182 -0
- data/lib/helpers/mission_helper.rb +59 -0
- data/lib/helpers/rails_helper.rb +7 -0
- data/lib/wackamole.rb +47 -0
- data/lib/wackamole/core_ext/date_time.rb +8 -0
- data/lib/wackamole/core_ext/time.rb +9 -0
- data/lib/wackamole/models/control.rb +132 -0
- data/lib/wackamole/models/feature.rb +74 -0
- data/lib/wackamole/models/log.rb +45 -0
- data/lib/wackamole/models/mission.rb +164 -0
- data/lib/wackamole/models/mole_info.rb +94 -0
- data/lib/wackamole/models/search_filter.rb +192 -0
- data/lib/wackamole/models/user.rb +46 -0
- data/public/favicon.ico +0 -0
- data/public/images/.DS_Store +0 -0
- data/public/images/browsers/.DS_Store +0 -0
- data/public/images/browsers/MSIE 6.png +0 -0
- data/public/images/browsers/MSIE 8.0 +0 -0
- data/public/images/browsers/MSIE 8.0.png +0 -0
- data/public/images/browsers/chrome.png +0 -0
- data/public/images/browsers/firefox.png +0 -0
- data/public/images/browsers/ie 6.0.png +0 -0
- data/public/images/browsers/ie6.jpg +0 -0
- data/public/images/browsers/ie7.jpg +0 -0
- data/public/images/browsers/ie7.png +0 -0
- data/public/images/browsers/msie 6.0.png +0 -0
- data/public/images/browsers/msie 7.0.png +0 -0
- data/public/images/browsers/opera.jpg +0 -0
- data/public/images/browsers/opera.png +0 -0
- data/public/images/browsers/safari.jpg +0 -0
- data/public/images/browsers/safari.png +0 -0
- data/public/images/browsers/unknown_browser.png +0 -0
- data/public/images/close.png +0 -0
- data/public/images/error.png +0 -0
- data/public/images/fade.png +0 -0
- data/public/images/fault_big.png +0 -0
- data/public/images/fault_small.png +0 -0
- data/public/images/feature.png +0 -0
- data/public/images/h300.png +0 -0
- data/public/images/hori_large.png +0 -0
- data/public/images/info_big.png +0 -0
- data/public/images/info_small.png +0 -0
- data/public/images/loading.gif +0 -0
- data/public/images/loading1.gif +0 -0
- data/public/images/mole_error.png +0 -0
- data/public/images/more.gif +0 -0
- data/public/images/next.png +0 -0
- data/public/images/perf_big.png +0 -0
- data/public/images/perf_small.png +0 -0
- data/public/images/powered_by.png +0 -0
- data/public/images/prev.png +0 -0
- data/public/images/row_fade.png +0 -0
- data/public/images/search.png +0 -0
- data/public/images/small_logo.png +0 -0
- data/public/images/spaceball.gif +0 -0
- data/public/images/tick.png +0 -0
- data/public/images/users.png +0 -0
- data/public/images/wackamole_logo.png +0 -0
- data/public/javascripts/.DS_Store +0 -0
- data/public/javascripts/application.js +28 -0
- data/public/javascripts/g.dot.min.js +7 -0
- data/public/javascripts/g.raphael.min.js +7 -0
- data/public/javascripts/jit.js +9052 -0
- data/public/javascripts/jit.min.js +1 -0
- data/public/javascripts/jquery-ui.js +188 -0
- data/public/javascripts/jquery.example.js +160 -0
- data/public/javascripts/jquery.js +19 -0
- data/public/javascripts/jquery.tools.min.js +38 -0
- data/public/javascripts/jquery_ba-url.js +9 -0
- data/public/javascripts/jquery_min.js +19 -0
- data/public/javascripts/jquery_ui_min.js +298 -0
- data/public/javascripts/raphael.min.js +7 -0
- data/public/stylesheets/tabs-slideshow.css +95 -0
- data/public/stylesheets/wackamole.css +684 -0
- data/spec/config/bogus_test.yml +3 -0
- data/spec/config/test.yml +3 -0
- data/spec/core_ext/date_time_spec.rb +8 -0
- data/spec/core_ext/time_spec.rb +12 -0
- data/spec/data/fixtures.rb +92 -0
- data/spec/models/control_spec.rb +98 -0
- data/spec/models/feature_spec.rb +52 -0
- data/spec/models/log_spec.rb +43 -0
- data/spec/models/mission_spec.rb +225 -0
- data/spec/models/moled_info_spec.rb +30 -0
- data/spec/models/search_filter_spec.rb +151 -0
- data/spec/models/user_spec.rb +44 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/wackamole_spec.rb +20 -0
- data/tasks/bones.rake +20 -0
- data/tasks/fixtures.rake +13 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +32 -0
- data/tasks/rdoc.rake +54 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +290 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +46 -0
- data/views/dashboard/_report.erb +118 -0
- data/views/dashboard/index.erb +4 -0
- data/views/dashboard/refresh_js.erb +3 -0
- data/views/features/_rows.erb +26 -0
- data/views/features/filter.js.erb +2 -0
- data/views/features/index.erb +24 -0
- data/views/features/index.js.erb +2 -0
- data/views/layout.erb +47 -0
- data/views/logs/_array.erb +12 -0
- data/views/logs/_hash.erb +14 -0
- data/views/logs/_rows.erb +65 -0
- data/views/logs/filter.js.erb +4 -0
- data/views/logs/index.erb +13 -0
- data/views/logs/index.js.erb +2 -0
- data/views/logs/show.erb +66 -0
- data/views/mission/_report.erb +49 -0
- data/views/mission/index.erb +7 -0
- data/views/mission/refresh_js.erb +3 -0
- data/views/shared/_filter.erb +63 -0
- data/views/shared/_flash.erb +3 -0
- data/views/shared/_search.erb +25 -0
- data/views/shared/_timestamp.erb +1 -0
- data/views/shared/_wait.erb +1 -0
- data/views/shared/flash.js.erb +10 -0
- data/views/users/_rows.erb +26 -0
- data/views/users/filter.js.erb +4 -0
- data/views/users/index.erb +24 -0
- data/views/users/index.js.erb +2 -0
- metadata +347 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module Wackamole
|
4
|
+
class MoledInfo
|
5
|
+
|
6
|
+
def self.logs_cltn() Wackamole::Control.collection( 'logs' ) ; end
|
7
|
+
def self.users_cltn() Wackamole::Control.collection( 'users' ); end
|
8
|
+
def self.features_cltn() Wackamole::Control.collection( 'features' ); end
|
9
|
+
|
10
|
+
# ---------------------------------------------------------------------------
|
11
|
+
# Collect various data points to power up dashboard
|
12
|
+
# TODO - PERF - try just using cursor vs to_a
|
13
|
+
def self.collect_dashboard_info( now )
|
14
|
+
info = {}
|
15
|
+
day_logs = logs_cltn.find( { :did => now.to_date_id.to_s }, :fields => [:typ, :fid, :tid, :did, :uid] ).to_a
|
16
|
+
|
17
|
+
# Fetch user count for this hour
|
18
|
+
users = day_logs.inject( Set.new ) do |set,log|
|
19
|
+
set << log['uid'] if log['tid'] =~ /^#{"%02d" % now.hour}/
|
20
|
+
set
|
21
|
+
end
|
22
|
+
info[:total_users] = users_cltn.count
|
23
|
+
info[:user_load] = users.size
|
24
|
+
|
25
|
+
# Fetch features for this hour
|
26
|
+
features = day_logs.inject( Set.new ) do |set,log|
|
27
|
+
set << log['fid'].to_s if log['tid'] =~ /^#{"%02d" % now.hour}/
|
28
|
+
set
|
29
|
+
end
|
30
|
+
info[:total_features] = features_cltn.count
|
31
|
+
info[:feature_load] = features.size
|
32
|
+
|
33
|
+
info[:perf_load] = day_logs.inject(0) do |count,log|
|
34
|
+
if log['tid'] =~ /^#{"%02d" % now.hour}/
|
35
|
+
count += (log['typ'] == Rackamole.perf ? 1 : 0 )
|
36
|
+
end
|
37
|
+
count
|
38
|
+
end
|
39
|
+
info[:fault_load] = day_logs.inject(0) do |count,log|
|
40
|
+
if log['tid'] =~ /^#{"%02d" % now.hour}/
|
41
|
+
count += (log['typ'] == Rackamole.fault ? 1 : 0 )
|
42
|
+
end
|
43
|
+
count
|
44
|
+
end
|
45
|
+
|
46
|
+
# Count all logs per hourly time period
|
47
|
+
times = (0...24).to_a
|
48
|
+
time_info = times.inject(OrderedHash.new) { |res,time| res[time] = { :user => 0, :feature => 0, :perf => 0, :fault => 0 };res }
|
49
|
+
user_per_hour = {}
|
50
|
+
day_logs.each do |log|
|
51
|
+
date_tokens = log['did'].match( /(\d{4})(\d{2})(\d{2})/ ).captures
|
52
|
+
time_tokens = log['tid'].match( /(\d{2})(\d{2})(\d{2})/ ).captures
|
53
|
+
utc = Time.utc( date_tokens[0], date_tokens[1], date_tokens[2], time_tokens[0], time_tokens[1], time_tokens[2] )
|
54
|
+
time = utc.getlocal.hour
|
55
|
+
if user_per_hour[time]
|
56
|
+
unless user_per_hour[time].include? log['uid']
|
57
|
+
time_info[time][:user] += 1
|
58
|
+
user_per_hour[time] << log['uid']
|
59
|
+
end
|
60
|
+
else
|
61
|
+
user_per_hour[time] = [ log['uid'] ]
|
62
|
+
time_info[time][:user] += 1
|
63
|
+
end
|
64
|
+
case log['typ']
|
65
|
+
when Rackamole.feature : time_info[time][:feature] += 1 if features.add?( log['fid'])
|
66
|
+
when Rackamole.perf : time_info[time][:perf] += 1
|
67
|
+
when Rackamole.fault : time_info[time][:fault] += 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# BOZO !! LAME ASS...
|
72
|
+
info[:user_series] = []
|
73
|
+
info[:fault_series] = []
|
74
|
+
info[:perf_series] = []
|
75
|
+
info[:feature_series] = []
|
76
|
+
time_info.values.map do |hash|
|
77
|
+
info[:user_series] << hash[:user]
|
78
|
+
info[:fault_series] << hash[:fault]
|
79
|
+
info[:perf_series] << hash[:perf]
|
80
|
+
info[:feature_series] << hash[:feature]
|
81
|
+
end
|
82
|
+
info
|
83
|
+
end
|
84
|
+
|
85
|
+
# ===========================================================================
|
86
|
+
private
|
87
|
+
|
88
|
+
# -------------------------------------------------------------------------
|
89
|
+
# Makes sure we have some indexes set
|
90
|
+
# BOZO !! Create script to set these up ?
|
91
|
+
def self.ensure_indexes
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'chronic'
|
2
|
+
|
3
|
+
module Wackamole
|
4
|
+
class SearchFilter
|
5
|
+
|
6
|
+
attr_accessor :time_frame, :feature_id, :type, :browser_type, :search_terms
|
7
|
+
|
8
|
+
# ---------------------------------------------------------------------------
|
9
|
+
# Ctor
|
10
|
+
def initialize
|
11
|
+
reset!
|
12
|
+
end
|
13
|
+
|
14
|
+
# ---------------------------------------------------------------------------
|
15
|
+
# Reset filter to defaults
|
16
|
+
def reset!
|
17
|
+
@feature_id = -1
|
18
|
+
@time_frame = SearchFilter.time_frames.first
|
19
|
+
@browser_type = SearchFilter.browser_types.first
|
20
|
+
@type = SearchFilter.mole_types.first
|
21
|
+
@search_terms = ""
|
22
|
+
end
|
23
|
+
|
24
|
+
# ---------------------------------------------------------------------------
|
25
|
+
# Available browsers
|
26
|
+
def self.browser_types
|
27
|
+
@browsers ||= [ 'All', 'Unknown', 'Firefox', 'Safari', 'MSIE 8.0', 'MSIE 7.0', 'MSIE 6.0', 'Opera', 'Chrome' ]
|
28
|
+
end
|
29
|
+
|
30
|
+
# ---------------------------------------------------------------------------
|
31
|
+
# Available time frames
|
32
|
+
def self.time_frames
|
33
|
+
@time_frames ||= ['today', '2 days', '1 week', '2 weeks', '1 month', '3 months', '6 months', '1 year' ]
|
34
|
+
end
|
35
|
+
|
36
|
+
# ---------------------------------------------------------------------------
|
37
|
+
# Collection of mole types
|
38
|
+
def self.mole_types
|
39
|
+
@types ||= %w[All Feature Perf Fault]
|
40
|
+
end
|
41
|
+
|
42
|
+
# ---------------------------------------------------------------------------
|
43
|
+
# Find all features
|
44
|
+
def features
|
45
|
+
features = Feature.features_cltn.find().to_a
|
46
|
+
features = features.map { |f| [context_for(f), f['_id']] }
|
47
|
+
features.sort! { |a,b| a.first <=> b.first }
|
48
|
+
features.insert( 0, ["All", -1] )
|
49
|
+
end
|
50
|
+
|
51
|
+
# ---------------------------------------------------------------------------
|
52
|
+
# Retrieves feature context
|
53
|
+
def context_for( f )
|
54
|
+
return "#{f['ctl']}##{f['act']}" if f['ctl']
|
55
|
+
f['ctx']
|
56
|
+
end
|
57
|
+
|
58
|
+
# ---------------------------------------------------------------------------
|
59
|
+
# Fetch filter start date id
|
60
|
+
def from_date_id
|
61
|
+
start = Chronic.parse( time_frame + ( time_frame == SearchFilter.time_frames.first ? "" : " ago" ) )
|
62
|
+
start.to_date_id
|
63
|
+
end
|
64
|
+
|
65
|
+
# ---------------------------------------------------------------------------
|
66
|
+
# fetch time series from time frame
|
67
|
+
def time_ids
|
68
|
+
now = Time.now
|
69
|
+
start = Chronic.parse( time_frame + ( time_frame == SearchFilter.time_frames.first ? "" : " ago" ) )
|
70
|
+
corpus_end = DateTime.new( now.year, now.month, now.day )
|
71
|
+
corpus_start = DateTime.new( start.year, start.month, start.day )
|
72
|
+
|
73
|
+
calc_date = corpus_start
|
74
|
+
time_series = []
|
75
|
+
while calc_date <= corpus_end do
|
76
|
+
time_series << calc_date.to_date_id
|
77
|
+
calc_date += 1
|
78
|
+
end
|
79
|
+
time_series
|
80
|
+
end
|
81
|
+
|
82
|
+
# ---------------------------------------------------------------------------
|
83
|
+
# fetch time series from time frame
|
84
|
+
def time_series
|
85
|
+
now = Time.now
|
86
|
+
start = Chronic.parse( time_frame + ( time_frame == SearchFilter.time_frames.first ? "" : " ago" ) )
|
87
|
+
corpus_end = DateTime.new( now.year, now.month, now.day )
|
88
|
+
corpus_start = DateTime.new( start.year, start.month, start.day )
|
89
|
+
|
90
|
+
calc_date = corpus_start
|
91
|
+
time_series = []
|
92
|
+
while calc_date <= corpus_end do
|
93
|
+
time_series << calc_date.strftime( "%Y-%m-%d" )
|
94
|
+
calc_date += 1
|
95
|
+
end
|
96
|
+
time_series
|
97
|
+
end
|
98
|
+
|
99
|
+
# ---------------------------------------------------------------------------
|
100
|
+
# Populate filter from request params
|
101
|
+
def from_options( options )
|
102
|
+
return unless options
|
103
|
+
options.each_pair do |k,v|
|
104
|
+
# value = k.index( /_id$/) ? v.to_i : v
|
105
|
+
self.send( "#{k}=", v )
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# ---------------------------------------------------------------------------
|
110
|
+
# Spews filter conditions
|
111
|
+
def to_conds
|
112
|
+
conds = {}
|
113
|
+
|
114
|
+
# filter mole_types
|
115
|
+
if type != 'All'
|
116
|
+
conds[:typ] = map_mole_type( type )
|
117
|
+
end
|
118
|
+
|
119
|
+
if browser_type != 'All'
|
120
|
+
conds[:bro] = browser_type
|
121
|
+
end
|
122
|
+
|
123
|
+
# filter mole_features
|
124
|
+
unless feature_id.to_s == "-1"
|
125
|
+
conds[:fid] = Mongo::ObjectID.from_string( feature_id )
|
126
|
+
end
|
127
|
+
|
128
|
+
# filter by date
|
129
|
+
time = Chronic.parse( time_frame + ( time_frame == SearchFilter.time_frames.first ? "" : " ago" ) )
|
130
|
+
conds[:did] = { '$gte' => time.to_date_id.to_s }
|
131
|
+
|
132
|
+
unless search_terms.empty?
|
133
|
+
tokens = search_terms.split( ":" ).collect{ |c| c.strip }
|
134
|
+
key = tokens.shift
|
135
|
+
if key
|
136
|
+
if key == "user"
|
137
|
+
users = Wackamole::User.users_cltn.find( { :una => Regexp.new( tokens.first ) }, :fields => ['_id'] )
|
138
|
+
conds[field_map( key )] = { '$in' => users.collect{ |u| u['_id'] } }
|
139
|
+
elsif tokens.size == 2
|
140
|
+
conds["#{field_map(key)}.#{tokens.first}"] = Regexp.new( tokens.last )
|
141
|
+
elsif tokens.size == 1
|
142
|
+
conds[field_map(key)] = Regexp.new( tokens.first )
|
143
|
+
else
|
144
|
+
raise "Unable to evaluate search terms"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
conds
|
149
|
+
end
|
150
|
+
|
151
|
+
# ===========================================================================
|
152
|
+
private
|
153
|
+
|
154
|
+
# ---------------------------------------------------------------------------
|
155
|
+
# Search filter key name map
|
156
|
+
def field_map( key )
|
157
|
+
case key.to_sym
|
158
|
+
when :user : :uid
|
159
|
+
when :method : :met
|
160
|
+
when :host : :hos
|
161
|
+
when :session : :ses
|
162
|
+
when :params : :par
|
163
|
+
else key
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# Map named type to fixnum value
|
168
|
+
def map_mole_type( type )
|
169
|
+
case type
|
170
|
+
when 'Feature'
|
171
|
+
Rackamole.feature
|
172
|
+
when 'Perf'
|
173
|
+
Rackamole.perf
|
174
|
+
when 'Fault'
|
175
|
+
Rackamole.fault
|
176
|
+
else
|
177
|
+
raise "Invalid mole type `#{type}"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# ---------------------------------------------------------------------------
|
182
|
+
# Zeroed out now
|
183
|
+
def now
|
184
|
+
to_zero_hour( Time.now )
|
185
|
+
end
|
186
|
+
|
187
|
+
# Zeros out time info
|
188
|
+
def to_zero_hour( time )
|
189
|
+
Time.gm( time.year, time.month, time.day, 0, 0, 0 )
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'will_paginate/collection'
|
2
|
+
|
3
|
+
module Wackamole
|
4
|
+
class User
|
5
|
+
|
6
|
+
def self.logs_cltn() Wackamole::Control.collection( 'logs' ) ; end
|
7
|
+
def self.users_cltn() Wackamole::Control.collection( 'users' ); end
|
8
|
+
|
9
|
+
# Pagination size
|
10
|
+
def self.default_page_size() @page_size ||= 20; end
|
11
|
+
|
12
|
+
# ---------------------------------------------------------------------------
|
13
|
+
# Find all users matching criteria and returns pagination collection
|
14
|
+
def self.paginate_tops( conds, page=1, page_size=default_page_size )
|
15
|
+
tops = logs_cltn.group( [:uid], conds, { :count => 0 }, 'function(obj,prev) { prev.count += 1}', true )
|
16
|
+
users = []
|
17
|
+
tops.sort{ |a,b| b['count'] <=> a['count'] }.each do |row|
|
18
|
+
users << { :uid => row['uid'], :total => row['count'].to_i, :details => [] }
|
19
|
+
end
|
20
|
+
|
21
|
+
WillPaginate::Collection.create( page, page_size, users.size ) do |pager|
|
22
|
+
offset = (page-1)*page_size
|
23
|
+
result = users[offset...(offset+page_size)]
|
24
|
+
result.each do |u|
|
25
|
+
user = users_cltn.find_one( u[:uid] ) #, :fields => [:una] )
|
26
|
+
raise "Unable to find user with id `#{u[:uid].inspect}" unless user
|
27
|
+
u[:name] = user['una']
|
28
|
+
end
|
29
|
+
pager.replace( result )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# ---------------------------------------------------------------------------
|
34
|
+
# Make sure indexes are setup for users
|
35
|
+
def self.ensure_indexes!
|
36
|
+
indexes = users_cltn.index_information
|
37
|
+
created_count = 0
|
38
|
+
|
39
|
+
unless indexes.has_key?( :una )
|
40
|
+
users_cltn.create_index( :una )
|
41
|
+
created_count += 1
|
42
|
+
end
|
43
|
+
created_count
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/public/favicon.ico
ADDED
File without changes
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,28 @@
|
|
1
|
+
function truncate(str, limit)
|
2
|
+
{
|
3
|
+
var bits, i;
|
4
|
+
bits = str.split('');
|
5
|
+
if (bits.length > limit) {
|
6
|
+
for (i = bits.length - 1; i > -1; --i) {
|
7
|
+
if (i > limit) {
|
8
|
+
bits.length = i;
|
9
|
+
}
|
10
|
+
else if (' ' === bits[i]) {
|
11
|
+
bits.length = i;
|
12
|
+
break;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
bits.push('...');
|
16
|
+
}
|
17
|
+
return bits.join('');
|
18
|
+
}
|
19
|
+
|
20
|
+
|
21
|
+
function blee( url )
|
22
|
+
{
|
23
|
+
if( url == "/explore")
|
24
|
+
window.location = url;
|
25
|
+
else
|
26
|
+
$.ajax( {url: url, dataType: 'script', method: 'GET'} );
|
27
|
+
return false;
|
28
|
+
}
|