wontomedia 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/Rakefile +9 -5
  2. data/VERSION.yml +2 -2
  3. data/app/controllers/admin_controller.rb +34 -11
  4. data/app/controllers/connections_controller.rb +8 -2
  5. data/app/controllers/items_controller.rb +213 -68
  6. data/app/helpers/format_helper.rb +22 -1
  7. data/app/helpers/items_helper.rb +43 -0
  8. data/app/models/connection.rb +128 -100
  9. data/app/models/item.rb +96 -0
  10. data/app/views/connections/_spo_select_controls.html.erb +65 -31
  11. data/app/views/connections/edit.html.erb +7 -2
  12. data/app/views/connections/index.html.erb +11 -4
  13. data/app/views/connections/show.html.erb +13 -5
  14. data/app/views/items/_class_select.html.erb +41 -0
  15. data/app/views/items/_content_examples.html.erb +21 -18
  16. data/app/views/items/_core_tasks.html.erb +40 -5
  17. data/app/views/items/_form_fields.html.erb +4 -4
  18. data/app/views/items/_inline_item_add_form.html.erb +48 -0
  19. data/app/views/items/_inline_scalar_add_form.html.erb +36 -0
  20. data/app/views/items/_most_populous_classes.html.erb +42 -0
  21. data/app/views/items/_type_select.html.erb +28 -26
  22. data/app/views/items/edit.html.erb +8 -2
  23. data/app/views/items/index.html.erb +5 -1
  24. data/app/views/items/new.html.erb +69 -54
  25. data/app/views/items/newpop.html.erb +18 -3
  26. data/app/views/items/show.html.erb +110 -7
  27. data/app/views/layouts/application.html.erb +2 -2
  28. data/app/views/layouts/base.html.erb +4 -2
  29. data/app/views/layouts/home.html.erb +2 -2
  30. data/assets/wontomedia-sample.rb +2 -0
  31. data/config/asset_packages.yml +1 -0
  32. data/config/cucumber.yml +11 -13
  33. data/db/fixtures/connections.yml +85 -4
  34. data/db/fixtures/items.yml +140 -8
  35. data/db/migrate/20100315135952_provide_scalar_objects.rb +32 -0
  36. data/db/migrate/20100321042343_add_timestamp_columns.rb +33 -0
  37. data/db/schema.rb +17 -11
  38. data/default-custom/app/views/items/home.html.erb +1 -1
  39. data/default-custom/public/stylesheets/wm.css +21 -4
  40. data/lib/helpers/connection_helper.rb +84 -1
  41. data/lib/helpers/item_helper.rb +16 -3
  42. data/lib/tasks/cucumber.rake +0 -2
  43. data/public/images/transparent_ltblue_background.png +0 -0
  44. data/public/images/{alert_background.png → transparent_white_background.png} +0 -0
  45. data/public/javascripts/forConnectionsForms.js +182 -41
  46. data/public/javascripts/forItemsForms.js +40 -5
  47. data/public/javascripts/forItemsShow.js +27 -0
  48. data/public/javascripts/itemCreatePopup.js +10 -3
  49. metadata +13 -5
@@ -0,0 +1,32 @@
1
+ # WontoMedia - a wontology web application
2
+ # Copyright (C) 2010 - Glen E. Ivey
3
+ # www.wontology.com
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License version
7
+ # 3 as published by the Free Software Foundation.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program in the file COPYING and/or LICENSE. If not,
16
+ # see <http://www.gnu.org/licenses/>.
17
+
18
+
19
+ class ProvideScalarObjects < ActiveRecord::Migration
20
+ def self.up
21
+ add_column :connections, :kind_of_obj, :string # really an enum
22
+ add_column :connections, :scalar_obj, :string
23
+
24
+ # all existing Connections have Items as their objects
25
+ execute 'UPDATE connections SET kind_of_obj="item"'
26
+ end
27
+
28
+ def self.down
29
+ remove_column :connections, :kind_of_object
30
+ remove_column :connections, :scalar_obj
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ # WontoMedia - a wontology web application
2
+ # Copyright (C) 2010 - Glen E. Ivey
3
+ # www.wontology.com
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU Affero General Public License version
7
+ # 3 as published by the Free Software Foundation.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program in the file COPYING and/or LICENSE. If not,
16
+ # see <http://www.gnu.org/licenses/>.
17
+
18
+
19
+ class AddTimestampColumns < ActiveRecord::Migration
20
+ def self.up
21
+ add_column :items, :created_at, :datetime
22
+ add_column :items, :updated_at, :datetime
23
+ add_column :connections, :created_at, :datetime
24
+ add_column :connections, :updated_at, :datetime
25
+ end
26
+
27
+ def self.down
28
+ remove_column :items, :created_at
29
+ remove_column :items, :updated_at
30
+ remove_column :connections, :created_at
31
+ remove_column :connections, :updated_at
32
+ end
33
+ end
@@ -9,22 +9,28 @@
9
9
  #
10
10
  # It's strongly recommended to check this file into your version control system.
11
11
 
12
- ActiveRecord::Schema.define(:version => 20090605215028) do
12
+ ActiveRecord::Schema.define(:version => 20100321042343) do
13
13
 
14
14
  create_table "connections", :force => true do |t|
15
- t.integer "subject_id"
16
- t.integer "predicate_id"
17
- t.integer "obj_id"
18
- t.integer "connection_desc_id"
19
- t.integer "flags", :default => 0, :null => false
15
+ t.integer "subject_id"
16
+ t.integer "predicate_id"
17
+ t.integer "obj_id"
18
+ t.integer "connection_desc_id"
19
+ t.integer "flags", :default => 0, :null => false
20
+ t.string "kind_of_obj"
21
+ t.string "scalar_obj"
22
+ t.datetime "created_at"
23
+ t.datetime "updated_at"
20
24
  end
21
25
 
22
26
  create_table "items", :force => true do |t|
23
- t.string "name"
24
- t.string "title"
25
- t.text "description"
26
- t.string "sti_type"
27
- t.integer "flags", :default => 0, :null => false
27
+ t.string "name"
28
+ t.string "title"
29
+ t.text "description"
30
+ t.string "sti_type"
31
+ t.integer "flags", :default => 0, :null => false
32
+ t.datetime "created_at"
33
+ t.datetime "updated_at"
28
34
  end
29
35
 
30
36
  end
@@ -42,7 +42,7 @@
42
42
  <%= render :partial => "content_examples" %>
43
43
  <% end -%>
44
44
  <% content_for :content_lower_narrow do %>
45
- <%= render :partial => "screen_select" %>
45
+ <%= render :partial => "most_populous_classes" %>
46
46
  <% end -%>
47
47
  <% content_for :content_bottom do %>
48
48
  <div class="horizontal-link-list">
@@ -63,6 +63,14 @@ div#fancybox-outer {
63
63
 
64
64
 
65
65
  /* settings for the basic division of pages into areas and boxes */
66
+ .page-title-text {
67
+ position: absolute;
68
+ top: 0;
69
+ left: 15.5%;
70
+ height: 8ex;
71
+ width: 69.7%;
72
+ overflow: hidden;
73
+ }
66
74
  .content {
67
75
  padding: 2px;
68
76
  background-color: white;
@@ -89,12 +97,21 @@ div#fancybox-outer {
89
97
  .headline:first-child {
90
98
  margin-top: 0;
91
99
  }
92
- .subheading {
100
+
101
+ h2.subheading {
93
102
  font-size: 150%;
103
+ }
104
+ h3.subheading {
105
+ font-size: 120%;
106
+ text-decoration: underline;
107
+ }
108
+ .subheading {
94
109
  font-weight: bold;
95
110
  color: #b0b0ff;
111
+ margin-top: 0;
96
112
  margin-bottom: 1.3ex;
97
113
  }
114
+
98
115
  .fine-print {
99
116
  font-size: 60%;
100
117
  color: #040460;
@@ -212,10 +229,10 @@ p + table { margin-bottom: 1.5em; }
212
229
 
213
230
  #block-help {
214
231
  float: right;
215
- border-width: 1px;
232
+ border-width: 2px;
216
233
  border-color: #e8e8ff;
217
234
  border-style: none solid solid;
218
- background: #f8f8ff;
235
+ background: url("/images/transparent_ltblue_background.png") repeat;
219
236
  padding: 0 0.3em;
220
237
  height: 2.7ex;
221
238
  font-size: 200%;
@@ -229,7 +246,7 @@ p + table { margin-bottom: 1.5em; }
229
246
  z-index: 150;
230
247
  font-size: 110%;
231
248
  font-weight: bold;
232
- background: url("/images/alert_background.png") repeat;
249
+ background: url("/images/transparent_white_background.png") repeat;
233
250
  text-align: center;
234
251
  }
235
252
  #alert_box_close_link {
@@ -34,8 +34,91 @@ module ConnectionHelper
34
34
  result = ""
35
35
  connections.each do |connection|
36
36
  result << "<##{connection.subject.name}> <##{connection.predicate.name}> "
37
- result << "<##{connection.obj.name}> .\n"
37
+ if connection.kind_of_obj == Connection::OBJECT_KIND_ITEM
38
+ result << "<##{connection.obj.name}> .\n"
39
+ else
40
+ result << "\"#{connection.scalar_obj}\" .\n"
41
+ end
38
42
  end
39
43
  result
40
44
  end
45
+
46
+ # An operator usable for sorting, returns 0 for a==b, <0 for a<b
47
+ def self.compare(a, b, factor_hash = {})
48
+ # sort relationships using built-in predicates to the bottom
49
+ aflag = a.predicate.flags & Item::DATA_IS_UNALTERABLE
50
+ bflag = b.predicate.flags & Item::DATA_IS_UNALTERABLE
51
+ return aflag - bflag if aflag != bflag
52
+
53
+ # sort relationships to scalar values above those with item objects
54
+ # connection with filled-in scalar valued object
55
+ if a.kind_of_obj && b.kind_of_obj && a.kind_of_obj != b.kind_of_obj
56
+ return a.kind_of_obj == Connection::OBJECT_KIND_ITEM ? 1 : -1
57
+ end
58
+ if a.kind_of_obj.nil? || b.kind_of_obj.nil?
59
+ # connection with blank object intended to be a scalar
60
+ a_kind = Connection.first( :conditions => [
61
+ "subject_id = ? AND predicate_id = ?", a.predicate_id,
62
+ Item.find_by_name('has_scalar_object') ])
63
+ b_kind = Connection.first( :conditions => [
64
+ "subject_id = ? AND predicate_id = ?", b.predicate_id,
65
+ Item.find_by_name('has_scalar_object') ])
66
+ if a_kind != b_kind
67
+ return a_kind.nil? ? 1 : -1
68
+ end
69
+ end
70
+
71
+ # relationships w/ predicates constrained by max_uses_per_item:
72
+ # sort lower maximums closer to top of list. Sort unconstrained
73
+ # predicates as "infinite maximum"
74
+ max_uses = Item.find_by_name('max_uses_per_item')
75
+ a_max = Connection.first( :conditions => [
76
+ "subject_id = ? AND predicate_id = ?", a.predicate_id, max_uses.id ])
77
+ unless a_max.nil?
78
+ a_max = a_max.scalar_obj.nil? ? nil : a_max.scalar_obj.to_i
79
+ end
80
+ b_max = Connection.first( :conditions => [
81
+ "subject_id = ? AND predicate_id = ?", b.predicate_id, max_uses.id ])
82
+ unless b_max.nil?
83
+ b_max = b_max.scalar_obj.nil? ? nil : b_max.scalar_obj.to_i
84
+ end
85
+ unless a_max == b_max
86
+ return -1 if b_max.nil?
87
+ return 1 if a_max.nil?
88
+ return a_max - b_max
89
+ end
90
+
91
+ a_id = a.predicate_id
92
+ b_id = b.predicate_id
93
+
94
+ # sort by "predicate external factor" hash, higher numbers better
95
+ # (this is intended to support, among other things, sorting based
96
+ # on the number of times the same predicate is used in the list of
97
+ # connections being sorted. Requires caller to make a counting
98
+ # pass over the sort set first to build the hash
99
+ a_count = factor_hash[a_id].nil? ? 0 : factor_hash[a_id]
100
+ b_count = factor_hash[b_id].nil? ? 0 : factor_hash[b_id]
101
+ return b_count - a_count if a_count != b_count
102
+
103
+ # sort by property ID order (this is essentially arbitrary, but
104
+ # ensures the sort order is stable and that subsequent factors
105
+ # only affect sorting within groups of connections all referencing
106
+ # the same property item)
107
+ return a_id - b_id if a_id != b_id
108
+
109
+ # sort connections with missing objects (e.g. "add here"
110
+ # place-holders) below others
111
+ a_blank = a.kind_of_obj.nil? ||
112
+ (a.kind_of_obj == Connection::OBJECT_KIND_ITEM ?
113
+ a.obj : a.scalar_obj ) == nil
114
+ b_blank = b.kind_of_obj.nil? ||
115
+ (b.kind_of_obj == Connection::OBJECT_KIND_ITEM ?
116
+ b.obj : b.scalar_obj ) == nil
117
+ return a_blank ? 1 : -1 if a_blank != b_blank
118
+
119
+ # sort connections whose objects are built-in below others
120
+ aflag = a.obj.nil? ? 0 : a.obj.flags & Item::DATA_IS_UNALTERABLE
121
+ bflag = b.obj.nil? ? 0 : b.obj.flags & Item::DATA_IS_UNALTERABLE
122
+ return aflag - bflag
123
+ end
41
124
  end
@@ -119,10 +119,23 @@ module ItemHelper
119
119
 
120
120
  # Takes an Item object, +n+, and returns a symbol-indexed hash
121
121
  # containing each of the Item's data fields.
122
- def self.item_to_hash(n)
122
+ def self.item_to_hash(item)
123
123
  # Really ought to be doing this programatically from the schema....
124
- { :id => n.id, :name => n.name, :title => n.title, :flags => n.flags,
125
- :description => n.description, :sti_type => n.sti_type }
124
+ { :id => item.id, :name => item.name, :title => item.title,
125
+ :flags => item.flags, :description => item.description,
126
+ :sti_type => item.sti_type, :class_item_id => item.class_item_id }
127
+ end
128
+
129
+ # Map from an Item.name of a built-in item representing an item type
130
+ # (e.g., is_instance_of ItemType_Item) to the matching internal
131
+ # Item.sti_type string
132
+ def self.sti_type_for_ItemType( type_item_name )
133
+ case type_item_name
134
+ when 'Value_ItemType_Category' then 'CategoryItem'
135
+ when 'Value_ItemType_Individual' then 'IndividualItem'
136
+ when 'Value_ItemType_Property' then 'PropertyItem'
137
+ else ''
138
+ end
126
139
  end
127
140
 
128
141
 
@@ -36,14 +36,12 @@ begin
36
36
  Cucumber::Rake::Task.new({:static_ok => 'db:test:prepare'},
37
37
  'Run non-Selenium features that should pass') do |t|
38
38
  t.binary = vendored_cucumber_binary
39
- t.fork = false
40
39
  t.profile = "static_acceptance"
41
40
  end
42
41
 
43
42
  Cucumber::Rake::Task.new({:dynamic_ok => 'db:test:prepare'},
44
43
  'Run need-Selenium-to-test features that should pass') do |t|
45
44
  t.binary = vendored_cucumber_binary
46
- t.fork = false
47
45
  t.profile = "dynamic_acceptance"
48
46
  end
49
47
 
@@ -16,22 +16,38 @@
16
16
  // see <http://www.gnu.org/licenses/>.
17
17
 
18
18
 
19
- var selectElems = [ "subject", "predicate", "obj", "submit" ];
20
- var connectionSubmit;
21
- var l = window.location;
22
- var base = l.protocol + "//" + l.hostname + ":" + l.port;
23
- var lastValue = {}
24
-
19
+ var selectElems = [ "subject", "predicate", "obj" ];
25
20
 
26
21
  // define fields subject to check, order they occur in form
27
- var requiredConnectionElements = [ "subject", "predicate", "obj", "submit" ];
28
- var connectionElementNames = [ "Subject selector", "Relationship selector",
29
- "Object selector" ];
22
+ // these arrays must have same index order:
23
+ var requiredConnectionElements =
24
+ [ "subject", "predicate", "kind_of_obj", "obj", "submit" ];
25
+ var connectionElementNames =
26
+ [ "Subject selector", "Relationship selector",
27
+ "Object kind", "Object content" ];
28
+ var controlIds =
29
+ [ [ "connection_subject_id" ],
30
+ [ "connection_predicate_id" ],
31
+ [ "connection_kind_of_obj_scalar", "connection_kind_of_obj_item" ],
32
+ [ "connection_scalar_obj", "connection_obj_id" ],
33
+ [ "connection_submit" ] // this one doesn't really matter--won't be used
34
+ ];
35
+ var controlTypes =
36
+ [ [ "value" ],
37
+ [ "value" ],
38
+ [ "checked", "checked" ],
39
+ [ "value", "value" ],
40
+ [ "checked" ] // this one doesn't really matter--won't be used
41
+ ];
30
42
 
31
43
  // encoding: -1 -> haven't checked yet
32
44
  // false -> no error
33
45
  // true -> error condition present
34
- var connectionFormErrors = {};
46
+ var connectionFormErrors = {}; // don't display errors until user goes past
47
+
48
+ var lastValue = {} // don't error check unless changed
49
+ var connectionSubmit; // $(submit-botton) for convenience
50
+
35
51
 
36
52
 
37
53
  if ((typeof inAConnectionsForm !== 'undefined') && inAConnectionsForm){
@@ -40,7 +56,8 @@ if ((typeof inAConnectionsForm !== 'undefined') && inAConnectionsForm){
40
56
  lastValue = {
41
57
  subject : $('connection_subject_id').value,
42
58
  predicate : $('connection_predicate_id').value,
43
- obj : $('connection_obj_id').value
59
+ obj : $('connection_obj_id').value,
60
+ scalar : $('connection_scalar_obj').value
44
61
  };
45
62
 
46
63
  for (var c=0; c < requiredConnectionElements.length-1; c++)
@@ -48,24 +65,31 @@ if ((typeof inAConnectionsForm !== 'undefined') && inAConnectionsForm){
48
65
  creatingNewConnection ? -1 : false;
49
66
 
50
67
  for (var c=0; c < selectElems.length; c++){
51
- createOnchangeHandler(selectElems[c]);
52
- createOnfocusHandler(selectElems[c]);
68
+ createIdOnchangeHandler(selectElems[c]);
69
+ createIdOnfocusHandler(selectElems[c]);
53
70
  }
71
+ createKindHandlers();
72
+ createScalarHandlers();
54
73
 
55
74
  if (creatingNewConnection)
56
75
  // connections/new -- can't submit a blank form
57
76
  makeButtonSeemDisabled(connectionSubmit);
58
77
  else // connections/##/edit -- can submit as-is form
59
78
  makeButtonSeemEnabled(connectionSubmit);
60
-
61
79
  connectionSubmit.observe('click', submitOnclickHandler);
80
+ connectionSubmit.observe('focus',
81
+ function(){ onfocusBehavior(connectionSubmit); } );
82
+
83
+ // do after browser has done final layout of all the select controls
84
+ setDescDivsWidths([ 'subject', 'predicate', 'obj' ]);
85
+ syncEnabledStateOfObjectControls();
62
86
  }
63
87
 
64
88
 
65
- function createOnchangeHandler(thisName){
89
+ function createIdOnchangeHandler(thisName){
66
90
  var thisElem = $('connection_' + thisName + '_id');
67
- if (thisElem == null)
68
- thisElem = $('connection_' + thisName);
91
+ if (thisElem == null) // only deal with ID select controls
92
+ return;
69
93
  thisElem.observe('change',
70
94
  function(){ // nest all these fn defs for closure
71
95
  if (thisElem.value != lastValue[thisName]){
@@ -78,23 +102,12 @@ function createOnchangeHandler(thisName){
78
102
  }
79
103
  else if (thisElem.value == "-1"){
80
104
  divToBlank(thisName);
81
- var type = (thisName == "predicate") ? "verb" : "noun";
82
- itemCreatePopup(thisElem, type, lastLast);
105
+ var popupType = (thisName == "predicate") ? "verb" : "noun";
106
+ itemCreatePopup(thisElem, lastLast, popupType, null);
83
107
  }
84
108
  else {
85
109
  clearError(thisName);
86
- divToWorking(thisName);
87
- new Ajax.Request(base + "/w/items/" + thisElem.value + ".json", {
88
- method: 'get',
89
- onSuccess: function(response){
90
- var itemObject = response.responseJSON;
91
- var key = getFirstHashKey(itemObject);
92
- divToText(thisName, itemObject[key]["description"]);
93
- },
94
- onFailure: function(){
95
- divToBlank(thisName);
96
- }
97
- });
110
+ loadItemDescDiv(thisName, thisElem);
98
111
  }
99
112
  refreshSubmitState();
100
113
  }
@@ -102,6 +115,44 @@ function createOnchangeHandler(thisName){
102
115
  );
103
116
  }
104
117
 
118
+ function loadItemDescDiv(thisName, thisElem){
119
+ divToWorking(thisName);
120
+
121
+ var winloc = window.location;
122
+ var base = winloc.protocol + "//" + winloc.hostname + ":" + winloc.port;
123
+ new Ajax.Request(base + "/w/items/" + thisElem.value + ".json", {
124
+ method: 'get',
125
+ onSuccess: function(response){
126
+ var itemObject = response.responseJSON;
127
+ var key = getFirstHashKey(itemObject);
128
+ divToText(thisName, itemObject[key]["description"]);
129
+ },
130
+ onFailure: function(){
131
+ divToBlank(thisName);
132
+ }
133
+ });
134
+ }
135
+
136
+ function createScalarHandlers(){
137
+ var name = 'scalar';
138
+ var scalar = $("connection_scalar_obj");
139
+
140
+ scalar.observe('change',
141
+ function(){ // nest all these fn defs for closure
142
+ if (scalar.value != lastValue[name]){
143
+ var lastLast = lastValue[name];
144
+ lastValue[name] = scalar.value;
145
+
146
+ if (scalar.value == "") // pretend we're same as 'obj_id' select
147
+ flagConnectionAsRequired('obj');
148
+ else
149
+ clearError('obj');
150
+ refreshSubmitState();
151
+ }
152
+ }
153
+ );
154
+ }
155
+
105
156
  function getFirstHashKey(hashObj){
106
157
  for (var key in hashObj)
107
158
  return key;
@@ -130,19 +181,57 @@ function divToText(divName, divText){
130
181
  }
131
182
 
132
183
 
133
- function createOnfocusHandler(thisName){
184
+ function createIdOnfocusHandler(thisName){
134
185
  var thisElem = $('connection_' + thisName + '_id');
135
186
  if (thisElem == null)
136
- thisElem = $('connection_' + thisName);
187
+ return;
188
+ thisElem.observe('focus', function(){ onfocusBehavior(thisElem); } );
189
+ }
190
+
191
+ function createKindHandlers(){
192
+ thisElem = $('connection_kind_of_obj_scalar');
193
+ thisElem.observe('focus', function(){ onfocusBehavior(thisElem); } );
194
+ thisElem.observe('change', kindOnchangeBehavior);
195
+
196
+ thisElem = $('connection_kind_of_obj_item');
137
197
  thisElem.observe('focus', function(){ onfocusBehavior(thisElem); } );
198
+ thisElem.observe('change', kindOnchangeBehavior);
199
+ }
200
+
201
+ function kindOnchangeBehavior(){
202
+ syncEnabledStateOfObjectControls();
203
+
204
+ var itemChecked = $('connection_kind_of_obj_item' ).checked;
205
+ var scalarChecked = $('connection_kind_of_obj_scalar').checked;
206
+
207
+ var hasAValue = itemChecked || scalarChecked;
208
+ if (hasAValue)
209
+ clearError('kind_of_obj');
210
+ else
211
+ flagConnectionAsRequired('kind_of_obj');
212
+
213
+ if (connectionFormErrors['obj'] != -1){
214
+ var value = null;
215
+ if (itemChecked)
216
+ value = $('connection_obj_id').value;
217
+ else
218
+ value = $('connection_scalar_obj').value;
219
+ if (value != null && value != "")
220
+ clearError('obj'); // if selected control has a value, great
221
+ else
222
+ flagConnectionAsRequired('obj'); // otherwise, error (ignore other ctrl)
223
+ }
224
+
225
+ refreshSubmitState();
138
226
  }
139
227
 
140
228
  function onfocusBehavior(elem){
141
- var eId = elem.id;
229
+ // find this control's position in flag arrays. Value of rCE[c] is
230
+ // always a substring of matching control's id=''
142
231
  var c;
143
232
  for (c=0; c < requiredConnectionElements.length; c++){
144
233
  var re = new RegExp(requiredConnectionElements[c]);
145
- var mtch = eId.match(re);
234
+ var mtch = elem.id.match(re);
146
235
  if (mtch != null && mtch.length > 0)
147
236
  break;
148
237
  }
@@ -152,27 +241,56 @@ function onfocusBehavior(elem){
152
241
 
153
242
  var lastToCheck = c-1;
154
243
  for (c=0; c <= lastToCheck; c++){
155
- var ck = $("connection_" + requiredConnectionElements[c] + "_id");
156
- if (ck.value == null || ck.value == "")
157
- flagConnectionAsRequired(requiredConnectionElements[c]);
158
- else
244
+ var hasAValue = false;
245
+ var equivalentIds = controlIds[c];
246
+
247
+ perControlLoop:
248
+ for (var cn=0; cn < equivalentIds.length; cn++){
249
+
250
+ control = $(equivalentIds[cn]);
251
+ if (control.disabled)
252
+ continue perControlLoop;
253
+
254
+ if (controlTypes[c][cn] == "checked"){
255
+ if (control.checked == true){
256
+ hasAValue = true;
257
+ break;
258
+ }
259
+ }
260
+ else { // Type == "value"
261
+ if (control.value != null && control.value != ""){
262
+ hasAValue = true;
263
+ break;
264
+ }
265
+ }
266
+ }
267
+
268
+ if (hasAValue)
159
269
  clearError(requiredConnectionElements[c]);
270
+ else
271
+ flagConnectionAsRequired(requiredConnectionElements[c]);
160
272
  }
161
273
  refreshSubmitState();
162
274
  }
163
275
 
164
276
  function flagConnectionAsRequired(elemName){
165
277
  connectionFormErrors[elemName] = true;
166
- $(elemName + "_required").className = "helpTextFlagged";
278
+ maybeSetTextClass(elemName, "helpTextFlagged");
167
279
  $(elemName + "_error_icon").src = "/images/error_error_icon.png";
168
280
  }
169
281
 
170
282
  function clearError(elemName){
171
283
  connectionFormErrors[elemName] = false;
172
- $(elemName + "_required").className = "";
284
+ maybeSetTextClass(elemName, "");
173
285
  $(elemName + "_error_icon").src = "/images/blank_error_icon.png";
174
286
  }
175
287
 
288
+ function maybeSetTextClass(elemName, newClass){
289
+ var requiredText = $(elemName + "_required");
290
+ if (requiredText)
291
+ requiredText.className = newClass
292
+ }
293
+
176
294
  function refreshSubmitState(){
177
295
  var detectedAtLeastOneError = false;
178
296
  for (var c =0; c < requiredConnectionElements.length-1; c++)
@@ -216,3 +334,26 @@ function maybeShowErrorDialog(){
216
334
  }
217
335
  return accum;
218
336
  }
337
+
338
+ function syncEnabledStateOfObjectControls(){
339
+ var scalarDisabled = !($('connection_kind_of_obj_scalar').checked);
340
+ $('connection_scalar_obj').disabled = scalarDisabled;
341
+ if (!scalarDisabled){
342
+ divToBlank('obj');
343
+ lastValue['obj'] = "";
344
+ }
345
+
346
+ var idDisabled = !($('connection_kind_of_obj_item')).checked;
347
+ control = $('connection_obj_id');
348
+ control.disabled = idDisabled;
349
+ if (!idDisabled && (control.value != lastValue['obj']) )
350
+ loadItemDescDiv('obj', control);
351
+ }
352
+
353
+ function setDescDivsWidths(names){
354
+ for (var c=0; c < names.length; c++){
355
+ var descDiv = $(names[c] + "_desc");
356
+ var w = $("connection_" + names[c] + "_id").getWidth();
357
+ descDiv.style.width = w + "px";
358
+ }
359
+ }