hobo 0.7.3 → 0.7.4

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 (46) hide show
  1. data/bin/hobo +1 -1
  2. data/hobo_files/plugin/CHANGES.txt +302 -0
  3. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +2 -9
  4. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +1 -1
  5. data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +0 -2
  6. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +76 -46
  7. data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +25 -18
  8. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +29 -11
  9. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +2 -2
  10. data/hobo_files/plugin/init.rb +0 -1
  11. data/hobo_files/plugin/lib/active_record/has_many_association.rb +3 -0
  12. data/hobo_files/plugin/lib/hobo.rb +12 -8
  13. data/hobo_files/plugin/lib/hobo/bundle.rb +1 -1
  14. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +1 -1
  15. data/hobo_files/plugin/lib/hobo/dryml/parser/attribute.rb +41 -0
  16. data/hobo_files/plugin/lib/hobo/dryml/parser/base_parser.rb +253 -0
  17. data/hobo_files/plugin/lib/hobo/dryml/parser/document.rb +26 -0
  18. data/hobo_files/plugin/lib/hobo/dryml/parser/element.rb +27 -0
  19. data/hobo_files/plugin/lib/hobo/dryml/parser/elements.rb +45 -0
  20. data/hobo_files/plugin/lib/hobo/dryml/parser/source.rb +58 -0
  21. data/hobo_files/plugin/lib/hobo/dryml/parser/text.rb +13 -0
  22. data/hobo_files/plugin/lib/hobo/dryml/parser/tree_parser.rb +67 -0
  23. data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +10 -5
  24. data/hobo_files/plugin/lib/hobo/dryml/template.rb +48 -27
  25. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +28 -13
  26. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +3 -1
  27. data/hobo_files/plugin/lib/hobo/model.rb +70 -10
  28. data/hobo_files/plugin/lib/hobo/model_controller.rb +49 -34
  29. data/hobo_files/plugin/lib/hobo/model_router.rb +10 -2
  30. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +1 -0
  31. data/hobo_files/plugin/lib/hobo/scopes.rb +15 -0
  32. data/hobo_files/plugin/lib/hobo/scopes/apply_scopes.rb +23 -0
  33. data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +4 -2
  34. data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +34 -7
  35. data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +3 -1
  36. data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +1 -5
  37. data/hobo_files/plugin/taglibs/rapid.dryml +33 -24
  38. data/hobo_files/plugin/taglibs/rapid_editing.dryml +6 -5
  39. data/hobo_files/plugin/taglibs/rapid_forms.dryml +37 -31
  40. data/hobo_files/plugin/taglibs/rapid_generics.dryml +68 -27
  41. data/hobo_files/plugin/taglibs/rapid_navigation.dryml +5 -8
  42. data/hobo_files/plugin/taglibs/rapid_pages.dryml +71 -47
  43. data/hobo_files/plugin/taglibs/rapid_plus.dryml +4 -5
  44. data/hobo_files/plugin/taglibs/rapid_support.dryml +11 -4
  45. metadata +23 -6
  46. data/hobo_files/plugin/lib/rexml.rb +0 -443
@@ -1,8 +1,8 @@
1
1
  LowPro = {};
2
2
  LowPro.Version = '0.5';
3
- LowPro.CompatibleWithPrototype = '1.6.0';
3
+ LowPro.CompatibleWithPrototype = '1.6';
4
4
 
5
- if (Prototype.Version != LowPro.CompatibleWithPrototype && console && console.warn)
5
+ if (Prototype.Version.indexOf(LowPro.CompatibleWithPrototype) != 0 && window.console && window.console.warn)
6
6
  console.warn("This version of Low Pro is tested with Prototype " + LowPro.CompatibleWithPrototype +
7
7
  " it may not work as expected with this version (" + Prototype.Version + ")");
8
8
 
@@ -15,19 +15,19 @@ DOM = {};
15
15
  // DOMBuilder for prototype
16
16
  DOM.Builder = {
17
17
  tagFunc : function(tag) {
18
- return function() {
19
- var attrs, children;
20
- if (arguments.length>0) {
21
- if (arguments[0].nodeName ||
22
- typeof arguments[0] == "string")
23
- children = arguments;
24
- else {
25
- attrs = arguments[0];
26
- children = Array.prototype.slice.call(arguments, 1);
27
- };
28
- }
29
- return DOM.Builder.create(tag, attrs, children);
30
- };
18
+ return function() {
19
+ var attrs, children;
20
+ if (arguments.length>0) {
21
+ if (arguments[0].constructor == Object) {
22
+ attrs = arguments[0];
23
+ children = Array.prototype.slice.call(arguments, 1);
24
+ } else {
25
+ children = arguments;
26
+ };
27
+ children = $A(children).flatten()
28
+ }
29
+ return DOM.Builder.create(tag, attrs, children);
30
+ };
31
31
  },
32
32
  create : function(tag, attrs, children) {
33
33
  attrs = attrs || {}; children = children || []; tag = tag.toLowerCase();
@@ -71,7 +71,8 @@ DOM.Builder.fromHTML = function(html) {
71
71
  // Event.onReady(callbackFunction);
72
72
  Object.extend(Event, {
73
73
  onReady : function(f) {
74
- document.observe('dom:loaded', f);
74
+ if (document.body) f();
75
+ else document.observe('dom:loaded', f);
75
76
  }
76
77
  });
77
78
 
@@ -96,7 +97,7 @@ Event.addBehavior = function(rules) {
96
97
  Ajax.Responders.register({
97
98
  onComplete : function() {
98
99
  if (Event.addBehavior.reassignAfterAjax)
99
- setTimeout(function() { ab.unload(); ab.load(ab.rules) }, 10);
100
+ setTimeout(function() { ab.reload() }, 10);
100
101
  }
101
102
  });
102
103
  ab.responderApplied = true;
@@ -145,6 +146,12 @@ Object.extend(Event.addBehavior, {
145
146
  this.cache = [];
146
147
  },
147
148
 
149
+ reload: function() {
150
+ var ab = Event.addBehavior;
151
+ ab.unload();
152
+ ab.load(ab.rules);
153
+ },
154
+
148
155
  _wrapObserver: function(observer) {
149
156
  return function(event) {
150
157
  if (observer.call(this, event) === false) event.stop();
@@ -277,7 +284,7 @@ Remote.Form = Behavior.create(Remote.Base, {
277
284
  onclick : function(e) {
278
285
  var sourceElement = e.element();
279
286
 
280
- if (sourceElement.nodeName.toLowerCase() == 'input' &&
287
+ if (['input', 'button'].include(sourceElement.nodeName.toLowerCase()) &&
281
288
  sourceElement.type == 'submit')
282
289
  this._submitButton = sourceElement;
283
290
  },
@@ -53,7 +53,7 @@ input.file_upload {
53
53
  .actions {height: 100%; overflow:hidden; font-size: 11px;}
54
54
 
55
55
  .flash {
56
- margin: 0px 40px; padding: 10px 30px; border-width: 2px 0;
56
+ margin: 0 40px 10px; padding: 10px 30px; border-width: 2px 0;
57
57
  color: white;
58
58
  }
59
59
  .flash.notice {border: 1px solid #829862; background: #92ab6e; text-shadow: #728852 1px 1px 0;}
@@ -91,9 +91,12 @@ input.file_upload {
91
91
  .content-header, .content-body {padding-bottom: 15px;}
92
92
  .content-footer {padding-bottom: 20px;}
93
93
 
94
+ .page-content { margin-top: 20px; }
95
+
94
96
  /* aside layout */
95
97
  body.aside-layout {width: 960px;}
96
- .aside-layout .page-content {height: 100%; overflow: hidden;}
98
+ .aside-layout .page-content {height: 100%; overflow: hidden; margin-top: 0px;}
99
+ .aside-layout .main-content {margin-top: 20px;}
97
100
  .main-content {float: left; width: 670px;}
98
101
  .aside {float: right; width: 220px; padding: 20px 30px; background: #E5E5E5;}
99
102
  .aside-content {
@@ -106,7 +109,7 @@ body.aside-layout {width: 960px;}
106
109
  .aside, .main-content {padding-bottom: 10000px; margin-bottom: -10000px;}
107
110
 
108
111
 
109
- .page-header {position: relative; margin: 20px 0; padding: 20px 0 0; color: white; background: black;}
112
+ .page-header {position: relative; margin-top: 20px; padding: 20px 0 0; color: white; background: black;}
110
113
  .page-header h1 {
111
114
  margin: 0; padding: 0 30px 30px;
112
115
  font-family: "Arial Black", Tahoma, Arial, sans-serif; font-size: 36px; letter-spacing: -1.5pt;
@@ -154,7 +157,6 @@ ul.main-nav {float: left; margin: 0 10px;}
154
157
  float: left;
155
158
  margin-left: 0; padding-left: 20px;
156
159
  color: #ddd;
157
- text-shadow: #444 1px 1px 0;
158
160
  list-style: none;
159
161
  }
160
162
  .account-nav a {font-weight: bold;}
@@ -186,9 +188,9 @@ form .actions {margin: 30px 0; width: 100%; text-align: center;}
186
188
  .aside-content .collection {padding-bottom: 10px;}
187
189
 
188
190
  .content-header .creation-details, .content-header .primary-collection-count {font-size: 11px; line-height: 11px;}
189
- .content-header .creator {margin-right: 5px;}
191
+ .content-header .creator {margin-right: 15px;}
190
192
  .content-header .created-at {color: #444;}
191
- .content-header .primary-collection-count {margin-left: 15px; font-weight: bold;}
193
+ .content-header .primary-collection-count {font-weight: bold;}
192
194
 
193
195
  .new-in-collection-page .content-header h2 {margin-top: 6px; font-size: 14px;}
194
196
  .new-in-collection-page .content-header h2, .new-in-collection-page .content-header h2 a {color: #222;}
@@ -203,18 +205,21 @@ form .actions {margin: 30px 0; width: 100%; text-align: center;}
203
205
  /* styling of generic elements */
204
206
  .creator {font-weight: bold;}
205
207
  .card {
206
- clear: both;
207
208
  height: 100%; overflow: hidden;
208
- margin-bottom: 5px; padding:10px 20px; border: 1px solid #e8e8e8;
209
+ margin: 10px 0; padding: 12px 20px; border: 1px solid #e8e8e8;
209
210
  background: #f5f5f5;
210
211
  }
212
+ .card h3 {margin-top: 0;}
211
213
  .card a {background: #f5f5f5;}
212
214
  .card .creation-details {
213
215
  display: block; color: #333; font-size: 11px;
214
216
  }
215
217
  .card .datetime {color: #666;}
218
+ .card a.edit { float: right;}
219
+ .card .delete-button { float: right;}
220
+ div.ordering-handle { float: left; background: #ccc; color: white; margin-right: 5px; cursor: move; padding: 0 2px;}
216
221
 
217
- .card.content {
222
+ .card.content.with-owner {
218
223
  padding: 0; margin: 10px 0 30px; border: none;
219
224
  background: none;
220
225
  font-size: 11px;
@@ -224,7 +229,7 @@ form .actions {margin: 30px 0; width: 100%; text-align: center;}
224
229
  line-height: 14px;
225
230
  }
226
231
  .card.content .creation-details .created-at {display: block;}
227
- .card.content .content {
232
+ .card.content.with-owner .content {
228
233
  float: right; width: 72%;
229
234
  }
230
235
  ul.collection li {clear: both; margin-left: 0; list-style: none;}
@@ -256,11 +261,23 @@ ul.collection li {clear: both; margin-left: 0; list-style: none;}
256
261
  .table-plus table td {
257
262
  padding: 6px 10px; border-bottom: 1px solid #e2e2e2; color: #666;
258
263
  }
259
- .table-plus table td input.button {padding: 1px; margin-left: 10px; }
264
+ .table-plus table td input.button, .collection .button {padding: 1px 3px; margin-left: 10px; margin-top: 0;}
260
265
  .table-plus table td.controls {width: 100px;}
261
266
  .table-plus .even {background: #f8f8f8;}
262
267
  .table-plus a {background: none;}
263
268
 
269
+ /* <select-many> */
270
+
271
+ div.select-many {border-top: 1px dotted #999;}
272
+ div.select-many .items {margin-bottom: 10px;}
273
+ div.select-many .item {
274
+ overflow:hidden; height: 100%; font-weight: bold;
275
+ border-bottom: 1px dotted #999; padding: 5px 10px; /*margin: 5px 25px 5px 0;*/
276
+ }
277
+ div.select-many .item span { float: left; }
278
+ div.select-many .item .remove-item { float: right; }
279
+
280
+
264
281
  /*******************************************************/
265
282
  /* these styles are for the generated front index page */
266
283
  /* you can delete them if you over-ride it */
@@ -273,3 +290,4 @@ ul.collection li {clear: both; margin-left: 0; list-style: none;}
273
290
  font-size: 18px; line-height: 27px;
274
291
  }
275
292
  .front-page .content-body li {margin-left: 0; list-style: none;}
293
+
@@ -1,6 +1,6 @@
1
1
  class <%= class_name %> < ActiveRecord::Base
2
2
 
3
- hobo_user_model
3
+ hobo_user_model # Don't put anything above this
4
4
 
5
5
  fields do
6
6
  username :string, :login => true, :name => true
@@ -17,7 +17,7 @@ class <%= class_name %> < ActiveRecord::Base
17
17
  # def super_user?; true; end
18
18
 
19
19
  def creatable_by?(creator)
20
- true
20
+ creator.administrator? || !administrator
21
21
  end
22
22
 
23
23
  def updatable_by?(updater, new)
@@ -5,7 +5,6 @@ require 'hobosupport'
5
5
  HoboFields
6
6
 
7
7
  # Monkey patches, ooh ooh
8
- require 'rexml'
9
8
  require 'active_record/has_many_association'
10
9
  require 'active_record/has_many_through_association'
11
10
  require 'active_record/association_proxy'
@@ -28,14 +28,17 @@ module ActiveRecord::Associations
28
28
  proxy_reflection.klass
29
29
  end
30
30
 
31
+
31
32
  def origin
32
33
  proxy_owner
33
34
  end
34
35
 
36
+
35
37
  def origin_attribute
36
38
  proxy_reflection.association_name
37
39
  end
38
40
 
41
+
39
42
  private
40
43
 
41
44
  def set_reverse_association(object)
@@ -171,9 +171,13 @@ module Hobo
171
171
  if object.is_a?(Class) and object < ActiveRecord::Base
172
172
  object = object.new
173
173
  object.set_creator(person)
174
- elsif Hobo.simple_has_many_association?(object)
175
- object = object.new
176
- object.set_creator(person)
174
+ elsif (refl = object.try.proxy_reflection) && refl.macro == :has_many
175
+ if Hobo.simple_has_many_association?(object)
176
+ object = object.new
177
+ object.set_creator(person)
178
+ else
179
+ return false
180
+ end
177
181
  end
178
182
  check_permission(:create, person, object)
179
183
  end
@@ -210,13 +214,13 @@ module Hobo
210
214
  object.send("#{field}_editable_by?", person)
211
215
  elsif object.has_hobo_method?(:editable_by?)
212
216
  check_permission(:edit, person, object)
217
+ elsif refl._?.macro == :has_many
218
+ # The below technique to figure out edit permission based on
219
+ # update permission doesn't work for has_many associations
220
+ false
213
221
  else
214
222
  # Fake an edit test by setting the field in question to
215
223
  # Hobo::Undefined and then testing for update permission
216
-
217
- # This technique is not suitable for has_many associations
218
- return false if refl._?.macro == :has_many
219
-
220
224
  current = object.send(field)
221
225
  new = object.duplicate
222
226
 
@@ -333,7 +337,7 @@ module Hobo
333
337
  when :edit; :editable_by?
334
338
  when :view; :viewable_by?
335
339
  end
336
- p = if object.has_hobo_method?(obj_method)
340
+ p = if (obj_method.respond_to?(:has_hobo_method) ? object.has_hobo_method?(obj_method) : object.respond_to?(obj_method))
337
341
  begin
338
342
  object.send(obj_method, person, *args)
339
343
  rescue Hobo::UndefinedAccessError
@@ -167,7 +167,7 @@ module ::Hobo
167
167
  def create_controllers
168
168
  bundle = self
169
169
  self.class.controller_declarations.each do |model_name, block|
170
- klass = make_class("#{new_name_for(model_name).to_s.pluralize}Controller", ApplicationController) do
170
+ klass = make_class("#{new_name_for(model_name).to_s.pluralize}Controller", ::ApplicationController) do
171
171
  hobo_model_controller
172
172
  end
173
173
  klass.class_eval(&block)
@@ -54,7 +54,7 @@ module Hobo::Dryml
54
54
  ("def render_page(__page_this__, __local_assigns__); " +
55
55
  "#{locals} new_object_context(__page_this__) do " +
56
56
  src +
57
- "; _erbout; end + part_contexts_storage_tag; end")
57
+ "; _erbout; end; end")
58
58
  end
59
59
 
60
60
 
@@ -0,0 +1,41 @@
1
+ module Hobo::Dryml::Parser
2
+
3
+ class Attribute < REXML::Attribute
4
+
5
+ def initialize(first, second=nil, parent=nil)
6
+ super
7
+ if first.is_a?(String) && second == true
8
+ @value = true
9
+ end
10
+ end
11
+
12
+ def value
13
+ if has_rhs?
14
+ super
15
+ else
16
+ element.document.default_attribute_value
17
+ end
18
+ end
19
+
20
+ def to_string
21
+ if has_rhs?
22
+ super
23
+ else
24
+ @expanded_name
25
+ end
26
+ end
27
+
28
+ def has_rhs?
29
+ @value != true
30
+ end
31
+
32
+
33
+ # Override to supress Text.check call
34
+ def element=( element )
35
+ @element = element
36
+ self
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,253 @@
1
+ module Hobo::Dryml::Parser
2
+
3
+ class BaseParser < REXML::Parsers::BaseParser
4
+
5
+ REX_3_1_7_1 = REXML::VERSION == "3.1.7.1"
6
+
7
+ DRYML_NAME_STR = "#{NCNAME_STR}(?::(?:#{NCNAME_STR})?)?"
8
+ DRYML_ATTRIBUTE_PATTERN = if REX_3_1_7_1
9
+ /\s*(#{NAME_STR})(?:\s*=\s*(["'])(.*?)\2)?/um
10
+ else
11
+ /\s*(#{NAME_STR})(?:\s*=\s*(["'])(.*?)\4)?/um
12
+ end
13
+ DRYML_TAG_MATCH = if REX_3_1_7_1
14
+ /^<((?>#{DRYML_NAME_STR}))\s*((?>\s+#{NAME_STR}(?:\s*=\s*(["']).*?\3)?)*)\s*(\/)?>/um
15
+ else
16
+ /^<((?>#{DRYML_NAME_STR}))\s*((?>\s+#{NAME_STR}(?:\s*=\s*(["']).*?\5)?)*)\s*(\/)?>/um
17
+ end
18
+ DRYML_CLOSE_MATCH = /^\s*<\/(#{DRYML_NAME_STR})\s*>/um
19
+
20
+ # For compatibility with REXML 3.1.7.3
21
+ IDENTITY = /^([!\*\w\-]+)(\s+#{NCNAME_STR})?(\s+["'](.*?)['"])?(\s+['"](.*?)["'])?/u
22
+
23
+ def pull
24
+ if @closed
25
+ x, @closed = @closed, nil
26
+ return [ :end_element, x, false ]
27
+ end
28
+ return [ :end_document ] if empty?
29
+ return @stack.shift if @stack.size > 0
30
+ #STDERR.puts @source.encoding
31
+ @source.read if @source.buffer.size<2
32
+ #STDERR.puts "BUFFER = #{@source.buffer.inspect}"
33
+ if @document_status == nil
34
+ #@source.consume( /^\s*/um )
35
+ word = @source.match( /^((?:\s+)|(?:<[^>]*>))/um ) #word = @source.match(/(<[^>]*)>/um)
36
+ word = word[1] unless word.nil?
37
+ #STDERR.puts "WORD = #{word.inspect}"
38
+ case word
39
+ when COMMENT_START
40
+ return [ :comment, @source.match( COMMENT_PATTERN, true )[1] ]
41
+ when XMLDECL_START
42
+ #STDERR.puts "XMLDECL"
43
+ results = @source.match( XMLDECL_PATTERN, true )[1]
44
+ version = VERSION.match( results )
45
+ version = version[1] unless version.nil?
46
+ encoding = ENCODING.match(results)
47
+ encoding = encoding[1] unless encoding.nil?
48
+ @source.encoding = encoding
49
+ standalone = STANDALONE.match(results)
50
+ standalone = standalone[1] unless standalone.nil?
51
+ return [ :xmldecl, version, encoding, standalone ]
52
+ when INSTRUCTION_START
53
+ return [ :processing_instruction, *@source.match(INSTRUCTION_PATTERN, true)[1,2] ]
54
+ when DOCTYPE_START
55
+ md = @source.match( DOCTYPE_PATTERN, true )
56
+ #@nsstack.unshift(curr_ns=Set.new)
57
+ identity = md[1]
58
+ close = md[2]
59
+ identity =~ IDENTITY
60
+ name = $1
61
+ raise REXML::ParseException.new("DOCTYPE is missing a name") if name.nil?
62
+ pub_sys = $2.nil? ? nil : $2.strip
63
+ long_name = $4.nil? ? nil : $4.strip
64
+ uri = $6.nil? ? nil : $6.strip
65
+ args = [ :start_doctype, name, pub_sys, long_name, uri ]
66
+ if close == ">"
67
+ @document_status = :after_doctype
68
+ @source.read if @source.buffer.size<2
69
+ md = @source.match(/^\s*/um, true)
70
+ @stack << [ :end_doctype ]
71
+ else
72
+ @document_status = :in_doctype
73
+ end
74
+ return args
75
+ when /^\s+/
76
+ else
77
+ @document_status = :after_doctype
78
+ @source.read if @source.buffer.size<2
79
+ md = @source.match(/\s*/um, true)
80
+ if @source.encoding == "UTF-8"
81
+ if @source.buffer.respond_to? :force_encoding
82
+ @source.buffer.force_encoding(Encoding::UTF_8)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ if @document_status == :in_doctype
88
+ md = @source.match(/\s*(.*?>)/um)
89
+ case md[1]
90
+ when SYSTEMENTITY
91
+ match = @source.match( SYSTEMENTITY, true )[1]
92
+ return [ :externalentity, match ]
93
+
94
+ when ELEMENTDECL_START
95
+ return [ :elementdecl, @source.match( ELEMENTDECL_PATTERN, true )[1] ]
96
+
97
+ when ENTITY_START
98
+ match = @source.match( ENTITYDECL, true ).to_a.compact
99
+ match[0] = :entitydecl
100
+ ref = false
101
+ if match[1] == '%'
102
+ ref = true
103
+ match.delete_at 1
104
+ end
105
+ # Now we have to sort out what kind of entity reference this is
106
+ if match[2] == 'SYSTEM'
107
+ # External reference
108
+ match[3] = match[3][1..-2] # PUBID
109
+ match.delete_at(4) if match.size > 4 # Chop out NDATA decl
110
+ # match is [ :entity, name, SYSTEM, pubid(, ndata)? ]
111
+ elsif match[2] == 'PUBLIC'
112
+ # External reference
113
+ match[3] = match[3][1..-2] # PUBID
114
+ match[4] = match[4][1..-2] # HREF
115
+ # match is [ :entity, name, PUBLIC, pubid, href ]
116
+ else
117
+ match[2] = match[2][1..-2]
118
+ match.pop if match.size == 4
119
+ # match is [ :entity, name, value ]
120
+ end
121
+ match << '%' if ref
122
+ return match
123
+ when ATTLISTDECL_START
124
+ md = @source.match( ATTLISTDECL_PATTERN, true )
125
+ raise REXML::ParseException.new( "Bad ATTLIST declaration!", @source ) if md.nil?
126
+ element = md[1]
127
+ contents = md[0]
128
+
129
+ pairs = {}
130
+ values = md[0].scan( ATTDEF_RE )
131
+ values.each do |attdef|
132
+ unless attdef[3] == "#IMPLIED"
133
+ attdef.compact!
134
+ val = attdef[3]
135
+ val = attdef[4] if val == "#FIXED "
136
+ pairs[attdef[0]] = val
137
+ if attdef[0] =~ /^xmlns:(.*)/
138
+ #@nsstack[0] << $1
139
+ end
140
+ end
141
+ end
142
+ return [ :attlistdecl, element, pairs, contents ]
143
+ when NOTATIONDECL_START
144
+ md = nil
145
+ if @source.match( PUBLIC )
146
+ md = @source.match( PUBLIC, true )
147
+ vals = [md[1],md[2],md[4],md[6]]
148
+ elsif @source.match( SYSTEM )
149
+ md = @source.match( SYSTEM, true )
150
+ vals = [md[1],md[2],nil,md[4]]
151
+ else
152
+ raise REXML::ParseException.new( "error parsing notation: no matching pattern", @source )
153
+ end
154
+ return [ :notationdecl, *vals ]
155
+ when CDATA_END
156
+ @document_status = :after_doctype
157
+ @source.match( CDATA_END, true )
158
+ return [ :end_doctype ]
159
+ end
160
+ end
161
+ begin
162
+ if @source.buffer[0] == ?<
163
+ if @source.buffer[1] == ?/
164
+ #@nsstack.shift
165
+ last_tag, line_no = @tags.pop
166
+ #md = @source.match_to_consume( '>', CLOSE_MATCH)
167
+ md = @source.match(DRYML_CLOSE_MATCH, true)
168
+
169
+ valid_end_tag = last_tag =~ /^#{Regexp.escape(md[1])}(:.*)?/
170
+ raise REXML::ParseException.new( "Missing end tag for "+
171
+ "'#{last_tag}' (line #{line_no}) (got \"#{md[1]}\")",
172
+ @source) unless valid_end_tag
173
+ return [ :end_element, last_tag, true ]
174
+ elsif @source.buffer[1] == ?!
175
+ md = @source.match(/\A(\s*[^>]*>)/um)
176
+ #STDERR.puts "SOURCE BUFFER = #{source.buffer}, #{source.buffer.size}"
177
+ raise REXML::ParseException.new("Malformed node", @source) unless md
178
+ if md[0][2] == ?-
179
+ md = @source.match( COMMENT_PATTERN, true )
180
+
181
+ case md[1]
182
+ when /--/, /-$/
183
+ raise REXML::ParseException.new("Malformed comment", @source)
184
+ end
185
+
186
+ return [ :comment, md[1] ] if md
187
+ else
188
+ md = @source.match( CDATA_PATTERN, true )
189
+ return [ :cdata, md[1] ] if md
190
+ end
191
+ raise REXML::ParseException.new( "Declarations can only occur "+
192
+ "in the doctype declaration.", @source)
193
+ elsif @source.buffer[1] == ??
194
+ md = @source.match( INSTRUCTION_PATTERN, true )
195
+ return [ :processing_instruction, md[1], md[2] ] if md
196
+ raise REXML::ParseException.new( "Bad instruction declaration",
197
+ @source)
198
+ else
199
+ # Get the next tag
200
+ md = @source.match(DRYML_TAG_MATCH, true)
201
+ unless md
202
+ # Check for missing attribute quotes
203
+ raise REXML::ParseException.new("missing attribute quote", @source) if @source.match(MISSING_ATTRIBUTE_QUOTES )
204
+ raise REXML::ParseException.new("malformed XML: missing tag start", @source)
205
+
206
+ end
207
+ attributes = {}
208
+ #@nsstack.unshift(curr_ns=Set.new)
209
+ if md[2].size > 0
210
+ attrs = md[2].scan(DRYML_ATTRIBUTE_PATTERN)
211
+ raise REXML::ParseException.new( "error parsing attributes: [#{attrs.join ', '}], excess = \"#$'\"", @source) if $' and $'.strip.size > 0
212
+ attrs.each { |a,b,c,d,e|
213
+ val = REX_3_1_7_1 ? c : e
214
+ if attributes.has_key? a
215
+ msg = "Duplicate attribute #{a.inspect}"
216
+ raise REXML::ParseException.new( msg, @source, self)
217
+ end
218
+ attributes[a] = val || true
219
+ }
220
+ end
221
+
222
+ if md[REX_3_1_7_1 ? 4 : 6]
223
+ @closed = md[1]
224
+ #@nsstack.shift
225
+ else
226
+ cl = @source.current_line
227
+ @tags.push( [md[1], cl && cl[2]] )
228
+ end
229
+ return [ :start_element, md[1], attributes, md[0],
230
+ @source.respond_to?(:last_match_offset) && @source.last_match_offset ]
231
+ end
232
+ else
233
+ md = @source.match( TEXT_PATTERN, true )
234
+ if md[0].length == 0
235
+ @source.match( /(\s+)/, true )
236
+ end
237
+ #STDERR.puts "GOT #{md[1].inspect}" unless md[0].length == 0
238
+ #return [ :text, "" ] if md[0].length == 0
239
+ # unnormalized = Text::unnormalize( md[1], self )
240
+ # return PullEvent.new( :text, md[1], unnormalized )
241
+ return [ :text, md[1] ]
242
+ end
243
+ rescue REXML::ParseException
244
+ raise
245
+ rescue Exception, NameError => error
246
+ raise REXML::ParseException.new( "Exception parsing",
247
+ @source, self, (error ? error : $!) )
248
+ end
249
+ return [ :dummy ]
250
+ end
251
+ end
252
+
253
+ end