sws 0.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.
- data/doc/DOC.otl +34 -0
- data/doc/Makefile +13 -0
- data/doc/architecture.dia +0 -0
- data/doc/docbook/architecture.png +0 -0
- data/doc/docbook/concepts.docbook +474 -0
- data/doc/docbook/installation.docbook +57 -0
- data/doc/docbook/introduction.docbook +130 -0
- data/doc/docbook/sws_manual.docbook +35 -0
- data/doc/docbook/todo.docbook +38 -0
- data/doc/docbook/tutorial.docbook +594 -0
- data/examples/README +1 -0
- data/examples/addressbook/CardBrowse/CardBrowse.html +43 -0
- data/examples/addressbook/CardBrowse/CardBrowse.rb +65 -0
- data/examples/addressbook/CardBrowse/CardBrowse.sws +92 -0
- data/examples/addressbook/Login/LoginPage.html +12 -0
- data/examples/addressbook/Login/LoginPage.rb +19 -0
- data/examples/addressbook/Login/LoginPage.sws +15 -0
- data/examples/addressbook/README +1 -0
- data/examples/addressbook/addressbook.rb +70 -0
- data/examples/addressbook/application.yaml +8 -0
- data/examples/addressbook/db.yaml +7 -0
- data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.html +11 -0
- data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.rb +21 -0
- data/examples/component_demo/CheckBoxDemo/CheckBoxDemo.sws +25 -0
- data/examples/component_demo/ComponentDemo.rb +21 -0
- data/examples/component_demo/ConditionalDemo/ConditionalDemo.html +18 -0
- data/examples/component_demo/ConditionalDemo/ConditionalDemo.rb +2 -0
- data/examples/component_demo/ConditionalDemo/ConditionalDemo.sws +22 -0
- data/examples/component_demo/FileUploadDemo/FileUploadDemo.html +10 -0
- data/examples/component_demo/FileUploadDemo/FileUploadDemo.rb +9 -0
- data/examples/component_demo/FileUploadDemo/FileUploadDemo.sws +33 -0
- data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.html +12 -0
- data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.rb +21 -0
- data/examples/component_demo/FormFieldsDemo/FormFieldsDemo.sws +40 -0
- data/examples/component_demo/FormListsDemo/FormListsDemo.html +11 -0
- data/examples/component_demo/FormListsDemo/FormListsDemo.rb +37 -0
- data/examples/component_demo/FormListsDemo/FormListsDemo.sws +47 -0
- data/examples/component_demo/GenericDemo/GenericDemo.html +4 -0
- data/examples/component_demo/GenericDemo/GenericDemo.rb +2 -0
- data/examples/component_demo/GenericDemo/GenericDemo.sws +10 -0
- data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.html +8 -0
- data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.rb +20 -0
- data/examples/component_demo/HyperlinkDemo/HyperlinkDemo.sws +19 -0
- data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.html +11 -0
- data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.rb +2 -0
- data/examples/component_demo/ImageLinkDemo/ImageLinkDemo.sws +14 -0
- data/examples/component_demo/PageWrapper/PageWrapper.html +23 -0
- data/examples/component_demo/PageWrapper/PageWrapper.rb +2 -0
- data/examples/component_demo/PageWrapper/PageWrapper.sws +42 -0
- data/examples/component_demo/README +1 -0
- data/examples/component_demo/RepetitionDemo/RepetitionDemo.html +13 -0
- data/examples/component_demo/RepetitionDemo/RepetitionDemo.rb +19 -0
- data/examples/component_demo/RepetitionDemo/RepetitionDemo.sws +20 -0
- data/examples/component_demo/StringDemo/StringDemo.html +5 -0
- data/examples/component_demo/StringDemo/StringDemo.rb +16 -0
- data/examples/component_demo/StringDemo/StringDemo.sws +14 -0
- data/examples/component_demo/application.yaml +28 -0
- data/examples/component_demo/poweredby.jpg +0 -0
- data/examples/component_demo/style.css +1 -0
- data/examples/movies/Menu/Menu.html +3 -0
- data/examples/movies/Menu/Menu.rb +7 -0
- data/examples/movies/Menu/Menu.sws +7 -0
- data/examples/movies/MovieBrowse/MovieBrowse.html +68 -0
- data/examples/movies/MovieBrowse/MovieBrowse.rb +178 -0
- data/examples/movies/MovieBrowse/MovieBrowse.sws +127 -0
- data/examples/movies/README +1 -0
- data/examples/movies/UserBrowse/UserBrowse.html +50 -0
- data/examples/movies/UserBrowse/UserBrowse.rb +69 -0
- data/examples/movies/UserBrowse/UserBrowse.sws +49 -0
- data/examples/movies/application.yaml +11 -0
- data/examples/movies/da.rb +36 -0
- data/examples/movies/dbmovies.rb +44 -0
- data/examples/movies/frameworks/TestFramework/framework.yaml +4 -0
- data/examples/movies/frameworks/TestFramework/resources/im1.jpg +0 -0
- data/examples/movies/images/pbr1b.jpg +0 -0
- data/examples/movies/movies.rb +28 -0
- data/examples/movies/movies.sds +119 -0
- data/examples/movies/movies.sqlite +0 -0
- data/examples/movies/movies_mysql.sql +28 -0
- data/examples/movies/movies_postgres.sql +27 -0
- data/examples/movies/movies_sqlite.sql +28 -0
- data/lib/sws.rb +89 -0
- data/lib/sws/Core/components/CheckBox/CheckBox.api +5 -0
- data/lib/sws/Core/components/CheckBox/CheckBox.rb +45 -0
- data/lib/sws/Core/components/CheckBoxList/CheckBoxList.api +13 -0
- data/lib/sws/Core/components/CheckBoxList/CheckBoxList.rb +54 -0
- data/lib/sws/Core/components/Conditional/Conditional.api +3 -0
- data/lib/sws/Core/components/Conditional/Conditional.html +1 -0
- data/lib/sws/Core/components/Conditional/Conditional.rb +39 -0
- data/lib/sws/Core/components/Conditional/Conditional.sws +2 -0
- data/lib/sws/Core/components/Content/Content.rb +18 -0
- data/lib/sws/Core/components/ExceptionPage/ExceptionPage.html +13 -0
- data/lib/sws/Core/components/ExceptionPage/ExceptionPage.rb +18 -0
- data/lib/sws/Core/components/ExceptionPage/ExceptionPage.sws +16 -0
- data/lib/sws/Core/components/FileUpload/FileUpload.api +16 -0
- data/lib/sws/Core/components/FileUpload/FileUpload.rb +62 -0
- data/lib/sws/Core/components/Form/Form.api +9 -0
- data/lib/sws/Core/components/Form/Form.html +3 -0
- data/lib/sws/Core/components/Form/Form.rb +55 -0
- data/lib/sws/Core/components/Form/Form.sws +12 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.api +10 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.html +1 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.rb +39 -0
- data/lib/sws/Core/components/GenericContainer/GenericContainer.sws +12 -0
- data/lib/sws/Core/components/GenericElement/GenericElement.api +10 -0
- data/lib/sws/Core/components/GenericElement/GenericElement.rb +34 -0
- data/lib/sws/Core/components/HiddenField/HiddenField.api +7 -0
- data/lib/sws/Core/components/HiddenField/HiddenField.rb +37 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.api +13 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.html +1 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.rb +102 -0
- data/lib/sws/Core/components/Hyperlink/Hyperlink.sws +12 -0
- data/lib/sws/Core/components/Image/Image.api +11 -0
- data/lib/sws/Core/components/Image/Image.rb +49 -0
- data/lib/sws/Core/components/ImageButton/ImageButton.api +16 -0
- data/lib/sws/Core/components/ImageButton/ImageButton.rb +76 -0
- data/lib/sws/Core/components/Link/Link.api +11 -0
- data/lib/sws/Core/components/Link/Link.rb +39 -0
- data/lib/sws/Core/components/PasswordField/PasswordField.api +7 -0
- data/lib/sws/Core/components/PasswordField/PasswordField.rb +41 -0
- data/lib/sws/Core/components/RadioButton/RadioButton.api +7 -0
- data/lib/sws/Core/components/RadioButton/RadioButton.rb +44 -0
- data/lib/sws/Core/components/RadioButtonList/RadioButtonList.api +20 -0
- data/lib/sws/Core/components/RadioButtonList/RadioButtonList.rb +76 -0
- data/lib/sws/Core/components/Repetition/Repetition.api +10 -0
- data/lib/sws/Core/components/Repetition/Repetition.html +1 -0
- data/lib/sws/Core/components/Repetition/Repetition.rb +137 -0
- data/lib/sws/Core/components/Repetition/Repetition.sws +2 -0
- data/lib/sws/Core/components/ResetButton/ResetButton.api +5 -0
- data/lib/sws/Core/components/ResetButton/ResetButton.rb +28 -0
- data/lib/sws/Core/components/Select/Select.api +24 -0
- data/lib/sws/Core/components/Select/Select.rb +57 -0
- data/lib/sws/Core/components/String/String.api +5 -0
- data/lib/sws/Core/components/String/String.rb +28 -0
- data/lib/sws/Core/components/SubmitButton/SubmitButton.api +6 -0
- data/lib/sws/Core/components/SubmitButton/SubmitButton.rb +53 -0
- data/lib/sws/Core/components/TextArea/TextArea.api +9 -0
- data/lib/sws/Core/components/TextArea/TextArea.rb +28 -0
- data/lib/sws/Core/components/TextField/TextField.api +9 -0
- data/lib/sws/Core/components/TextField/TextField.rb +46 -0
- data/lib/sws/Core/framework.yaml +25 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.api +5 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.html +58 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.rb +34 -0
- data/lib/sws/JSComponents/components/JSMenu/JSMenu.sws +37 -0
- data/lib/sws/JSComponents/framework.yaml +3 -0
- data/lib/sws/adaptor.rb +334 -0
- data/lib/sws/application.rb +604 -0
- data/lib/sws/component.rb +656 -0
- data/lib/sws/cookie.rb +27 -0
- data/lib/sws/direct_action.rb +38 -0
- data/lib/sws/extensions.rb +49 -0
- data/lib/sws/parsers.rb +374 -0
- data/lib/sws/request.rb +308 -0
- data/lib/sws/response.rb +70 -0
- data/lib/sws/session.rb +195 -0
- data/lib/sws/slot.rb +198 -0
- metadata +263 -0
data/lib/sws/cookie.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module SWS
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# Represents HTML cookie
|
|
5
|
+
class Cookie
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
attr_reader :key
|
|
9
|
+
attr_reader :value
|
|
10
|
+
attr_reader :path
|
|
11
|
+
|
|
12
|
+
#TODO: add domain?
|
|
13
|
+
|
|
14
|
+
# Creates new Cookie with given key=value pair and optionally path
|
|
15
|
+
def initialize ( key, value, path="/" )
|
|
16
|
+
@key,@value,@path = key,value,path
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Returns cookie representation in HTTP header form
|
|
21
|
+
def to_s
|
|
22
|
+
return "Set-Cookie: #{@key}=#{@value}; path=#{@path}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
module SWS
|
|
4
|
+
|
|
5
|
+
# DirectAction is a way to define multiple entry point to the application.
|
|
6
|
+
# Default entry point is a default_component defined in application config
|
|
7
|
+
# file. But sometimes you may need more entry points. In such a situation
|
|
8
|
+
# create appropiate ..._action methods in your DirectAction class and then you
|
|
9
|
+
# may access them by using
|
|
10
|
+
# http://application_url/direct_action_key/method_name_without_action_word
|
|
11
|
+
# URL.
|
|
12
|
+
# DirectAction requests are served by instances of this class by default. You
|
|
13
|
+
# can also define your own classes for this purpose.
|
|
14
|
+
class DirectAction
|
|
15
|
+
|
|
16
|
+
# The Request object the receiver is associated to
|
|
17
|
+
attr_reader :request
|
|
18
|
+
|
|
19
|
+
def initialize ( request )
|
|
20
|
+
@request = request
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# This methods returns default_component instance
|
|
24
|
+
def default_action ()
|
|
25
|
+
|
|
26
|
+
component = Component.create( Application.instance.default_component_class_name, nil, @request )
|
|
27
|
+
return component.process_request( @request )
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def action_for_name ( name )
|
|
32
|
+
# TODO: handle non-existing action?
|
|
33
|
+
return method( name + "_action" ).call()
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Extensions to String class - encoding and decoding of URL-encoded string
|
|
4
|
+
class String
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Decode URL-encoded string - returns decoded string.
|
|
8
|
+
def url_decode ()
|
|
9
|
+
tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
|
|
10
|
+
[$1.delete('%')].pack('H*')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Decode URL-encoded string - modifies the receiver. Returns decoded string.
|
|
17
|
+
def url_decode! ()
|
|
18
|
+
|
|
19
|
+
tr!('+', ' ')
|
|
20
|
+
gsub!(/((?:%[0-9a-fA-F]{2})+)/n) do
|
|
21
|
+
[$1.delete('%')].pack('H*')
|
|
22
|
+
end
|
|
23
|
+
return self
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# Encode string with URL-encoding - returns encoded string.
|
|
29
|
+
def url_encode ()
|
|
30
|
+
|
|
31
|
+
gsub(/([^ a-zA-Z0-9_.-]+)/n) do
|
|
32
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
|
33
|
+
end.tr(' ', '+')
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Encode string with URL-encoding - modifies the receiver. Returns encoded
|
|
39
|
+
# string.
|
|
40
|
+
def url_encode! ()
|
|
41
|
+
|
|
42
|
+
gsub!(/([^ a-zA-Z0-9_.-]+)/n) do
|
|
43
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
|
44
|
+
end.tr(' ', '+')
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
data/lib/sws/parsers.rb
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Definition and template parser and helper classes
|
|
4
|
+
module SWS
|
|
5
|
+
|
|
6
|
+
# Class that retrieves definition of the component from .sws and/or .api files.
|
|
7
|
+
# Parsed definitions are cached to avoid unnecessary overhead.
|
|
8
|
+
class DefinitionParser
|
|
9
|
+
|
|
10
|
+
include Singleton
|
|
11
|
+
|
|
12
|
+
def initialize ()
|
|
13
|
+
|
|
14
|
+
# .sws file cache
|
|
15
|
+
@sws_cache = Hash.new do |hash,filename|
|
|
16
|
+
$log_sws_parser.debug( "Parsing .sws #{filename}" )
|
|
17
|
+
hash[filename] = YAML::load( File.open( filename ) ) #sws_parse( filename )
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# .api file cache
|
|
21
|
+
@api_cache = Hash.new do |hash,filename|
|
|
22
|
+
$log_sws_parser.debug( "Parsing .api #{filename}" )
|
|
23
|
+
hash[filename] = YAML::load( File.open( filename ) )
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Most important method of the class - returns Component object for given
|
|
30
|
+
# token in given parent component. Does it by parsing parent definition file
|
|
31
|
+
# and looking for definition of Component with given token name. When the
|
|
32
|
+
# definition is found, new Component object with proper slots and definition
|
|
33
|
+
# (again retrieved from file and parsed) is created.
|
|
34
|
+
def component_for_token_with_parent ( token, parent )
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Element to find - component definition
|
|
38
|
+
component_definition = nil
|
|
39
|
+
|
|
40
|
+
# Definition is looked for in parent component recursively - up to the
|
|
41
|
+
# topmost component
|
|
42
|
+
next_parent = parent
|
|
43
|
+
|
|
44
|
+
name = token.sws_name
|
|
45
|
+
|
|
46
|
+
loop do
|
|
47
|
+
|
|
48
|
+
$log_sws_parser.debug( "Looking for #{name} in #{next_parent}" )
|
|
49
|
+
|
|
50
|
+
# Because this is the parent (so it has children) we don't have to check
|
|
51
|
+
# if the .sws file is present
|
|
52
|
+
parent_definition = @sws_cache[ next_parent.definition_filename ]
|
|
53
|
+
|
|
54
|
+
# Definition of component looked for in parent definition - if not
|
|
55
|
+
# found, the parent's parent will be searched
|
|
56
|
+
# FIXME: this will surely work incorrectly when 2 nested components have
|
|
57
|
+
# subcomponents of the same name!
|
|
58
|
+
component_definition = parent_definition[name]
|
|
59
|
+
|
|
60
|
+
if ( component_definition != nil )
|
|
61
|
+
break
|
|
62
|
+
elsif ( next_parent.parent != nil )
|
|
63
|
+
next_parent = next_parent.parent
|
|
64
|
+
else
|
|
65
|
+
raise( "Definiton for component #{name} not found!" )
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
$log_sws_parser.debug( "Definition found" )
|
|
71
|
+
|
|
72
|
+
# Create Component object using component_definition hash
|
|
73
|
+
class_name = component_definition[ "_class" ]
|
|
74
|
+
unless( class_name )
|
|
75
|
+
raise( "Cannot find _class attribute for component #{token.sws_name}" )
|
|
76
|
+
end
|
|
77
|
+
component = Component.create( class_name, parent.request, name.dup(), parent )
|
|
78
|
+
component.definition_component = next_parent
|
|
79
|
+
component.html_attrs = token.attributes
|
|
80
|
+
|
|
81
|
+
slots = Hash.new
|
|
82
|
+
|
|
83
|
+
if ( component.api_filename != nil )
|
|
84
|
+
@api_cache[ component.api_filename ].each_pair do |slot_name, slot_attrs|
|
|
85
|
+
|
|
86
|
+
slot = Slot.new( component, slot_name )
|
|
87
|
+
slot.bind( component_definition[slot_name] )
|
|
88
|
+
|
|
89
|
+
if ( slot_attrs )
|
|
90
|
+
slot.default = slot_attrs["default"]
|
|
91
|
+
slot.required = slot_attrs["required"]
|
|
92
|
+
slot.settable = slot_attrs["settable"]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
slots[slot_name] = slot
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
component.slots = slots
|
|
100
|
+
component.synchronize_slots()
|
|
101
|
+
return component
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Parser for HTML templates of the components.
|
|
109
|
+
class TemplateParser
|
|
110
|
+
|
|
111
|
+
@@file_data = {}
|
|
112
|
+
|
|
113
|
+
# Creates new TemplateParser for given filename.
|
|
114
|
+
def initialize ( filename )
|
|
115
|
+
|
|
116
|
+
$log_sws_parser.debug( "Creating template parser for name #{filename}" )
|
|
117
|
+
@template_filename = filename
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Main parsing method - parses definition file into the tree of
|
|
123
|
+
# TemplateToken objects
|
|
124
|
+
def parse ()
|
|
125
|
+
|
|
126
|
+
begin
|
|
127
|
+
#TODO: implement debug mode which forces sws to reload
|
|
128
|
+
# template file every time parse() is called
|
|
129
|
+
unless( @@file_data[ @template_filename ] )
|
|
130
|
+
@@file_data[ @template_filename ] = File.read( @template_filename )
|
|
131
|
+
end
|
|
132
|
+
template_data = @@file_data[ @template_filename ]
|
|
133
|
+
|
|
134
|
+
rescue Exception
|
|
135
|
+
raise "Cannot read template data from file \"#{@template_filename}\""
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
tokenized_data = template_data.scan( /<!--.*?-->|<[^<>]+>|[^<>]+/ )
|
|
139
|
+
root_token = create_token_tree( tokenized_data )
|
|
140
|
+
|
|
141
|
+
return root_token
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
# Parses attributes of the tag - used only for SWS tags, so returns null if "sws"
|
|
149
|
+
# attribute is not found
|
|
150
|
+
def parse_attributes ( tag )
|
|
151
|
+
|
|
152
|
+
attrs = Hash.new
|
|
153
|
+
|
|
154
|
+
# Scans tag attrs - matches also attrs with no value, like "selected"
|
|
155
|
+
# parameter of checkbox or "disabled"
|
|
156
|
+
tag.scan( /\s+(\w+)\s*=?\s*(['"])(.*?)\2|\s+(\w+)\s*=?\s*([^'"\s>]*)/ ).each do |name,quote,value,second_name,second_value|
|
|
157
|
+
unless ( name )
|
|
158
|
+
name = second_name
|
|
159
|
+
value = second_value
|
|
160
|
+
end
|
|
161
|
+
value ||= quote
|
|
162
|
+
attrs[name.downcase] = value
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
return attrs["sws"] ? attrs : nil
|
|
166
|
+
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Creates token tree from the array of lexems
|
|
171
|
+
def create_token_tree ( lexems )
|
|
172
|
+
|
|
173
|
+
nesting_level = 0
|
|
174
|
+
root_tokens = Array.new
|
|
175
|
+
current_token = nil
|
|
176
|
+
|
|
177
|
+
lexems.reverse.each do |lexem|
|
|
178
|
+
|
|
179
|
+
if ( comment?( lexem ) )
|
|
180
|
+
|
|
181
|
+
token = TemplateToken.new( current_token,nil,true,true )
|
|
182
|
+
token.data = lexem
|
|
183
|
+
if ( current_token )
|
|
184
|
+
current_token.children << token
|
|
185
|
+
else
|
|
186
|
+
root_tokens << token
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
elsif ( closing?( lexem ) )
|
|
190
|
+
|
|
191
|
+
tag_name = lexem.scan( /^<\/(\w+)>$/ )[0][0].downcase
|
|
192
|
+
token = TemplateToken.new( current_token,tag_name )
|
|
193
|
+
if ( current_token )
|
|
194
|
+
current_token.children << token
|
|
195
|
+
else
|
|
196
|
+
root_tokens << token
|
|
197
|
+
end
|
|
198
|
+
current_token = token
|
|
199
|
+
nesting_level += 1
|
|
200
|
+
|
|
201
|
+
elsif ( opening_or_standalone?( lexem ) )
|
|
202
|
+
|
|
203
|
+
tag_name = lexem.scan( /^<(\w+)/ )[0][0].downcase
|
|
204
|
+
if ( current_token && current_token.name == tag_name ) # tag should be closed
|
|
205
|
+
attrs = parse_attributes( lexem )
|
|
206
|
+
if ( attrs )
|
|
207
|
+
current_token.attributes = attrs
|
|
208
|
+
else
|
|
209
|
+
# Why should we parse an ordinal tag (attributes etc) and then
|
|
210
|
+
# generate it again, if we can just put the original lexem?
|
|
211
|
+
current_token.data = lexem
|
|
212
|
+
end
|
|
213
|
+
current_token = current_token.parent
|
|
214
|
+
nesting_level -= 1
|
|
215
|
+
else # standalone tag
|
|
216
|
+
token = TemplateToken.new( current_token,tag_name,true )
|
|
217
|
+
attrs = parse_attributes( lexem )
|
|
218
|
+
if ( attrs )
|
|
219
|
+
token.attributes = attrs
|
|
220
|
+
else
|
|
221
|
+
token.data = lexem
|
|
222
|
+
end
|
|
223
|
+
if ( current_token )
|
|
224
|
+
current_token.children << token
|
|
225
|
+
else
|
|
226
|
+
root_tokens << token
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
else #text token
|
|
231
|
+
|
|
232
|
+
token = TemplateToken.new( current_token,nil,true )
|
|
233
|
+
token.data = lexem
|
|
234
|
+
if ( current_token )
|
|
235
|
+
current_token.children << token
|
|
236
|
+
else
|
|
237
|
+
root_tokens << token
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
root_tokens.each { |token| token.reverse() }
|
|
245
|
+
root_tokens.reverse!
|
|
246
|
+
|
|
247
|
+
unless ( nesting_level == 0 )
|
|
248
|
+
raise( "Invalid .html template file - probably some tags are not closed" )
|
|
249
|
+
end
|
|
250
|
+
#root_tokens.each { |token| token.dump ( "" ) }
|
|
251
|
+
|
|
252
|
+
return root_tokens
|
|
253
|
+
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# Returns true if the tag is an opening or standalone tag
|
|
258
|
+
def opening_or_standalone? ( tag )
|
|
259
|
+
return ( tag =~ /^<\w+/ )
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# Returns true if the tag is a HTML comment
|
|
264
|
+
def comment? ( tag )
|
|
265
|
+
return tag =~ /^<!--.*-->$/
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# Returns true if the tag is a closing tag
|
|
270
|
+
def closing? (tag)
|
|
271
|
+
return ( tag =~ /^<\/\w+>/ )
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
# Internal class - used only by TemplateParser. Represents a HTML tag or text
|
|
278
|
+
# token
|
|
279
|
+
class TemplateToken
|
|
280
|
+
|
|
281
|
+
attr_reader :parent, :name, :children
|
|
282
|
+
attr_accessor :attributes, :data
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
# Creates new Token with given parent
|
|
286
|
+
def initialize ( parent,name,standalone=false,comment=false )
|
|
287
|
+
|
|
288
|
+
@parent = parent
|
|
289
|
+
@name = name
|
|
290
|
+
@children = Array.new
|
|
291
|
+
@comment = comment
|
|
292
|
+
@standalone = standalone
|
|
293
|
+
@data = nil
|
|
294
|
+
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# Reverses receiver's children array. Used at the end of parsing process, as
|
|
298
|
+
# parsing is performed backwards (otherwise it is hard to distinguish
|
|
299
|
+
# open/close and standalone tags)
|
|
300
|
+
def reverse ()
|
|
301
|
+
@children.reverse!
|
|
302
|
+
@children.each { |child| child.reverse() }
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# Returns true if the receiver represents a Component
|
|
306
|
+
def sws? ()
|
|
307
|
+
return @name != nil && @attributes != nil
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Returns name of the Component
|
|
311
|
+
def sws_name ()
|
|
312
|
+
return @attributes["sws"]
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
# Returns true if the receiver represents a HTML tag
|
|
317
|
+
def tag? ()
|
|
318
|
+
return @name != nil && @data != nil
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# Returns true if the receiver is a purely text one
|
|
323
|
+
def text? ()
|
|
324
|
+
return @name == nil
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# Returns true if the receiver is a HTML comment
|
|
329
|
+
def comment? ()
|
|
330
|
+
return @comment
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# Returns true if the receiver is a standalone tag
|
|
335
|
+
def standalone? ()
|
|
336
|
+
return @standalone
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# Returns string representation of the receiver
|
|
341
|
+
def to_s ()
|
|
342
|
+
|
|
343
|
+
if( comment? )
|
|
344
|
+
return "comment"
|
|
345
|
+
elsif ( text? )
|
|
346
|
+
return "text"
|
|
347
|
+
elsif( tag? )
|
|
348
|
+
return "#{data}"
|
|
349
|
+
else
|
|
350
|
+
attrs = @attributes.to_a.join( "| " )
|
|
351
|
+
return "#{@name} #{attrs}"
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Dumps the token recursively in a tree form.
|
|
357
|
+
def dump ( prefix )
|
|
358
|
+
if( comment? )
|
|
359
|
+
puts "#{prefix}comment"
|
|
360
|
+
elsif ( text? )
|
|
361
|
+
puts "#{prefix}text"
|
|
362
|
+
elsif( tag? )
|
|
363
|
+
puts "#{prefix}#{data}"
|
|
364
|
+
@children.each { |child| child.dump( prefix + " " ) }
|
|
365
|
+
else
|
|
366
|
+
attrs = @attributes.to_a.join( "| " )
|
|
367
|
+
puts "#{prefix}#{@name} #{attrs}"
|
|
368
|
+
@children.each { |child| child.dump( prefix + " " ) }
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
end
|