muck-services 0.1.10 → 0.1.11

Sign up to get free protection for your applications and to get access to all the features.
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
+ }