wontomedia 0.1.1 → 0.2.0

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