hobo 0.7.3 → 0.7.4

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