muck-services 0.1.10 → 0.1.11

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.
Files changed (73) hide show
  1. data/VERSION +1 -1
  2. data/app/controllers/muck/recommendations_controller.rb +21 -6
  3. data/app/models/entry.rb +7 -10
  4. data/app/views/recommendations/get_button.html.erb +5 -0
  5. data/app/views/recommendations/index.pjs.erb +1 -1
  6. data/app/views/recommendations/index.xml.builder +4 -6
  7. data/app/views/recommendations/real_time.html.erb +13 -0
  8. data/app/views/recommendations/{index_real_time.pjs.erb → real_time.pjs.erb} +0 -0
  9. data/app/views/recommendations/{index_real_time.xml.builder → real_time.xml.builder} +1 -1
  10. data/config/muck_services_routes.rb +3 -0
  11. data/locales/en.yml +2 -1
  12. data/muck-services.gemspec +8 -52
  13. data/public/images/folksemantic/logo-folksemantic-gm.gif +0 -0
  14. data/public/javascripts/recommenderextension.user.js +392 -0
  15. data/test/rails_root/config/environment.rb +7 -6
  16. data/test/rails_root/config/environments/test.rb +0 -10
  17. data/test/rails_root/db/migrate/20090602191243_create_muck_raker.rb +4 -19
  18. data/test/rails_root/db/migrate/20090703175825_denormalize_entries_subjects.rb +3 -29
  19. data/test/rails_root/db/migrate/20090717175825_normalize_entries_subjects.rb +3 -32
  20. data/test/rails_root/test/factories.rb +0 -145
  21. data/test/rails_root/test/functional/topics_controller_test.rb +4 -0
  22. data/test/rails_root/test/test_helper.rb +5 -40
  23. data/test/rails_root/test/unit/share_test.rb +3 -0
  24. data/test/rails_root/test/unit/user_test.rb +3 -0
  25. metadata +8 -52
  26. data/app/views/recommendations/index_js.pjs.erb +0 -39
  27. data/config/solr/README.txt +0 -0
  28. data/config/solr/cores/de/conf/protwords.txt +0 -21
  29. data/config/solr/cores/de/conf/schema.xml +0 -81
  30. data/config/solr/cores/de/conf/solrconfig.xml +0 -311
  31. data/config/solr/cores/de/conf/stopwords.txt +0 -341
  32. data/config/solr/cores/de/conf/synonyms.txt +0 -31
  33. data/config/solr/cores/en/conf/protwords.txt +0 -21
  34. data/config/solr/cores/en/conf/schema.xml +0 -83
  35. data/config/solr/cores/en/conf/solrconfig.xml +0 -311
  36. data/config/solr/cores/en/conf/stopwords.txt +0 -341
  37. data/config/solr/cores/en/conf/synonyms.txt +0 -31
  38. data/config/solr/cores/es/conf/protwords.txt +0 -21
  39. data/config/solr/cores/es/conf/schema.xml +0 -81
  40. data/config/solr/cores/es/conf/solrconfig.xml +0 -311
  41. data/config/solr/cores/es/conf/stopwords.txt +0 -206
  42. data/config/solr/cores/es/conf/synonyms.txt +0 -31
  43. data/config/solr/cores/fr/conf/protwords.txt +0 -21
  44. data/config/solr/cores/fr/conf/schema.xml +0 -82
  45. data/config/solr/cores/fr/conf/solrconfig.xml +0 -311
  46. data/config/solr/cores/fr/conf/stopwords.txt +0 -57
  47. data/config/solr/cores/fr/conf/synonyms.txt +0 -31
  48. data/config/solr/cores/ja/conf/protwords.txt +0 -21
  49. data/config/solr/cores/ja/conf/schema.xml +0 -69
  50. data/config/solr/cores/ja/conf/solrconfig.xml +0 -311
  51. data/config/solr/cores/ja/conf/stopwords.txt +0 -341
  52. data/config/solr/cores/ja/conf/synonyms.txt +0 -31
  53. data/config/solr/cores/nl/conf/protwords.txt +0 -21
  54. data/config/solr/cores/nl/conf/schema.xml +0 -78
  55. data/config/solr/cores/nl/conf/solrconfig.xml +0 -311
  56. data/config/solr/cores/nl/conf/stopwords.txt +0 -45691
  57. data/config/solr/cores/nl/conf/synonyms.txt +0 -31
  58. data/config/solr/cores/ru/conf/protwords.txt +0 -21
  59. data/config/solr/cores/ru/conf/schema.xml +0 -78
  60. data/config/solr/cores/ru/conf/solrconfig.xml +0 -311
  61. data/config/solr/cores/ru/conf/stopwords.txt +0 -341
  62. data/config/solr/cores/ru/conf/synonyms.txt +0 -31
  63. data/config/solr/cores/zh/conf/protwords.txt +0 -21
  64. data/config/solr/cores/zh/conf/schema.xml +0 -72
  65. data/config/solr/cores/zh/conf/solrconfig.xml +0 -311
  66. data/config/solr/cores/zh/conf/stopwords.txt +0 -341
  67. data/config/solr/cores/zh/conf/synonyms.txt +0 -31
  68. data/config/solr/solr.xml +0 -50
  69. data/test/rails_root/test/shoulda_macros/controller.rb +0 -49
  70. data/test/rails_root/test/shoulda_macros/forms.rb +0 -32
  71. data/test/rails_root/test/shoulda_macros/models.rb +0 -50
  72. data/test/rails_root/test/shoulda_macros/pagination.rb +0 -53
  73. data/test/rails_root/test/shoulda_macros/plugins.rb +0 -34
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.10
1
+ 0.1.11
@@ -5,10 +5,7 @@ class Muck::RecommendationsController < ApplicationController
5
5
  # GET /recommendations
6
6
  # GET /recommendations.xml
7
7
  def index
8
-
9
- @real_time == params[:rtr]
10
-
11
- @details = params[:details] == "true"
8
+ @details = (params[:details] == 'true')
12
9
 
13
10
  @uri = params[:u] || request.env['HTTP_REFERER']
14
11
  if @uri.blank? || !allowed_uri(@uri)
@@ -40,17 +37,35 @@ class Muck::RecommendationsController < ApplicationController
40
37
  end
41
38
  end
42
39
  format.xml {
43
- render(:template => @entry.id.nil? && @real_time == true ? '/recommendations/index_real_time.xml.builder' : '/recommendations/index.xml.builder', :layout => false)
40
+ render('recommendations/index.xml.builder', :layout => false)
44
41
  }
45
42
  format.pjs {
46
43
  @host = "http://#{URI.parse(@uri).host}"
47
- render(:template => @entry.id.nil? && @real_time == true ? 'recommendations/index_real_time.pjs.erb' : 'recommendations/index.pjs.erb', :layout => false)
44
+ render('recommendations/index.pjs.erb', :layout => false)
48
45
  }
49
46
  format.rss {
50
47
  render(:template => 'recommendations/index.rss.builder', :layout => false)
51
48
  }
52
49
  end
53
50
  end
51
+
52
+ def get_button
53
+ @uri = params[:u]
54
+ render 'recommendations/get_button', :layout => false
55
+ end
56
+
57
+ def real_time
58
+ respond_to do |format|
59
+ format.html do
60
+ @uri = params[:u]
61
+ @cache_key = "recommendations/real_time?u=#{CGI.escape(@uri)}"
62
+ if !fragment_exist?(@cache_key)
63
+ @recommendations = Entry.real_time_recommendations(@uri, I18n.locale.to_s, 5)
64
+ end
65
+ render 'recommendations/real_time', :layout => false
66
+ end
67
+ end
68
+ end
54
69
 
55
70
  protected
56
71
 
@@ -176,15 +176,11 @@ class Entry < ActiveRecord::Base
176
176
  words = text.split()
177
177
  words[0..(length-1)].join(' ') + (words.length > length ? end_string : '')
178
178
  end
179
-
179
+
180
180
  def ranked_recommendations(limit = 5, order = "mixed", details = false, omit_feeds = nil)
181
- # if self.id.nil?
182
- # return Entry.real_time_recommendations(self.permalink, details, :core => 'en', :limit => limit)
183
- # else
184
- return relevant_recommendations(limit, "clicks DESC, relevance", details, omit_feeds) if order == "clicks"
185
- return relevant_recommendations(limit, "relevance", details, omit_feeds) if (order == "relevance" || details == true)
186
- return relevant_recommendations_filtered(limit, details, omit_feeds) if omit_feeds != nil
187
- # end
181
+ return relevant_recommendations(limit, "clicks DESC, relevance", details, omit_feeds) if order == "clicks"
182
+ return relevant_recommendations(limit, "relevance", details, omit_feeds) if (order == "relevance" || details == true)
183
+ return relevant_recommendations_filtered(limit, details, omit_feeds) if omit_feeds != nil
188
184
 
189
185
  recs = []
190
186
  if self.popular != nil && !self.popular.empty?
@@ -332,10 +328,11 @@ class Entry < ActiveRecord::Base
332
328
  uri.sub(/index.?\.(html|aspx|shtm|htm|asp|php|cfm|jsp|shtml|jhtml)$/, '')
333
329
  end
334
330
 
335
- def self.real_time_recommendations(uri, details = false, options = {})
331
+ def self.real_time_recommendations(uri, language='en', limit=5, details=false, options = {})
332
+ raise MuckServices::Exceptions::LanguageNotSupported, I18n.t('muck.services.language_not_supported') unless Recommender::Languages.supported_languages.include?(language)
336
333
  fields = "entries.id, entries.title, entries.permalink, entries.direct_link, feeds.short_title AS collection"
337
334
  fields << ", entries.published_at, entries.description, entries.author" if details == true
338
- more_like_this(uri, options.merge(:select => fields, :joins => "INNER JOIN feeds ON feeds.id = entries.feed_id"))
335
+ more_like_this(uri, options.merge(:select => fields, :joins => "INNER JOIN feeds ON feeds.id = entries.feed_id", :core => language, :limit => limit))
339
336
  end
340
337
 
341
338
  end
@@ -0,0 +1,5 @@
1
+ <html>
2
+ <body style="margin:0;padding:0;">
3
+ <div style="padding-bottom:5px;margin:0 1px 0 3px;text-align:center;background-color:white;border-left:1px solid black;border-right:1px solid black;border-bottom:1px solid black;"><input type='button' value='<%= t('muck.services.get_recommendations') %>' onclick='window.open("<%= "real_time?u=#{@uri}" %>","_self");return false;' /></div>
4
+ </body>
5
+ </html>
@@ -6,7 +6,7 @@ if !@json_recommendations.nil?
6
6
  var catalog_page = <%= !@entry.direct_link.nil? and @uri == @entry.permalink %>;
7
7
  var document_host = '<%= @host %>';
8
8
  var recs = <%= @json_recommendations %>;
9
- var app = "http://folksemantic.com/";
9
+ var app = "<%= app = request.protocol + request.host_with_port + '/' %>";
10
10
  <% if @details == true -%>
11
11
  function truncate(text, length) {
12
12
  nEnd = text.indexOf(" ", 200);
@@ -4,20 +4,18 @@ if @entry.nil?
4
4
  xml.recommendations
5
5
  else
6
6
 
7
+ app = request.protocol + request.host_with_port + '/'
7
8
  @recommendations = @entry.ranked_recommendations(@limit, params[:order] || "mixed", @details)
8
- #xml.recommendations(:document_id => @entry.nil? ? "" : @entry.id, :uri => @uri, :title => t(:gm_title), :more_prompt => t(:gm_more_prompt), :direct_link_text => t(:direct_link), :locale => @entry.language) do
9
9
  xml.recommendations(:document_id => @entry.nil? ? "" : @entry.id, :uri => @uri, :title => t("muck.services.gm_title"), :more_prompt => t("muck.services.gm_more_prompt"), :direct_link_text => t("muck.services.direct_link")) do
10
10
  @recommendations.each do |recommendation|
11
11
  xml.recommendation do
12
12
  xml.title recommendation["title"]
13
13
  xml.collection recommendation["collection"]
14
- xml.link "http://folksemantic.com/r?id=" + recommendation["id"].to_s
15
- # xml.link "http://localhost:3000/r?id=" + recommendation["id"].to_s
16
- xml.has_direct_link "true" if (recommendation["direct_link"] != nil and @uri[0..20] == recommendation["uri"][0..20])
14
+ xml.link app + 'r?id=' + recommendation['id'].to_s
15
+ xml.has_direct_link "true" if (recommendation["direct_link"] != nil and @uri[0..20] == recommendation["uri"][0..20])
17
16
 
18
17
  if @details == true
19
- xml.direct_link "http://www.folksemantic.com/r?id=" + recommendation["id"].to_s + "&u=" + recommendation["direct_link"] if recommendation["direct_link"] != nil
20
- # xml.direct_link "http://localhost:3000/r?id=" + recommendation["id"].to_s + "&u=" + recommendation["direct_link"] if recommendation["direct_link"] != nil
18
+ xml.direct_link app + 'r?id=' + recommendation["id"].to_s + "&u=" + recommendation["direct_link"] if recommendation["direct_link"] != nil
21
19
  xml.uri recommendation["uri"]
22
20
  xml.direct_uri recommendation["direct_link"]
23
21
  xml.description recommendation["description"]
@@ -0,0 +1,13 @@
1
+ <% cache(@cache_key) do %>
2
+ <html>
3
+ <body style="margin:0;padding:0;">
4
+ <div style="padding-bottom:5px;margin:0 1px 0 3px;background-color:white;border-left:1px solid black;border-right:1px solid black;border-bottom:1px solid black;">
5
+ <ul style="margin:0 2px 2px;padding:0px;font-family:Arial,Helvetica,sans-serif;">
6
+ <% @recommendations.results.each_with_index do |r, i| %>
7
+ <li style="margin-left: 0px; padding: 3px; list-style-type: none; font-size: 12px; <%= (i % 2 == 0) ? 'background-color:#E6E6E6;' : '' %>"><%= link_to r.title + ' (' + r.collection + ')', visit_path(r), :target => '_top' %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ </body>
12
+ </html>
13
+ <% end %>
@@ -9,7 +9,7 @@ else
9
9
  xml.recommendation do
10
10
  xml.title recommendation["title"]
11
11
  xml.collection recommendation["collection"]
12
- xml.link "http://folksemantic.com/visits/" + recommendation["id"].to_s
12
+ xml.link "http://www.folksemantic.com/visits/" + recommendation["id"].to_s
13
13
 
14
14
  if @details == true
15
15
  xml.description recommendation["description"]
@@ -25,6 +25,9 @@ ActionController::Routing::Routes.draw do |map|
25
25
  map.resources :oai_endpoints, :controller => 'muck/oai_endpoints', :has_many => :feeds
26
26
 
27
27
  map.resources :feeds, :controller => 'muck/feeds', :collection => { :new_extended => :get, :new_oai_rss => :get }, :has_many => :entries
28
+
29
+ map.connect 'recommendations/real_time', :controller => 'muck/recommendations', :action => 'real_time'
30
+ map.connect 'recommendations/get_button', :controller => 'muck/recommendations', :action => 'get_button'
28
31
  map.resources :recommendations, :controller => 'muck/recommendations'
29
32
 
30
33
  map.resources :identity_feeds, :controller => 'muck/identity_feeds'
@@ -206,7 +206,8 @@ en:
206
206
  videos: "Videos"
207
207
  back: "Back"
208
208
  explore: "Search for {{topic}}"
209
- language_not_supported: "The requested language is not supported"
209
+ language_not_supported: The requested language is not supported
210
+ get_recommendations: Get Recommendations
210
211
  comment_entries_count:
211
212
  one: "1 Comment"
212
213
  other: "{{count}} Comments"
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{muck-services}
8
- s.version = "0.1.10"
8
+ s.version = "0.1.11"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Joel Duffin", "Justin Ball"]
12
- s.date = %q{2009-11-10}
12
+ s.date = %q{2009-11-13}
13
13
  s.description = %q{This gem contains the rails specific code for dealing with feeds, aggregations and recommendations. It is meant to work with the muck-raker gem.}
14
14
  s.email = %q{justin@tatemae.com}
15
15
  s.extra_rdoc_files = [
@@ -111,13 +111,14 @@ Gem::Specification.new do |s|
111
111
  "app/views/oai_endpoints/show.html.erb",
112
112
  "app/views/parts/_add_feed.html.erb",
113
113
  "app/views/parts/_select_feed.html.erb",
114
+ "app/views/recommendations/get_button.html.erb",
114
115
  "app/views/recommendations/index.html.erb",
115
116
  "app/views/recommendations/index.pjs.erb",
116
117
  "app/views/recommendations/index.rss.builder",
117
118
  "app/views/recommendations/index.xml.builder",
118
- "app/views/recommendations/index_js.pjs.erb",
119
- "app/views/recommendations/index_real_time.pjs.erb",
120
- "app/views/recommendations/index_real_time.xml.builder",
119
+ "app/views/recommendations/real_time.html.erb",
120
+ "app/views/recommendations/real_time.pjs.erb",
121
+ "app/views/recommendations/real_time.xml.builder",
121
122
  "app/views/search/_languages.html.erb",
122
123
  "app/views/search/_result.html.erb",
123
124
  "app/views/search/_search_box.html.erb",
@@ -332,48 +333,6 @@ Gem::Specification.new do |s|
332
333
  "app/views/visits/_toolbar.html.erb",
333
334
  "app/views/visits/show.html.erb",
334
335
  "config/muck_services_routes.rb",
335
- "config/solr/README.txt",
336
- "config/solr/cores/de/conf/protwords.txt",
337
- "config/solr/cores/de/conf/schema.xml",
338
- "config/solr/cores/de/conf/solrconfig.xml",
339
- "config/solr/cores/de/conf/stopwords.txt",
340
- "config/solr/cores/de/conf/synonyms.txt",
341
- "config/solr/cores/en/conf/protwords.txt",
342
- "config/solr/cores/en/conf/schema.xml",
343
- "config/solr/cores/en/conf/solrconfig.xml",
344
- "config/solr/cores/en/conf/stopwords.txt",
345
- "config/solr/cores/en/conf/synonyms.txt",
346
- "config/solr/cores/es/conf/protwords.txt",
347
- "config/solr/cores/es/conf/schema.xml",
348
- "config/solr/cores/es/conf/solrconfig.xml",
349
- "config/solr/cores/es/conf/stopwords.txt",
350
- "config/solr/cores/es/conf/synonyms.txt",
351
- "config/solr/cores/fr/conf/protwords.txt",
352
- "config/solr/cores/fr/conf/schema.xml",
353
- "config/solr/cores/fr/conf/solrconfig.xml",
354
- "config/solr/cores/fr/conf/stopwords.txt",
355
- "config/solr/cores/fr/conf/synonyms.txt",
356
- "config/solr/cores/ja/conf/protwords.txt",
357
- "config/solr/cores/ja/conf/schema.xml",
358
- "config/solr/cores/ja/conf/solrconfig.xml",
359
- "config/solr/cores/ja/conf/stopwords.txt",
360
- "config/solr/cores/ja/conf/synonyms.txt",
361
- "config/solr/cores/nl/conf/protwords.txt",
362
- "config/solr/cores/nl/conf/schema.xml",
363
- "config/solr/cores/nl/conf/solrconfig.xml",
364
- "config/solr/cores/nl/conf/stopwords.txt",
365
- "config/solr/cores/nl/conf/synonyms.txt",
366
- "config/solr/cores/ru/conf/protwords.txt",
367
- "config/solr/cores/ru/conf/schema.xml",
368
- "config/solr/cores/ru/conf/solrconfig.xml",
369
- "config/solr/cores/ru/conf/stopwords.txt",
370
- "config/solr/cores/ru/conf/synonyms.txt",
371
- "config/solr/cores/zh/conf/protwords.txt",
372
- "config/solr/cores/zh/conf/schema.xml",
373
- "config/solr/cores/zh/conf/solrconfig.xml",
374
- "config/solr/cores/zh/conf/stopwords.txt",
375
- "config/solr/cores/zh/conf/synonyms.txt",
376
- "config/solr/solr.xml",
377
336
  "db/bootstrap/attention.yml",
378
337
  "db/bootstrap/feeds.yml",
379
338
  "db/bootstrap/oai_endpoints.yml",
@@ -463,6 +422,7 @@ Gem::Specification.new do |s|
463
422
  "locales/zh-TW.yml",
464
423
  "locales/zh.yml",
465
424
  "muck-services.gemspec",
425
+ "public/images/folksemantic/logo-folksemantic-gm.gif",
466
426
  "public/images/service_icons/16/amazon.png",
467
427
  "public/images/service_icons/16/ask.png",
468
428
  "public/images/service_icons/16/bibsonomy.png",
@@ -726,6 +686,7 @@ Gem::Specification.new do |s|
726
686
  "public/images/service_icons/facebook_friend_links.jpg",
727
687
  "public/images/service_icons/facebook_my_links.jpg",
728
688
  "public/javascripts/muck_services.js",
689
+ "public/javascripts/recommenderextension.user.js",
729
690
  "public/stylesheets/frame.css",
730
691
  "public/stylesheets/muck_services.css",
731
692
  "rails/init.rb"
@@ -815,11 +776,6 @@ Gem::Specification.new do |s|
815
776
  "test/rails_root/test/functional/oai_endpoints_controller_test.rb",
816
777
  "test/rails_root/test/functional/topics_controller_test.rb",
817
778
  "test/rails_root/test/functional/visits_controller_test.rb",
818
- "test/rails_root/test/shoulda_macros/controller.rb",
819
- "test/rails_root/test/shoulda_macros/forms.rb",
820
- "test/rails_root/test/shoulda_macros/models.rb",
821
- "test/rails_root/test/shoulda_macros/pagination.rb",
822
- "test/rails_root/test/shoulda_macros/plugins.rb",
823
779
  "test/rails_root/test/test_helper.rb",
824
780
  "test/rails_root/test/unit/aggregation_feed_test.rb",
825
781
  "test/rails_root/test/unit/aggregation_test.rb",
@@ -0,0 +1,392 @@
1
+ // ==UserScript==
2
+ // @name RecommenderExtension
3
+ // @namespace folksemanticsemanticFrames.com
4
+ // @description Folksemantic Recommender GM Script - Recommends resources to a user based upon their current url.
5
+ // @include *
6
+ // ==/UserScript==
7
+
8
+ var sUrlToGetRecsFor = new String(window.location);
9
+ if (self != top && !folksemanticFrames(sUrlToGetRecsFor)) return;
10
+
11
+ var Drag = function() {
12
+ this.init.apply(this, arguments);
13
+ };
14
+
15
+ Drag.fixE = function(e) {
16
+ if (typeof e == 'undefined') e = window.event;
17
+ if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
18
+ if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
19
+ return e;
20
+ };
21
+
22
+ Drag.prototype.init = function(handle, dragdiv) {
23
+ this.div = dragdiv || handle;
24
+ this.handle = handle;
25
+ if (isNaN(parseInt(this.div.style.left))) this.div.style.left = '0px';
26
+ if (isNaN(parseInt(this.div.style.top))) this.div.style.top = '0px';
27
+ this.onDragStart = function() {};
28
+ this.onDragEnd = function() {};
29
+ this.onDrag = function() {};
30
+ this.onClick = function() {};
31
+ this.mouseDown = addEventHandler(this.handle, 'mousedown', this.start, this);
32
+ };
33
+
34
+ Drag.prototype.start = function(e) {
35
+ e = Drag.fixE(e);
36
+
37
+ this.started = new Date();
38
+ var y = this.startY = parseInt(this.div.style.top);
39
+ var x = this.startX = parseInt(this.div.style.left);
40
+ this.onDragStart(x, y);
41
+ this.lastMouseX = e.clientX;
42
+ this.lastMouseY = e.clientY;
43
+ this.documentMove = addEventHandler(document, 'mousemove', this.drag, this);
44
+ this.documentStop = addEventHandler(document, 'mouseup', this.end, this);
45
+ if (e.preventDefault) e.preventDefault();
46
+ return false;
47
+ };
48
+
49
+ Drag.prototype.drag = function(e) {
50
+ e = Drag.fixE(e);
51
+ var ey = e.clientY;
52
+ var ex = e.clientX;
53
+ var y = parseInt(this.div.style.top);
54
+ var x = parseInt(this.div.style.left);
55
+ var nx = ex + x - this.lastMouseX;
56
+ var ny = ey + y - this.lastMouseY;
57
+ this.div.style.left = nx + 'px';
58
+ this.div.style.top = ny + 'px';
59
+ this.lastMouseX = ex;
60
+ this.lastMouseY = ey;
61
+ this.onDrag(nx, ny);
62
+ if (e.preventDefault) e.preventDefault();
63
+ return false;
64
+ };
65
+
66
+ Drag.prototype.end = function() {
67
+ removeEventHandler(document, 'mousemove', this.documentMove);
68
+ removeEventHandler(document, 'mouseup', this.documentStop);
69
+ var time = (new Date()) - this.started;
70
+ var x = parseInt(this.div.style.left), dx = x - this.startX;
71
+ var y = parseInt(this.div.style.top), dy = y - this.startY;
72
+ this.onDragEnd(x, y, dx, dy, time);
73
+ if ((dx * dx + dy * dy) < (4 * 4) && time < 1e3)
74
+ this.onClick(x, y, dx, dy, time);
75
+ };
76
+
77
+ function removeEventHandler(target, eventName, eventHandler) {
78
+ if (target.addEventListener)
79
+ target.removeEventListener(eventName, eventHandler, true);
80
+ else if (target.attachEvent)
81
+ target.detachEvent('on' + eventName, eventHandler);
82
+ }
83
+
84
+ function addEventHandler(target, eventName, eventHandler, scope) {
85
+ var f = scope ? function() {
86
+ eventHandler.apply(scope, arguments);
87
+ } : eventHandler;
88
+ if (target.addEventListener)
89
+ target.addEventListener(eventName, f, true);
90
+ else if (target.attachEvent)
91
+ target.attachEvent('on' + eventName, f);
92
+ return f;
93
+ }
94
+
95
+ function dom_setStyle(elt, str) {
96
+ elt.setAttribute("style", str);
97
+ // for MSIE:
98
+ if (elt.style.setAttribute) {
99
+ elt.style.setAttribute("cssText", str, 0);
100
+ // positioning for MSIE:
101
+ if (elt.style.position == "fixed") {
102
+ elt.style.position = "absolute";
103
+ }
104
+ }
105
+ }
106
+
107
+ function special_website_url(url) {
108
+ // NEEDS
109
+ if (url.indexOf("http://www.needs.org/") == 0)
110
+ {
111
+ bNeeds = true;
112
+ if (url.length > 167 && url.indexOf("http://www.needs.org/needs/public/search/search_results/learning_resource/summary/index.jhtml") == 0)
113
+ {
114
+ bNeedsRecord = true;
115
+ nHook = url.indexOf("?");
116
+ if (nHook != -1)
117
+ {
118
+ url = "http://www.needs.org/needs/" + url.substring(nHook, url.length);
119
+ }
120
+ }
121
+ }
122
+ // eduCommons websites
123
+ else if (document.getElementById("portlet-eduCommonsNavigation"))
124
+ {
125
+ bSpecialSite = true;
126
+ var nEnd = url.indexOf("/", 20);
127
+ if (nEnd != -1)
128
+ {
129
+ nEnd = url.indexOf("/", nEnd + 1);
130
+ if (nEnd != -1)
131
+ {
132
+ url = url.substr(0, nEnd);
133
+ }
134
+ }
135
+ }
136
+ // MIT
137
+ else if (url.indexOf("http://ocw.mit.edu/") == 0)
138
+ {
139
+ bSpecialSite = true;
140
+ bMIT = true;
141
+ var nEnd = url.indexOf("/", 20);
142
+ if (nEnd != -1)
143
+ {
144
+ nEnd = url.indexOf("/", nEnd + 1);
145
+ if (nEnd != -1)
146
+ {
147
+ nEnd = url.indexOf("/", nEnd + 1);
148
+ if (nEnd != -1)
149
+ {
150
+ url = url.substr(0, nEnd + 1) + "CourseHome/index.htm";
151
+ }
152
+ }
153
+ }
154
+ }
155
+ return encodeURI(url);
156
+ }
157
+
158
+ function getElementByClassName(sTagName, sClassName) {
159
+ var aTags = document.getElementsByTagName(sTagName);
160
+ for (var nTag = 0; nTag < aTags.length; nTag++)
161
+ {
162
+ if (sClassName == aTags[nTag].className) return aTags[nTag];
163
+ }
164
+ return null;
165
+ }
166
+
167
+ function identifySquatParent() {
168
+ // educommons
169
+ squatParent = document.getElementById("portlet-eduCommonsNavigation");
170
+ if (squatParent) {
171
+ squatSibling = squatParent.lastChild;
172
+ divTags = squatParent.getElementsByTagName("div");
173
+ lastDivTag = divTags[divTags.length - 1];
174
+ sTopMargin = (lastDivTag.className == "currentSelection") ? "6px" : "0px";
175
+ sLeftMargin = "0px";
176
+ sRightMargin = "0px";
177
+ } else {
178
+ // MIT
179
+ squatParent = getElementByClassName("div", "left-nav");
180
+ if (bMIT && squatParent) {
181
+ ulTags = squatParent.getElementsByTagName("ul");
182
+ if (ulTags)
183
+ {
184
+ ulTag = ulTags[0];
185
+ if (ulTag) ulTag.style.margin = "0px 0px 6px;";
186
+ }
187
+ squatSibling = squatParent.lastChild;
188
+ sLeftMargin = "6px";
189
+ sRightMargin = "6px";
190
+ }
191
+ // everything else
192
+ else {
193
+ if (body.parentNode) {
194
+ squatParent = body.parentNode;
195
+ squatSibling = body.nextSibling;
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ String.prototype.startsWith = function(str)
202
+ {
203
+ return (this.match("^" + str) == str)
204
+ }
205
+
206
+ function demoDomain(sUrl) {
207
+ for (nDomain = 0; nDomain < demoDomains.length; nDomain++) {
208
+ if (sUrl.startsWith(demoDomains[nDomain])) return true;
209
+ }
210
+ return folksemanticFrames(sUrl);
211
+ }
212
+ function blacklistedDomain(sUrl) {
213
+ for (nDomain = 0; nDomain < blacklistedDomains.length; nDomain++) {
214
+ if (sUrl.indexOf(blacklistedDomains[nDomain]) != -1) return true;
215
+ }
216
+ return false;
217
+ }
218
+ function folksemanticFrames(sUrl) {
219
+ return (sUrl.indexOf("show_recs=true") != -1);
220
+ }
221
+
222
+ function recList(recommendations, sDirectLinkText) {
223
+ var sRecListDivStart = '<div id="recommenderlist" style="margin: 3px; padding: 0px;">';
224
+ var sRecListDivEnd = '</div>';
225
+ var sListStart = '<ul style="margin: 0px; padding: 0px; list-style-type: none;">';
226
+ var sListEnd = '</ul>';
227
+ var sRecList = bSpecialSite == true ? '' : sListStart;
228
+ for (var i = 0; i < recommendations.length; i++)
229
+ {
230
+ // get info for the recommendation
231
+ rec_title = recommendations[i].getElementsByTagName('title')[0].textContent;
232
+ link_uri = recommendations[i].getElementsByTagName('link')[0].textContent;
233
+ collection = recommendations[i].getElementsByTagName('collection')[0].textContent;
234
+ has_direct_link = recommendations[i].getElementsByTagName('has_direct_link')[0];
235
+ rec_link = link_uri;
236
+ rec_description = recommendations[i].getElementsByTagName('description')[0].textContent;
237
+ rec_tag = '<a href="' + rec_link + '" style="color:#3987DC !important; text-decoration:none !important; " target="_top">' + rec_title + ' (' + collection + ')</a>';
238
+ direct_link_tag = has_direct_link ? ' <a href="' + rec_link + '&target=direct_link" style="color:#556664 !important; text-decoration:none !important; " target="_top">' + sDirectLinkText + '</a>' : "";
239
+
240
+ // our lists are different in educommons and on MIT's websites
241
+ if (bSpecialSite == true)
242
+ {
243
+ sRecList += "<p style='font-size:12px;'>";
244
+ sRecList += rec_tag + direct_link_tag;
245
+ sRecList += "</p>";
246
+ }
247
+ // default list styles
248
+ else
249
+ {
250
+ sRecList += '<li style="margin-left: 0px; padding: 3px; list-style-type: none; font-size: 12px; ';
251
+ if (i % 2 == 0) sRecList += 'background-color:#E6E6E6;';
252
+ sRecList += '">';
253
+ sRecList += rec_tag + direct_link_tag;
254
+ sRecList += '</li>';
255
+ }
256
+ }
257
+ if (bSpecialSite == false) sRecList += sListEnd;
258
+ return sRecList;
259
+ }
260
+
261
+ function morePrompt(root) {
262
+ var sDocID = root.getAttribute("document_id");
263
+ var sMorePrompt = root.getAttribute("more_prompt");
264
+ return sDocID == '' ? '' : '<div class="recommender_more_link" style="margin: 3px; padding: 0px;" align="center"><a href="' + sShowDocUrl + sDocID + '" style="color:#3987DC !important; text-decoration:none !important; ">' + sMorePrompt + '</a></div>';
265
+ }
266
+
267
+ function createCloseBox(recommendersquat) {
268
+ var close = window.document.createElement("div");
269
+ dom_setStyle(close,
270
+ "margin:3px; position:absolute; top:3px; right:3px; font-family:arial,sans-serif;padding:1px; width:10px; border:1px solid #BBBBBB; text-align:center; cursor:pointer; color:#BBB; font-size:11px; background-color:#fff; font-weight:bold; z-index:999;");
271
+ close.setAttribute("title", "Click to close panel");
272
+ close.addEventListener('click', function() {
273
+ this.parentNode.style.display = "none";
274
+ }, true);
275
+ close.appendChild(window.document.createTextNode("x"));
276
+ recommendersquat.appendChild(close);
277
+ }
278
+
279
+ function verticalScrollBars() {
280
+ var vHeight = 0;
281
+ if (document.all) {
282
+ if (document.documentElement) {
283
+ vHeight = document.documentElement.clientHeight;
284
+ } else {
285
+ vHeight = document.body.clientHeight
286
+ }
287
+ } else {
288
+ vHeight = window.innerHeight;
289
+ }
290
+ return (document.body.offsetHeight > vHeight);
291
+ }
292
+
293
+ function positionSquatter(recommendersquat, bRealTime) {
294
+ // we treat educommons and mit sites differently
295
+ if (bSpecialSite == true)
296
+ {
297
+ // position the squatter
298
+ recommendersquat.setAttribute("style", 'margin: ' + sTopMargin + ' ' + sRightMargin + ' ' + sBottomMargin + ' ' + sLeftMargin + '; padding: 3px; border:1px solid #000000; background-color:#FFFFFF; z-index:999; font-size: 12px; font-family:Arial, Helvetica, sans-serif !important; ');
299
+ }
300
+ // default positioning and styling
301
+ else {
302
+ // calculate the position for the recommender div
303
+ var squatterWidth = 250;
304
+ var scrollWidth = verticalScrollBars() ? 20 : 5;
305
+ var rightAligned = window.innerWidth - squatterWidth - scrollWidth;
306
+
307
+ // position the squatter
308
+ if (bRealTime){
309
+ style = 'margin:0px; border:none; width:' + squatterWidth + 'px; position:absolute; left:' + rightAligned + 'px; top:2px; background-color:transparent; z-index:999;';
310
+ }else{
311
+ style = 'margin:0px; font-size: 12px; font-family:Arial, Helvetica, sans-serif !important; border:1px solid #000; width:' + squatterWidth + 'px; position:absolute; left:' + rightAligned + 'px; top:2px; background-color:#FFF; z-index:999;';
312
+ }
313
+ recommendersquat.setAttribute("style", style);
314
+
315
+ recommendersquat.drag = new Drag(document.getElementById('recommendertop'), recommendersquat);
316
+ createCloseBox(recommendersquat);
317
+ }
318
+ }
319
+
320
+ function squatterHeader(sTitle, bFrame) {
321
+ border = bFrame ? "border-top:1px solid black;border-right:1px solid black;border-left:1px solid black;width:240px;" : "border:none;";
322
+ return '<div id="recommendertop" style="color:#777;background-color:white;font-weight:bold;font-size:16px;font-family:Arial,Helvetica,sans-serif;margin: 2px 3px 0 3px; padding: 2px;' + border + '"><img src="' + sBaseUrl + '/images/folksemantic/logo-folksemantic-gm.gif" style="vertical-align:middle;"/>&nbsp;&nbsp;' + sTitle + '</div>';
323
+ }
324
+
325
+ function squatterContent(root, recommendations) {
326
+ return squatterHeader(root.getAttribute("title"),false) + recList(recommendations, root.getAttribute("direct_link_text")) + morePrompt(root);
327
+ }
328
+
329
+ function displayGetRecsButton(root,sUrl) {
330
+ src = sBaseUrl + 'recommendations/get_button?u=' + escape(sUrl);
331
+ header = squatterHeader(root.getAttribute("title"),true);
332
+ displaySquatter(header + '<iframe allowtransparency="true" scrolling="false" width="250px" height="280px" frameborder="0" src="' + src + '"></iframe>', true);
333
+ }
334
+
335
+ function displaySquatter(sContent, bRealTime) {
336
+ var recommendersquat = document.createElement('div');
337
+ recommendersquat.innerHTML = sContent;
338
+ squatParent.insertBefore(recommendersquat, squatSibling);
339
+ positionSquatter(recommendersquat, bRealTime);
340
+ }
341
+
342
+ function getRecs(sUrl) {
343
+ GM_xmlhttpRequest(
344
+ {
345
+ method: 'GET',
346
+ url: sUrl,
347
+ headers: {'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3'},
348
+
349
+ onload: function(responseDetails)
350
+ {
351
+ var dom = new DOMParser().parseFromString(responseDetails.responseText, "application/xml");
352
+ var root = dom.getElementsByTagName('recommendations')[0];
353
+ var recommendations = dom.getElementsByTagName('recommendation');
354
+ if (recommendations.length > 0) {
355
+ displaySquatter(squatterContent(root, recommendations), false);
356
+ } else {
357
+ displayGetRecsButton(root,sUrlToGetRecsFor);
358
+ }
359
+ }
360
+ });
361
+ }
362
+
363
+ var sBaseUrl = 'http://www.folksemantic.com/';
364
+ //var sBaseUrl = 'http://localhost:3000/';
365
+ var sServiceUrl = sBaseUrl + 'recommendations.xml?';
366
+ var sShowDocUrl = sBaseUrl + 'resources/';
367
+ var squatParent = null;
368
+ var squatSibling = null;
369
+ var sTopMargin = "3px";
370
+ var sRightMargin = "3px";
371
+ var sBottomMargin = "40px";
372
+ var sLeftMargin = "3px";
373
+ var bSpecialSite = false;
374
+ var bNeeds = false;
375
+ var bMIT = false;
376
+ var bNeedsRecord = false;
377
+ var body = null;
378
+ var bodyTags = document.getElementsByTagName('body');
379
+ var demoDomains = ['http://ocw.mit.edu','http://www.engineeringpathway.com'];
380
+ var blacklistedDomains = ['google.com','localhost','folksemantic.com','ocwfinder.org','ocwfinder.com','oerrecommender.org'];
381
+ if (sUrlToGetRecsFor.indexOf('http') == 0 && bodyTags && bodyTags.length > 0)
382
+ {
383
+ sUrlToGetRecsFor = special_website_url(sUrlToGetRecsFor);
384
+ body = bodyTags[0];
385
+ if ((bNeeds == true && bNeedsRecord == true) || bNeeds == false) {
386
+ identifySquatParent();
387
+ }
388
+ if (squatParent != null) {
389
+ if (!blacklistedDomain(sUrlToGetRecsFor))
390
+ getRecs(sServiceUrl + "u=" + sUrlToGetRecsFor);
391
+ }
392
+ }