修訂 | 132 (tree) |
---|---|
時間 | 2008-11-02 05:20:26 |
作者 | junkikuchi |
updated.
@@ -1,215 +1,155 @@ | ||
1 | 1 | |
2 | 2 | CONFIG[:init_model] = { |
3 | - 'service' => [ | |
3 | + 'admin_service' => [ | |
4 | 4 | { |
5 | - :name => 'yui-layout', | |
5 | + :name => 'admin', | |
6 | 6 | :class => 'Service', |
7 | - :row => {'name' => 'yui-layout'}, | |
8 | - :relation => {'default_view' => 'view:html.yui.layout'}, | |
7 | + :row => {'name' => 'admin'}, | |
8 | + :relation => {'default_view' => 'admin_view:admin'}, | |
9 | 9 | }, |
10 | - { | |
11 | - :name => 'yui-treeview', | |
12 | - :class => 'Service', | |
13 | - :row => {'name' => 'yui-treeview'}, | |
14 | - :relation => {'default_view' => 'view:html.yui.treeview'}, | |
15 | - }, | |
16 | 10 | ], |
17 | 11 | |
18 | - 'view' => [ | |
12 | + 'admin_view' => [ | |
19 | 13 | { |
20 | - :name => 'html', | |
14 | + :name => 'admin', | |
21 | 15 | :class => 'View', |
22 | - :row => {'name' => 'html'}, | |
23 | - :relation => {'resource' => 'widget:html'}, | |
16 | + :row => {'name' => 'admin'}, | |
17 | + :relation => {'model' => 'admin_widget:html'}, | |
24 | 18 | }, |
25 | 19 | { |
26 | - :name => 'html.yui', | |
20 | + :name => 'view', | |
27 | 21 | :class => 'View', |
28 | - :row => {'name' => 'yui'}, | |
29 | - :relation => {'resource' => 'widget:html'}, | |
22 | + :row => {'name' => 'view'}, | |
23 | + :relation => {'model' => 'admin_widget:view'}, | |
30 | 24 | }, |
31 | 25 | { |
32 | - :name => 'html.yui.layout', | |
26 | + :name => 'view.json', | |
33 | 27 | :class => 'View', |
34 | - :row => {'name' => 'layout'}, | |
35 | - :relation => {'resource' => 'widget:html'}, | |
28 | + :row => {'name' => 'view-json'}, | |
29 | + :relation => {'model' => 'admin_widget:view'}, | |
36 | 30 | }, |
37 | 31 | { |
38 | - :name => 'html.yui.treeview', | |
32 | + :name => 'view.widget', | |
39 | 33 | :class => 'View', |
40 | - :row => {'name' => 'treeview'}, | |
41 | - :relation => {'resource' => 'widget:html'}, | |
34 | + :row => {'name' => 'widget'}, | |
35 | + :relation => {'model' => 'admin_widget:view.widget'}, | |
42 | 36 | }, |
37 | + { | |
38 | + :name => 'view.widget.json', | |
39 | + :class => 'View', | |
40 | + :row => {'name' => 'widget-json'}, | |
41 | + :relation => {'model' => 'admin_widget:view.widget'}, | |
42 | + }, | |
43 | 43 | ], |
44 | 44 | |
45 | - 'widget' => [ | |
45 | + 'admin_widget' => [ | |
46 | 46 | { |
47 | 47 | :name => 'html', |
48 | 48 | :class => 'Widget::HTML', |
49 | - :relation => {'view' => 'view:html'}, | |
50 | - :css => [ | |
51 | - ['*_font_text-align', 'left'], | |
52 | - ['ul li_margin_margin-left', '10px'] | |
53 | - ], | |
49 | + :relation => {'view' => 'admin_view:admin'}, | |
54 | 50 | }, |
55 | 51 | { |
56 | - :name => 'html.yui', | |
57 | - :class => 'Widget::Yahoo::UI::Loader', | |
58 | - :relation => {'view' => 'view:html.yui'}, | |
52 | + :name => 'html.google_ajax_loader', | |
53 | + :class => 'Widget::HTML::JavaScript', | |
54 | + :relation => {'view' => 'admin_view:admin'}, | |
55 | + :property => { | |
56 | + :src => 'http://www.google.com/jsapi', | |
57 | + }, | |
59 | 58 | }, |
60 | 59 | { |
61 | - :name => 'html.yui.reset_css', | |
62 | - :class => 'Widget::Yahoo::UI::ResetCSS', | |
63 | - :relation => {'view' => 'view:html.yui'}, | |
64 | - }, | |
65 | - { | |
66 | - :name => 'html.yui.layout', | |
67 | - :class => 'Widget::Yahoo::UI::Layout', | |
68 | - :relation => {'view' => 'view:html.yui.layout'}, | |
69 | - :css => [ | |
70 | - ['.yui-layout-unit-top_background_background-color', '#c3d9ff'], | |
71 | - ['.yui-layout-unit-right_background_background-color', '#e1ecfe'], | |
72 | - ['.yui-layout-unit-bottom_background_background-color', '#c3d9ff'], | |
73 | - ['.yui-layout-unit-left_background_background-color', '#e1ecfe'], | |
74 | - ['.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob_background_background-color', '#c3d9ff'], | |
75 | - ['.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob_layout_position', 'absolute'], | |
76 | - ['.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob_size_height', '100%'], | |
77 | - ['.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob_size_width', '5px'], | |
78 | - ['.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob_layout_top', '0px'], | |
79 | - ['.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob_layout_left', '0px'], | |
80 | - ['.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob_background_background-color', '#c3d9ff'], | |
81 | - ['.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob_layout_position', 'absolute'], | |
82 | - ['.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob_size_height', '100%'], | |
83 | - ['.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob_size_width', '5px'], | |
84 | - ['.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob_layout_top', '0px'], | |
85 | - ['.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob_layout_left', '0px'], | |
86 | - ], | |
60 | + :name => 'html.yahoo_ui_loader', | |
61 | + :class => 'Widget::HTML::JavaScript', | |
62 | + :relation => {'view' => 'admin_view:admin'}, | |
87 | 63 | :property => { |
88 | - :top_body => "'hd'", | |
89 | - :top_height => '50', | |
90 | - | |
91 | - :right_body => "'right'", | |
92 | - :right_width => '200', | |
93 | - :right_resize => 'true', | |
94 | - :right_scroll => 'true', | |
95 | - :right_gutter => "'0 0 0 10px'", | |
96 | - | |
97 | - :bottom_body => "'ft'", | |
98 | - :bottom_height => '25', | |
99 | - | |
100 | - :left_body => "'left'", | |
101 | - :left_width => '200', | |
102 | - :left_resize => 'true', | |
103 | - :left_scroll => 'true', | |
104 | - | |
105 | - :center_body => "'center'" | |
106 | - } | |
64 | + :src => 'http://yui.yahooapis.com/2.6.0/build/yuiloader/yuiloader-min.js', | |
65 | + }, | |
107 | 66 | }, |
108 | 67 | { |
109 | - :name => 'html.doc', | |
110 | - :class => 'Widget::Panel', | |
111 | - :relation => {'view' => 'view:html.yui.layout'}, | |
68 | + :name => 'html.yahoo_ui_reset_css', | |
69 | + :class => 'Widget::HTML::CSS', | |
70 | + :relation => {'view' => 'admin_view:admin'}, | |
112 | 71 | :property => { |
113 | - :html_attr_id => 'doc3' | |
72 | + :src => 'http://yui.yahooapis.com/2.6.0/build/reset-fonts-grids/reset-fonts-grids.css', | |
114 | 73 | } |
115 | 74 | }, |
116 | 75 | { |
117 | - :name => 'html.doc.hd', | |
118 | - :class => 'Widget::Panel', | |
119 | - :relation => {'view' => 'view:html.yui.layout'}, | |
76 | + :name => 'html.admin_css', | |
77 | + :class => 'Widget::HTML::CSS', | |
78 | + :relation => {'view' => 'admin_view:admin'}, | |
120 | 79 | :property => { |
121 | - :html_attr_id => 'hd' | |
80 | + :src => 'http://bonnou.com/s/admin.css', | |
122 | 81 | } |
123 | 82 | }, |
124 | 83 | { |
125 | - :name => 'html.doc.bd', | |
126 | - :class => 'Widget::Panel', | |
127 | - :relation => {'view' => 'view:html.yui.layout'}, | |
84 | + :name => 'html.admin_js', | |
85 | + :class => 'Widget::HTML::JavaScript', | |
86 | + :relation => {'view' => 'admin_view:admin'}, | |
128 | 87 | :property => { |
129 | - :html_attr_id => 'bd' | |
130 | - } | |
88 | + :src => 'http://bonnou.com/s/admin.js', | |
89 | + }, | |
131 | 90 | }, |
132 | 91 | { |
133 | - :name => 'html.doc.bd.yui-main', | |
92 | + :name => 'html.top', | |
134 | 93 | :class => 'Widget::Panel', |
135 | - :relation => {'view' => 'view:html.yui.layout'}, | |
94 | + :relation => {'view' => 'admin_view:admin'}, | |
136 | 95 | :property => { |
137 | - :html_attr_id => 'yui-main' | |
96 | + :html_attr_id => 'top' | |
138 | 97 | } |
139 | 98 | }, |
140 | 99 | { |
141 | - :name => 'html.doc.bd.yui-main.yui-b', | |
100 | + :name => 'html.right', | |
142 | 101 | :class => 'Widget::Panel', |
143 | - :relation => {'view' => 'view:html.yui.layout'}, | |
102 | + :relation => {'view' => 'admin_view:admin'}, | |
144 | 103 | :property => { |
145 | - :html_attr_class => 'yui-b' | |
104 | + :html_attr_id => 'right' | |
146 | 105 | } |
147 | 106 | }, |
148 | 107 | { |
149 | - :name => 'html.doc.bd.yui-main.yui-b.yui-gb', | |
108 | + :name => 'html.bottom', | |
150 | 109 | :class => 'Widget::Panel', |
151 | - :relation => {'view' => 'view:html.yui.layout'}, | |
110 | + :relation => {'view' => 'admin_view:admin'}, | |
152 | 111 | :property => { |
153 | - :html_attr_class => 'yui-gb' | |
112 | + :html_attr_id => 'bottom' | |
154 | 113 | } |
155 | 114 | }, |
156 | 115 | { |
157 | - :name => 'html.doc.bd.yui-main.yui-b.yui-gb.left', | |
116 | + :name => 'html.left', | |
158 | 117 | :class => 'Widget::Panel', |
159 | - :relation => {'view' => 'view:html.yui.layout'}, | |
118 | + :relation => {'view' => 'admin_view:admin'}, | |
160 | 119 | :property => { |
161 | - :html_attr_id => 'left', | |
162 | - :html_attr_class => 'yui-u first' | |
120 | + :html_attr_id => 'left' | |
163 | 121 | } |
164 | 122 | }, |
165 | 123 | { |
166 | - :name => 'html.doc.bd.yui-main.yui-b.yui-gb.center', | |
124 | + :name => 'html.center', | |
167 | 125 | :class => 'Widget::Panel', |
168 | - :relation => {'view' => 'view:html.yui.layout'}, | |
126 | + :relation => {'view' => 'admin_view:admin'}, | |
169 | 127 | :property => { |
170 | - :html_attr_id => 'center', | |
171 | - :html_attr_class => 'yui-u' | |
128 | + :html_attr_id => 'center' | |
172 | 129 | } |
173 | 130 | }, |
131 | + | |
174 | 132 | { |
175 | - :name => 'html.doc.bd.yui-main.yui-b.yui-gb.right', | |
176 | - :class => 'Widget::Panel', | |
177 | - :relation => {'view' => 'view:html.yui.layout'}, | |
178 | - :property => { | |
179 | - :html_attr_id => 'right', | |
180 | - :html_attr_class => 'yui-u' | |
181 | - } | |
133 | + :name => 'view', | |
134 | + :class => 'Widget::View', | |
135 | + :relation => {'view' => 'admin_view:view'}, | |
136 | + :property => {:model => 'admin_view'}, | |
182 | 137 | }, |
183 | 138 | { |
184 | - :name => 'html.doc.ft', | |
185 | - :class => 'Widget::Panel', | |
186 | - :relation => {'view' => 'view:html.yui.layout'}, | |
187 | - :property => { | |
188 | - :html_attr_id => 'ft' | |
189 | - } | |
139 | + :name => 'view.json', | |
140 | + :class => 'Widget::View::JSON', | |
141 | + :relation => {'view' => 'admin_view:view.json'}, | |
190 | 142 | }, |
191 | - | |
192 | 143 | { |
193 | - :name => 'html.yui.treeview', | |
194 | - :class => 'Widget::Yahoo::UI::TreeView', | |
195 | - :relation => {'view' => 'view:html.yui.treeview'}, | |
144 | + :name => 'view.widget', | |
145 | + :class => 'Widget::Widget', | |
146 | + :relation => {'view' => 'admin_view:view.widget'}, | |
147 | + :property => {:model => 'admin_widget'}, | |
196 | 148 | }, |
197 | - ], | |
198 | - | |
199 | - 'content' => [ | |
200 | - ], | |
201 | - | |
202 | - 'admin' => [ | |
203 | 149 | { |
204 | - :name => 'admin', | |
205 | - :class => 'User', | |
206 | - :row => { | |
207 | - 'name' => 'admin', | |
208 | - '_password' => Tenarai::ClassLoader['User'].digest('admin') | |
209 | - }, | |
210 | - :relation => {}, | |
150 | + :name => 'view.widget.json', | |
151 | + :class => 'Widget::Widget::JSON', | |
152 | + :relation => {'view' => 'admin_view:view.widget.json'}, | |
211 | 153 | }, |
212 | 154 | ], |
213 | - | |
214 | - 'user' => [], | |
215 | 155 | } |
@@ -0,0 +1,394 @@ | ||
1 | +#-- | |
2 | +# | |
3 | +# Utility for creating Google Maps Encoded GPolylines | |
4 | +# | |
5 | +# License: You may distribute this code under the same terms as Ruby itself | |
6 | +# | |
7 | +# Author: Joel Rosenberg | |
8 | +# | |
9 | +# ( Drawing from the official example pages as well as Mark McClure's work ) | |
10 | +# | |
11 | +# == Example | |
12 | +# | |
13 | +# data = [ | |
14 | +# [ 37.4419, -122.1419], | |
15 | +# [ 37.4519, -122.1519], | |
16 | +# [ 37.4619, -122.1819], | |
17 | +# ] | |
18 | +# | |
19 | +# encoder = GMapPolylineEncoder.new() | |
20 | +# result = encoder.encode( data ) | |
21 | +# | |
22 | +# javascript << " var myLine = new GPolyline.fromEncoded({\n" | |
23 | +# javascript << " color: \"#FF0000\",\n" | |
24 | +# javascript << " weight: 10,\n" | |
25 | +# javascript << " opacity: 0.5,\n" | |
26 | +# javascript << " zoomFactor: #{result[:zoomFactor]},\n" | |
27 | +# javascript << " numLevels: #{result[:numLevels]},\n" | |
28 | +# javascript << " points: \"#{result[:points]}\",\n" | |
29 | +# javascript << " levels: \"#{result[:levels]}\"\n" | |
30 | +# javascript << " });" | |
31 | +# | |
32 | +# == Methods | |
33 | +# | |
34 | +# Constructor args (all optional): | |
35 | +# :numLevels (default 18) | |
36 | +# :zoomFactor (default 2) | |
37 | +# :reduce: Reduce points (default true) | |
38 | +# :escape: Escape backslashes (default true) | |
39 | +# | |
40 | +# encode( points ) method | |
41 | +# points (required): array of longitude, latitude pairs | |
42 | +# | |
43 | +# returns hash with keys :points, :levels, :zoomFactor, :numLevels | |
44 | +# | |
45 | +# == Background | |
46 | +# | |
47 | +# Description: http://www.google.com/apis/maps/documentation/#Encoded_Polylines | |
48 | +# API: http://www.google.com/apis/maps/documentation/reference.html#GPolyline | |
49 | +# Hints: http://www.google.com/apis/maps/documentation/polylinealgorithm.html | |
50 | +# | |
51 | +# Example Javascript for instantiating an encoded polyline: | |
52 | +# var encodedPolyline = new GPolyline.fromEncoded({ | |
53 | +# color: "#FF0000", | |
54 | +# weight: 10, | |
55 | +# points: "yzocFzynhVq}@n}@o}@nzD", | |
56 | +# levels: "BBB", | |
57 | +# zoomFactor: 32, | |
58 | +# numLevels: 4 | |
59 | +# }); | |
60 | +# | |
61 | +# == Changes | |
62 | +# | |
63 | +# 08.14.2007 - Release 0.2 | |
64 | +# Doug Fales pointed out a null pointer exception bug in the zoom | |
65 | +# factor implementation | |
66 | +# | |
67 | +# 06.29.2007 - Release 0.1 | |
68 | +# Profiling showed that distance() accounted for 50% of the time when | |
69 | +# processing McClure's British coast data. By moving the distance | |
70 | +# calculation into encode(), we can cache a few of the calculations | |
71 | +# (magnitude) and eliminate the overhead of the function call. This | |
72 | +# reduced the time to encode by ~ 30% | |
73 | +# | |
74 | +# 06.21.2007 Implementing the Doublas-Peucker algorithm for removing superflous | |
75 | +# points as per Mark McClure's design: | |
76 | +# http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/ | |
77 | +# | |
78 | +# 10.14.2006 Cleaned up (and finally grasped) zoom levels | |
79 | +# | |
80 | +# 09.2006 First port of the official example's javascript. Ignoring zoom | |
81 | +# levels for now, showing points at all zoom levels | |
82 | +# | |
83 | +#++ | |
84 | + | |
85 | +class GMapPolylineEncoder | |
86 | + attr_accessor :reduce, :escape #zoomFactor and numLevels need side effects | |
87 | + attr_reader :zoomFactor, :numLevels | |
88 | + | |
89 | + # The minimum distance from the line that a point must exceed to avoid | |
90 | + # elimination under the DP Algorithm. | |
91 | + @@dp_threshold = 0.00001 | |
92 | + | |
93 | + def initialize(options = {}) | |
94 | + # There are no required parameters | |
95 | + | |
96 | + # Nice defaults | |
97 | + @numLevels = options.has_key?(:numLevels) ? options[:numLevels] : 18 | |
98 | + @zoomFactor = options.has_key?(:zoomFactor) ? options[:zoomFactor] : 2 | |
99 | + | |
100 | + # Calculate the distance thresholds for each zoom level | |
101 | + calculate_zoom_breaks() | |
102 | + | |
103 | + # By default we'll simplify the polyline unless told otherwise | |
104 | + @reduce = ! options.has_key?(:reduce) ? true : options[:reduce] | |
105 | + | |
106 | + # Escape by default; most people are using this in a web context | |
107 | + @escape = ! options.has_key?(:escape) ? true : options[:escape] | |
108 | + | |
109 | + end | |
110 | + | |
111 | + def numLevels=( new_num_levels ) | |
112 | + @numLevels = new_num_levels | |
113 | + # We need to recalculate our zoom breaks | |
114 | + calculate_zoom_breaks() | |
115 | + end | |
116 | + | |
117 | + def zoomFactor=( new_zoom_factor ) | |
118 | + @zoomFactor = new_zoom_factor | |
119 | + # We need to recalculate our zoom breaks | |
120 | + calculate_zoom_breaks() | |
121 | + end | |
122 | + | |
123 | + def encode( points ) | |
124 | + | |
125 | + # | |
126 | + # This is an implementation of the Douglas-Peucker algorithm for simplifying | |
127 | + # a line. You can thing of it as an elimination of points that do not | |
128 | + # deviate enough from a vector. That threshold for point elimination is in | |
129 | + # @@dp_threshold. See | |
130 | + # | |
131 | + # http://everything2.com/index.pl?node_id=859282 | |
132 | + # | |
133 | + # for an explanation of the algorithm | |
134 | + # | |
135 | + | |
136 | + max_dist = 0 # Greatest distance we measured during the run | |
137 | + stack = [] | |
138 | + distances = Array.new(points.size) | |
139 | + | |
140 | + if(points.length > 2) | |
141 | + stack << [0, points.size-1] | |
142 | + | |
143 | + while(stack.length > 0) | |
144 | + current_line = stack.pop() | |
145 | + p1_idx = current_line[0] | |
146 | + pn_idx = current_line[1] | |
147 | + pb_dist = 0 | |
148 | + pb_idx = nil | |
149 | + | |
150 | + x1 = points[p1_idx][0] | |
151 | + y1 = points[p1_idx][1] | |
152 | + x2 = points[pn_idx][0] | |
153 | + y2 = points[pn_idx][1] | |
154 | + | |
155 | + # Caching the line's magnitude for performance | |
156 | + magnitude = Math.sqrt((x2 - x1)**2 + (y2 - y1)**2) | |
157 | + magnitude_squared = magnitude ** 2 | |
158 | + | |
159 | + # Find the farthest point and its distance from the line between our pair | |
160 | + for i in (p1_idx+1)..(pn_idx-1) | |
161 | + | |
162 | + # Refactoring distance computation inline for performance | |
163 | + #current_distance = compute_distance(points[i], points[p1_idx], points[pn_idx]) | |
164 | + | |
165 | + # | |
166 | + # This uses Euclidian geometry. It shouldn't be that big of a deal since | |
167 | + # we're using it as a rough comparison for line elimination and zoom | |
168 | + # calculation. | |
169 | + # | |
170 | + # TODO: Implement Haversine functions which would probably bring this to | |
171 | + # a snail's pace (ehhhh) | |
172 | + # | |
173 | + | |
174 | + px = points[i][0] | |
175 | + py = points[i][1] | |
176 | + | |
177 | + current_distance = nil | |
178 | + | |
179 | + if( magnitude == 0 ) | |
180 | + # The line is really just a point | |
181 | + current_distance = Math.sqrt((x2-px)**2 + (y2-py)**2) | |
182 | + else | |
183 | + | |
184 | + u = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) / magnitude_squared | |
185 | + | |
186 | + if( u <= 0 || u > 1 ) | |
187 | + # The point is closest to an endpoint. Find out which one | |
188 | + ix = Math.sqrt((x1 - px)**2 + (y1 - py)**2) | |
189 | + iy = Math.sqrt((x2 - px)**2 + (y2 - py)**2) | |
190 | + if( ix > iy ) | |
191 | + current_distance = iy | |
192 | + else | |
193 | + current_distance = ix | |
194 | + end | |
195 | + else | |
196 | + # The perpendicular point intersects the line | |
197 | + ix = x1 + u * (x2 - x1) | |
198 | + iy = y1 + u * (y2 - y1) | |
199 | + current_distance = Math.sqrt((ix - px)**2 + (iy - py)**2) | |
200 | + end | |
201 | + end | |
202 | + | |
203 | + # See if this distance is the greatest for this segment so far | |
204 | + if(current_distance > pb_dist) | |
205 | + pb_dist = current_distance | |
206 | + pb_idx = i | |
207 | + end | |
208 | + end | |
209 | + | |
210 | + # See if this is the greatest distance for all points | |
211 | + if(pb_dist > max_dist) | |
212 | + max_dist = pb_dist | |
213 | + end | |
214 | + | |
215 | + if(pb_dist > @@dp_threshold) | |
216 | + # Our point, Pb, that had the greatest distance from the line, is also | |
217 | + # greater than our threshold. Process again using Pb as a new | |
218 | + # start/end point. Record this distance - we'll use it later when | |
219 | + # creating zoom values | |
220 | + distances[pb_idx] = pb_dist | |
221 | + stack << [p1_idx, pb_idx] | |
222 | + stack << [pb_idx, pn_idx] | |
223 | + end | |
224 | + | |
225 | + end | |
226 | + end | |
227 | + | |
228 | + # Force line endpoints to be included (sloppy, but faster than checking for | |
229 | + # endpoints in encode_points()) | |
230 | + distances[0] = max_dist | |
231 | + distances[distances.length-1] = max_dist | |
232 | + | |
233 | + # Create Base64 encoded strings for our points and zoom levels | |
234 | + points_enc = encode_points( points, distances) | |
235 | + levels_enc = encode_levels( points, distances, max_dist) | |
236 | + | |
237 | + # Make points_enc an escaped string if desired. | |
238 | + # We should escape the levels too, in case google pulls a switcheroo | |
239 | + @escape && points_enc && points_enc.gsub!( /\\/, '\\\\\\\\' ) | |
240 | + | |
241 | + | |
242 | + # Returning a hash. Yes, I am a Perl programmer | |
243 | + return { | |
244 | + :points => points_enc, | |
245 | + :levels => levels_enc, | |
246 | + :zoomFactor => @zoomFactor, | |
247 | + :numLevels => @numLevels, | |
248 | + } | |
249 | + | |
250 | + end | |
251 | + | |
252 | + private | |
253 | + | |
254 | + def calculate_zoom_breaks() | |
255 | + # Calculate the distance thresholds for each zoom level | |
256 | + @zoom_level_breaks = Array.new(@numLevels); | |
257 | + | |
258 | + for i in 0..(@numLevels-1) | |
259 | + @zoom_level_breaks[i] = @@dp_threshold * (@zoomFactor ** ( @numLevels-i-1)); | |
260 | + end | |
261 | + | |
262 | + return | |
263 | + end | |
264 | + | |
265 | + def encode_points( points, distances ) | |
266 | + encoded = "" | |
267 | + | |
268 | + plat = 0 | |
269 | + plon = 0 | |
270 | + | |
271 | + #points.each do |point| # Gah, need the distances. | |
272 | + for i in 0..(points.size() - 1) | |
273 | + if(! @reduce || distances[i] != nil ) | |
274 | + point = points[i] | |
275 | + late5 = (point[0] * 1e5).floor(); | |
276 | + lone5 = (point[1] * 1e5).floor(); | |
277 | + | |
278 | + dlat = late5 - plat | |
279 | + dlon = lone5 - plon | |
280 | + | |
281 | + plat = late5; | |
282 | + plon = lone5; | |
283 | + | |
284 | + # I used to need this for some reason | |
285 | + #encoded << encodeSignedNumber(Fixnum.induced_from(dlat)).to_s | |
286 | + #encoded << encodeSignedNumber(Fixnum.induced_from(dlon)).to_s | |
287 | + encoded << encodeSignedNumber(dlat).to_s | |
288 | + encoded << encodeSignedNumber(dlon).to_s | |
289 | + end | |
290 | + end | |
291 | + | |
292 | + return encoded | |
293 | + | |
294 | + end | |
295 | + | |
296 | + def encode_levels( points, distances, max_dist ) | |
297 | + | |
298 | + encoded = ""; | |
299 | + | |
300 | + # Force startpoint | |
301 | + encoded << encodeNumber(@numLevels - 1) | |
302 | + | |
303 | + if( points.size() > 2 ) | |
304 | + for i in 1..(points.size() - 2) | |
305 | + distance = distances[i] | |
306 | + if( ! @reduce || distance != nil) | |
307 | + computed_level = 0 | |
308 | + | |
309 | + while (distance and (distance < @zoom_level_breaks[computed_level])) do | |
310 | + computed_level += 1 | |
311 | + end | |
312 | + | |
313 | + encoded << encodeNumber( @numLevels - computed_level - 1 ) | |
314 | + end | |
315 | + end | |
316 | + end | |
317 | + | |
318 | + # Force endpoint | |
319 | + encoded << encodeNumber(@numLevels - 1) | |
320 | + | |
321 | + return encoded; | |
322 | + | |
323 | + end | |
324 | + | |
325 | + def compute_distance( point, lineStart, lineEnd ) | |
326 | + | |
327 | + # | |
328 | + # Note: This has been refactored to encode() inline for performance and | |
329 | + # computation caching | |
330 | + # | |
331 | + | |
332 | + px = point[0] | |
333 | + py = point[1] | |
334 | + x1 = lineStart[0] | |
335 | + y1 = lineStart[1] | |
336 | + x2 = lineEnd[0] | |
337 | + y2 = lineEnd[1] | |
338 | + | |
339 | + distance = nil | |
340 | + | |
341 | + magnitude = Math.sqrt((x2 - x1)**2 + (y2 - y1)**2) | |
342 | + | |
343 | + if( magnitude == 0 ) | |
344 | + return Math.sqrt((x2-px)**2 + (y2-py)**2) | |
345 | + end | |
346 | + | |
347 | + u = (((px - x1) * (x2 - x1)) + ((py - y1) * (y2 - y1))) / (magnitude**2) | |
348 | + | |
349 | + if( u <= 0 || u > 1 ) | |
350 | + # The point is closest to an endpoint. Find out which | |
351 | + ix = Math.sqrt((x1 - px)**2 + (y1 - py)**2) | |
352 | + iy = Math.sqrt((x2 - px)**2 + (y2 - py)**2) | |
353 | + if( ix > iy ) | |
354 | + distance = iy | |
355 | + else | |
356 | + distance = ix | |
357 | + end | |
358 | + else | |
359 | + # The perpendicular point intersects the line | |
360 | + ix = x1 + u * (x2 - x1) | |
361 | + iy = y1 + u * (y2 - y1) | |
362 | + distance = Math.sqrt((ix - px)**2 + (iy - py)**2) | |
363 | + end | |
364 | + | |
365 | + return distance | |
366 | + end | |
367 | + | |
368 | + def encodeSignedNumber(num) | |
369 | + # Based on the official google example | |
370 | + | |
371 | + sgn_num = num << 1 | |
372 | + | |
373 | + if( num < 0 ) | |
374 | + sgn_num = ~(sgn_num) | |
375 | + end | |
376 | + | |
377 | + return encodeNumber(sgn_num) | |
378 | + end | |
379 | + | |
380 | + def encodeNumber(num) | |
381 | + # Based on the official google example | |
382 | + | |
383 | + encoded = ""; | |
384 | + | |
385 | + while (num >= 0x20) do | |
386 | + encoded << ((0x20 | (num & 0x1f)) + 63).chr; | |
387 | + num = num >> 5; | |
388 | + end | |
389 | + | |
390 | + encoded << (num + 63).chr; | |
391 | + return encoded; | |
392 | + end | |
393 | + | |
394 | +end |
@@ -0,0 +1,285 @@ | ||
1 | +# = Simple JSON parser & builder | |
2 | +# | |
3 | +# Author:: Chihiro Ito | |
4 | +# License:: Public domain (unlike other files) | |
5 | +# Support:: http://groups.google.com/group/webos-goodies/ | |
6 | +# | |
7 | +# This file contains two simple JSON processing classes. JsonParser | |
8 | +# converts a JSON string to an array or a hash. JsonBuilder performs | |
9 | +# vice versa. These classes are standard compliant and are designed for | |
10 | +# stability and reliability. Especially JsonParser has UTF-8 validation | |
11 | +# functionality so you can avoid some kind of security attack. | |
12 | + | |
13 | +require 'strscan' | |
14 | + | |
15 | + | |
16 | +# = Simple JSON parser | |
17 | +# | |
18 | +# This class converts a JSON string to an array or a hash. If *json_str* | |
19 | +# contains a JSON form string, you can convert it like below. | |
20 | +# | |
21 | +# ruby_obj = JsonParser.new.parse(json_str) | |
22 | +# | |
23 | +# If *json_str* has one or more invalid UTF-8 sequence, JsonParser throws | |
24 | +# exception by default. You can change this behavior to replacing with an | |
25 | +# arbitrary unicode character. See below for details. | |
26 | +class JsonParser | |
27 | + | |
28 | + #:stopdoc: | |
29 | + Debug = false | |
30 | + Name = 'JsonParser' | |
31 | + ERR_IllegalSyntax = "[#{Name}] Syntax error" | |
32 | + ERR_IllegalUnicode = "[#{Name}] Illegal unicode sequence" | |
33 | + | |
34 | + StringRegex = /\s*"((?:\\.|[^"\\])*)"/n | |
35 | + ValueRegex = /\s*(?: | |
36 | + (true)|(false)|(null)| # 1:true, 2:false, 3:null | |
37 | + (?:\"((?:\\.|[^\"\\])*)\")| # 4:String | |
38 | + ([-+]?\d+\.\d+(?:[eE][-+]?\d+)?)| # 5:Float | |
39 | + ([-+]?\d+)| # 6:Integer | |
40 | + (\{)|(\[))/xn # 7:Hash, 8:Array | |
41 | + #:startdoc: | |
42 | + | |
43 | + # Create a new instance of JsonParser. *options* can contain these values. | |
44 | + # [:validation] | |
45 | + # If set to false, UTF-8 validation is disabled. true by default. | |
46 | + # [:surrogate] | |
47 | + # If set to false, surrogate pair support is disabled. true by default. | |
48 | + # [:malformed_chr] | |
49 | + # An invalid sequence in JSON string will be replaced with this value. | |
50 | + # If set to nil, An exception will be thrown in this situation. | |
51 | + # nil by default. | |
52 | + def initialize(options = {}) | |
53 | + @default_validation = options.has_key?(:validation) ? options[:validation] : true | |
54 | + @default_surrogate = options.has_key?(:surrogate) ? options[:surrogate] : true | |
55 | + @default_malformed_chr = options.has_key?(:malformed_chr) ? options[:malformed_chr] : nil | |
56 | + end | |
57 | + | |
58 | + # Convert *str* to an array or hash. | |
59 | + # [str] | |
60 | + # A JSON form string. This must be encoded using UTF-8. | |
61 | + # [options] | |
62 | + # Same as new. | |
63 | + def parse(str, options = {}) | |
64 | + @enable_validation = options.has_key?(:validation) ? options[:validation] : @default_validation | |
65 | + @enable_surrogate = options.has_key?(:surrogate) ? options[:surrogate] : @default_surrogate | |
66 | + @malformed_chr = options.has_key?(:malformed_chr) ? options[:malformed_chr] : @default_malformed_chr | |
67 | + @malformed_chr = @malformed_chr[0] if String === @malformed_chr | |
68 | + @scanner = StringScanner.new(str) | |
69 | + obj = case get_symbol[0] | |
70 | + when ?{ then parse_hash | |
71 | + when ?[ then parse_array | |
72 | + else raise err_msg(ERR_IllegalSyntax) | |
73 | + end | |
74 | + @scanner = nil | |
75 | + obj | |
76 | + end | |
77 | + | |
78 | + private #--------------------------------------------------------- | |
79 | + | |
80 | + def validate_string(str, malformed_chr = nil) | |
81 | + code = 0 | |
82 | + rest = 0 | |
83 | + range = nil | |
84 | + ucs = [] | |
85 | + str.each_byte do |c| | |
86 | + if rest <= 0 | |
87 | + case c | |
88 | + when 0x01..0x7f then rest = 0 ; ucs << c | |
89 | + when 0xc0..0xdf then rest = 1 ; code = c & 0x1f ; range = 0x00080..0x0007ff | |
90 | + when 0xe0..0xef then rest = 2 ; code = c & 0x0f ; range = 0x00800..0x00ffff | |
91 | + when 0xf0..0xf7 then rest = 3 ; code = c & 0x07 ; range = 0x10000..0x10ffff | |
92 | + else ucs << handle_malformed_chr(malformed_chr) | |
93 | + end | |
94 | + elsif (0x80..0xbf) === c | |
95 | + code = (code << 6) | (c & 0x3f) | |
96 | + if (rest -= 1) <= 0 | |
97 | + if !(range === code) || (0xd800..0xdfff) === code | |
98 | + code = handle_malformed_chr(malformed_chr) | |
99 | + end | |
100 | + ucs << code | |
101 | + end | |
102 | + else | |
103 | + ucs << handle_malformed_chr(malformed_chr) | |
104 | + rest = 0 | |
105 | + end | |
106 | + end | |
107 | + ucs << handle_malformed_chr(malformed_chr) if rest > 0 | |
108 | + ucs.pack('U*') | |
109 | + end | |
110 | + | |
111 | + def handle_malformed_chr(chr) | |
112 | + raise err_msg(ERR_IllegalUnicode) unless chr | |
113 | + chr | |
114 | + end | |
115 | + | |
116 | + def err_msg(err) | |
117 | + err + (Debug ? " #{@scanner.string[[0, @scanner.pos - 8].max,16].inspect}" : "") | |
118 | + end | |
119 | + | |
120 | + def unescape_string(str) | |
121 | + str = str.gsub(/\\(["\\\/bfnrt])/n) do | |
122 | + $1.tr('"\\/bfnrt', "\"\\/\b\f\n\r\t") | |
123 | + end.gsub(/(\\u[0-9a-fA-F]{4})+/n) do |matched| | |
124 | + seq = matched.scan(/\\u([0-9a-fA-F]{4})/n).flatten.map { |c| c.hex } | |
125 | + if @enable_surrogate | |
126 | + seq.each_index do |index| | |
127 | + if seq[index] && (0xd800..0xdbff) === seq[index] | |
128 | + n = index + 1 | |
129 | + raise err_msg(ERR_IllegalUnicode) unless seq[n] && 0xdc00..0xdfff === seq[n] | |
130 | + seq[index] = 0x10000 + ((seq[index] & 0x03ff) << 10) + (seq[n] & 0x03ff) | |
131 | + seq[n] = nil | |
132 | + end | |
133 | + end.compact! | |
134 | + end | |
135 | + seq.pack('U*') | |
136 | + end | |
137 | + str = validate_string(str, @malformed_chr) if @enable_validation | |
138 | + str | |
139 | + end | |
140 | + | |
141 | + def get_symbol | |
142 | + raise err_msg(ERR_IllegalSyntax) unless @scanner.scan(/\s*(.)/n) | |
143 | + @scanner[1] | |
144 | + end | |
145 | + | |
146 | + def peek_symbol | |
147 | + @scanner.match?(/\s*(.)/n) ? @scanner[1] : nil | |
148 | + end | |
149 | + | |
150 | + def parse_string | |
151 | + raise err_msg(ERR_IllegalSyntax) unless @scanner.scan(StringRegex) | |
152 | + unescape_string(@scanner[1]) | |
153 | + end | |
154 | + | |
155 | + def parse_value | |
156 | + raise err_msg(ERR_IllegalSyntax) unless @scanner.scan(ValueRegex) | |
157 | + case | |
158 | + when @scanner[1] then true | |
159 | + when @scanner[2] then false | |
160 | + when @scanner[3] then nil | |
161 | + when @scanner[4] then unescape_string(@scanner[4]) | |
162 | + when @scanner[5] then @scanner[5].to_f | |
163 | + when @scanner[6] then @scanner[6].to_i | |
164 | + when @scanner[7] then parse_hash | |
165 | + when @scanner[8] then parse_array | |
166 | + else raise err_msg(ERR_IllegalSyntax) | |
167 | + end | |
168 | + end | |
169 | + | |
170 | + def parse_hash | |
171 | + obj = {} | |
172 | + if peek_symbol[0] == ?} then get_symbol ; return obj ; end | |
173 | + while true | |
174 | + index = parse_string | |
175 | + raise err_msg(ERR_IllegalSyntax) unless get_symbol[0] == ?: | |
176 | + value = parse_value | |
177 | + obj[index] = value | |
178 | + case get_symbol[0] | |
179 | + when ?} then return obj | |
180 | + when ?, then next | |
181 | + else raise err_msg(ERR_IllegalSyntax) | |
182 | + end | |
183 | + end | |
184 | + end | |
185 | + | |
186 | + def parse_array | |
187 | + obj = [] | |
188 | + if peek_symbol[0] == ?] then get_symbol ; return obj ; end | |
189 | + while true | |
190 | + obj << parse_value | |
191 | + case get_symbol[0] | |
192 | + when ?] then return obj | |
193 | + when ?, then next | |
194 | + else raise err_msg(ERR_IllegalSyntax) | |
195 | + end | |
196 | + end | |
197 | + end | |
198 | + | |
199 | +end | |
200 | + | |
201 | +# = Simple JSON builder | |
202 | +# | |
203 | +# This class converts an Ruby object to a JSON string. you can convert | |
204 | +# *ruby_obj* like below. | |
205 | +# | |
206 | +# json_str = JsonBuilder.new.build(ruby_obj) | |
207 | +# | |
208 | +# *ruby_obj* must satisfy these conditions. | |
209 | +# - It must support to_s method, otherwise must be an array, a hash or nil. | |
210 | +# - All keys of a hash must support to_s method. | |
211 | +# - All values of an array or a hash must satisfy all conditions mentioned above. | |
212 | +# | |
213 | +# If the *ruby_obj* is not an array or a hash, it will be converted to an array | |
214 | +# with a single element. | |
215 | +class JsonBuilder | |
216 | + | |
217 | + #:stopdoc: | |
218 | + Name = 'JsonBuilder' | |
219 | + ERR_NestIsTooDeep = "[#{Name}] Array / Hash nested too deep." | |
220 | + ERR_NaN = "[#{Name}] NaN and Infinite are not permitted in JSON." | |
221 | + #:startdoc: | |
222 | + | |
223 | + # Create a new instance of JsonBuilder. *options* can contain these values. | |
224 | + # [:max_nest] | |
225 | + # If Array / Hash is nested more than this value, an exception would be thrown. | |
226 | + # 64 by default. | |
227 | + # [:nan] | |
228 | + # NaN is replaced with this value. If nil or false, an exception will be thrown. | |
229 | + # nil by default. | |
230 | + def initialize(options = {}) | |
231 | + @default_max_nest = options.has_key?(:max_nest) ? options[:max_nest] : 64 | |
232 | + @default_nan = options.has_key?(:nan) ? options[:nan] : nil | |
233 | + end | |
234 | + | |
235 | + # Convert *obj* to a JSON form string. | |
236 | + # [obj] | |
237 | + # A ruby object. this object must satisfy all conditions mentioned above. | |
238 | + # [options] | |
239 | + # Same as new. | |
240 | + def build(obj, options = {}) | |
241 | + @max_nest = options.has_key?(:max_nest) ? options[:max_nest] : @default_max_nest | |
242 | + @nan = options.has_key?(:nan) ? options[:nan] : @default_nan | |
243 | + case obj | |
244 | + when Array then build_array(obj, 0) | |
245 | + when Hash then build_object(obj, 0) | |
246 | + else build_array([obj], 0) | |
247 | + end | |
248 | + end | |
249 | + | |
250 | + private #--------------------------------------------------------- | |
251 | + | |
252 | + def escape(str) | |
253 | + str.gsub(/[^\x20-\x21\x23-\x5b\x5d-\xff]/n) do |chr| | |
254 | + if chr[0] != 0 && (index = "\"\\/\b\f\n\r\t".index(chr[0])) | |
255 | + "\\" + '"\\/bfnrt'[index, 1] | |
256 | + else | |
257 | + sprintf("\\u%04x", chr[0]) | |
258 | + end | |
259 | + end | |
260 | + end | |
261 | + | |
262 | + def build_value(obj, level) | |
263 | + case obj | |
264 | + when Integer, TrueClass, FalseClass then obj.to_s | |
265 | + when Float then raise ERR_NaN unless obj.finite? || (obj = @nan) ; obj.to_s | |
266 | + when NilClass then 'null' | |
267 | + when Array then build_array(obj, level + 1) | |
268 | + when Hash then build_object(obj, level + 1) | |
269 | + else "\"#{escape(obj.to_s)}\"" | |
270 | + end | |
271 | + end | |
272 | + | |
273 | + def build_array(obj, level) | |
274 | + raise ERR_NestIsTooDeep if level >= @max_nest | |
275 | + '[' + obj.map { |item| build_value(item, level) }.join(',') + ']' | |
276 | + end | |
277 | + | |
278 | + def build_object(obj, level) | |
279 | + raise ERR_NestIsTooDeep if level >= @max_nest | |
280 | + '{' + obj.map do |item| | |
281 | + "#{build_value(item[0].to_s,level)}:#{build_value(item[1],level)}" | |
282 | + end.join(',') + '}' | |
283 | + end | |
284 | + | |
285 | +end |
@@ -21,13 +21,37 @@ | ||
21 | 21 | @config = config |
22 | 22 | end |
23 | 23 | |
24 | + def dispatch(models, request, response) | |
25 | + _nil, name, *params = if request['path_info'].nil? | |
26 | + '/' | |
27 | + else | |
28 | + if request['path_info'].empty? | |
29 | + request['script_name'] | |
30 | + else | |
31 | + request['path_info'] | |
32 | + end | |
33 | + end.split('/') | |
34 | + | |
35 | + name ||= 'index' | |
36 | + | |
37 | + if name == 'admin' | |
38 | + service = models['admin_service'] | |
39 | + view = models['admin_view'] | |
40 | + else | |
41 | + service = models['user_service'] | |
42 | + view = models['user_view'] | |
43 | + end | |
44 | + | |
45 | + service.dispatch(name, view, models, params, request, response) | |
46 | + end | |
47 | + | |
24 | 48 | def server(name) |
25 | - model do |m| | |
26 | - m['service'].pub_dir = @config[:server][:pub_dir] | |
49 | + db do |models| | |
27 | 50 | Tenarai::Server.new(@config[:server]) do |request, response| |
28 | 51 | begin |
29 | - m['service'].dispatch(m, request, response) | |
30 | - m.refresh | |
52 | + response.code = 404 | |
53 | + dispatch(models, request, response) | |
54 | + models.refresh | |
31 | 55 | rescue |
32 | 56 | p $! |
33 | 57 | $!.backtrace.each do |val| |
@@ -50,36 +74,36 @@ | ||
50 | 74 | server('mongrel') |
51 | 75 | end |
52 | 76 | |
53 | - def model(&block) | |
77 | + def db(&block) | |
54 | 78 | Tenarai::DB.new(@config[:db]) do |db| |
55 | - block.call(Tenarai::DB::Container.new(db, @config[:model])) | |
79 | + block.call(Tenarai::DB::Container.new(db, @config[:models])) | |
56 | 80 | end |
57 | 81 | end |
58 | 82 | |
59 | 83 | def install |
60 | - model do |m| | |
61 | - m.create | |
84 | + db do |models| | |
85 | + models.create | |
62 | 86 | end |
63 | 87 | end |
64 | 88 | |
65 | 89 | def uninstall |
66 | - model do |m| | |
67 | - m.drop | |
90 | + db do |models| | |
91 | + models.drop | |
68 | 92 | end |
69 | 93 | end |
70 | 94 | |
71 | 95 | def load |
72 | - model do |m| | |
96 | + db do |models| | |
73 | 97 | map = {} |
74 | - create_row(m, map, @config[:init_model]) | |
75 | - update_property(m, map, @config[:init_model]) | |
98 | + create_row(models, map, @config[:init_model]) | |
99 | + update_property(models, map, @config[:init_model]) | |
76 | 100 | end |
77 | 101 | end |
78 | 102 | |
79 | - def create_row(model, map, init_model) | |
103 | + def create_row(models, map, init_model) | |
80 | 104 | init_model.each do |key, val| |
81 | 105 | val.each do |v| |
82 | - obj = Tenarai::ClassLoader[v[:class]].new(model[key], v[:row] || {}) | |
106 | + obj = Tenarai::ClassLoader[v[:class]].new(models[key], v[:row] || {}) | |
83 | 107 | obj.save |
84 | 108 | |
85 | 109 | map[key] ||= {} |
@@ -101,7 +125,7 @@ | ||
101 | 125 | end |
102 | 126 | private :create_row |
103 | 127 | |
104 | - def update_property(model, map, init_model) | |
128 | + def update_property(models, map, init_model) | |
105 | 129 | init_model.each do |key, val| |
106 | 130 | val.each do |v| |
107 | 131 | v[:relation].each do |col, rel| |
@@ -0,0 +1,40 @@ | ||
1 | +/* Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> */ | |
2 | + | |
3 | +* { | |
4 | + text-align: left; | |
5 | +} | |
6 | + | |
7 | +h2 { | |
8 | + background-color: #e1ecfe; | |
9 | + padding: 5px; | |
10 | +} | |
11 | + | |
12 | +#right h2 { | |
13 | + padding-left: 10px; | |
14 | +} | |
15 | + | |
16 | +.yui-layout-unit-top { | |
17 | + background-color: #c3d9ff; | |
18 | +} | |
19 | + | |
20 | +.yui-layout-unit-bottom { | |
21 | + background-color: #c3d9ff; | |
22 | +} | |
23 | + | |
24 | +.yui-layout-unit .yui-resize-handle-r .yui-layout-resize-knob { | |
25 | + background-color: #c3d9ff; | |
26 | + height: 100%; | |
27 | + left: 0px; | |
28 | + position: absolute; | |
29 | + top: 0px; | |
30 | + width: 5px; | |
31 | +} | |
32 | + | |
33 | +.yui-layout-unit .yui-resize-handle-l .yui-layout-resize-knob { | |
34 | + background-color: #c3d9ff; | |
35 | + height: 100%; | |
36 | + left: 0px; | |
37 | + position: absolute; | |
38 | + top: 0px; | |
39 | + width: 5px; | |
40 | +} |
@@ -0,0 +1,249 @@ | ||
1 | +/* Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> */ | |
2 | + | |
3 | +(function() { | |
4 | + var config = { | |
5 | + view: { | |
6 | + view: { | |
7 | + url: '/admin/view-json/', | |
8 | + }, | |
9 | + widget: { | |
10 | + url: '/admin/widget-json/' | |
11 | + } | |
12 | + } | |
13 | + } | |
14 | + | |
15 | + google.load('jquery', '1.2'); | |
16 | + google.setOnLoadCallback(function() { | |
17 | + (new YAHOO.util.YUILoader({ | |
18 | + require: ['layout', 'resize', 'treeview', 'tabview'], | |
19 | + onSuccess: function() { | |
20 | + var Dom = YAHOO.util.Dom; | |
21 | + var Event = YAHOO.util.Event; | |
22 | + | |
23 | + Event.onDOMReady(function() { | |
24 | + (new App(config)).render(); | |
25 | + }); | |
26 | + } | |
27 | + })).insert(); | |
28 | + }); | |
29 | + | |
30 | + /* | |
31 | + * App | |
32 | + */ | |
33 | + var App = function() { | |
34 | + this.init.apply(this, arguments); | |
35 | + } | |
36 | + | |
37 | + App.prototype = { | |
38 | + init: function(config) { | |
39 | + this.layout = new App.Layout(this); | |
40 | + this.client = new App.View(this, config.view); | |
41 | + }, | |
42 | + | |
43 | + render: function() { | |
44 | + this.layout.render(); | |
45 | + this.client.render(); | |
46 | + } | |
47 | + } | |
48 | + | |
49 | + /* | |
50 | + * App.Layout | |
51 | + */ | |
52 | + App.Layout = function() { | |
53 | + this.init.apply(this, arguments); | |
54 | + } | |
55 | + | |
56 | + App.Layout.prototype = { | |
57 | + init: function(app) { | |
58 | + this.app = app; | |
59 | + | |
60 | + this.units = { | |
61 | + top: { | |
62 | + position: 'top', | |
63 | + body: 'top', | |
64 | + height: 50 | |
65 | + }, | |
66 | + right: { | |
67 | + position: 'right', | |
68 | + body: 'right', | |
69 | + width: 200, | |
70 | + resize: true, | |
71 | + scroll: true | |
72 | + }, | |
73 | + bottom: { | |
74 | + position: 'bottom', | |
75 | + body: 'bottom', | |
76 | + height: 25 | |
77 | + }, | |
78 | + left: { | |
79 | + position: 'left', | |
80 | + body: 'left', | |
81 | + width: 200, | |
82 | + resize: true, | |
83 | + scroll: true | |
84 | + }, | |
85 | + center: { | |
86 | + position: 'center', | |
87 | + body: 'center' | |
88 | + } | |
89 | + }; | |
90 | + | |
91 | + this.layout = new YAHOO.widget.Layout({ | |
92 | + units: [ | |
93 | + this.units.top, | |
94 | + this.units.right, | |
95 | + this.units.bottom, | |
96 | + this.units.left, | |
97 | + this.units.center | |
98 | + ] | |
99 | + }); | |
100 | + }, | |
101 | + | |
102 | + render: function() { | |
103 | + this.layout.render(); | |
104 | + } | |
105 | + } | |
106 | + | |
107 | + /* | |
108 | + * App.View | |
109 | + */ | |
110 | + App.View = function() { | |
111 | + this.init.apply(this, arguments); | |
112 | + } | |
113 | + | |
114 | + App.View.prototype = { | |
115 | + init: function(app, config) { | |
116 | + this.app = app; | |
117 | + this.config = config; | |
118 | + | |
119 | + /* View Tree */ | |
120 | + $('#left').append('<h2>View</h2>'); | |
121 | + $('#left').append('<div id="view"><div id="view-tree"></div></div>'); | |
122 | + this.view_tree = new App.View.ViewTree( | |
123 | + this, | |
124 | + 'view-tree', | |
125 | + config.view.url | |
126 | + ); | |
127 | + | |
128 | + // to make view tree resizable | |
129 | + new YAHOO.util.Resize( | |
130 | + 'view', | |
131 | + { | |
132 | + handles: ['b'], | |
133 | + ratio: false, | |
134 | + setSize: false | |
135 | + } | |
136 | + ); | |
137 | + $('#view').css('overflow', 'auto'); | |
138 | + $('#view .yui-resize-handle-b').css('background-color', '#c3d9ff'); | |
139 | + | |
140 | + /* Widget Tree */ | |
141 | + $('#left').append('<h2>Widget</h2>'); | |
142 | + $('#left').append('<div id="widget-tree"></div>'); | |
143 | + this.widget_tree = new App.View.WidgetTree( | |
144 | + this, | |
145 | + 'widget-tree', | |
146 | + config.widget.url | |
147 | + ); | |
148 | + | |
149 | + /* Property Table */ | |
150 | + $('#right').append('<h2>Property</h2>'); | |
151 | + $('#right').append('<div id="property"></div>'); | |
152 | + | |
153 | + /* Style Table */ | |
154 | + $('#right').append('<h2>Style</h2>'); | |
155 | + $('#right').append('<div id="style"></div>'); | |
156 | + }, | |
157 | + | |
158 | + render: function() { | |
159 | + this.view_tree.render(); | |
160 | + this.widget_tree.render(); | |
161 | + }, | |
162 | + | |
163 | + on_click_view: function(id) { | |
164 | + this.widget_tree.update(id); | |
165 | + }, | |
166 | + | |
167 | + on_click_widget: function(id) { | |
168 | + alert(id); | |
169 | + } | |
170 | + } | |
171 | + | |
172 | + /* | |
173 | + * App.View.ViewTree | |
174 | + */ | |
175 | + App.View.ViewTree = function() { | |
176 | + this.init.apply(this, arguments); | |
177 | + } | |
178 | + | |
179 | + App.View.ViewTree.prototype = { | |
180 | + init: function(client, tag_id, url) { | |
181 | + this.client = client; | |
182 | + this.tag_id = tag_id; | |
183 | + this.url = url | |
184 | + | |
185 | + this.tree = new YAHOO.widget.TreeView(this.tag_id); | |
186 | + this.tree.subscribe('labelClick', function(node) { | |
187 | + client.on_click_view(node.data.id); | |
188 | + }); | |
189 | + this.tree.subscribe('expand', function(node) { return false; }); | |
190 | + this.tree.subscribe('collapse', function(node) { return false; }); | |
191 | + }, | |
192 | + | |
193 | + update: function() { | |
194 | + var tree = this.tree; | |
195 | + | |
196 | + $.ajax({ | |
197 | + dataType: 'json', | |
198 | + url: this.url, | |
199 | + success: function(data, textStatus) { | |
200 | + tree.removeChildren(tree.getRoot()); | |
201 | + tree.buildTreeFromObject(data); | |
202 | + tree.render(); | |
203 | + } | |
204 | + }); | |
205 | + }, | |
206 | + | |
207 | + render: function() { | |
208 | + this.update(); | |
209 | + } | |
210 | + } | |
211 | + | |
212 | + /* | |
213 | + * App.View.WidgetTree | |
214 | + */ | |
215 | + App.View.WidgetTree = function() { | |
216 | + this.init.apply(this, arguments); | |
217 | + } | |
218 | + | |
219 | + App.View.WidgetTree.prototype = { | |
220 | + init: function(client, tag_id, url) { | |
221 | + this.client = client; | |
222 | + this.tag_id = tag_id; | |
223 | + this.url = url; | |
224 | + | |
225 | + this.tree = new YAHOO.widget.TreeView(this.tag_id); | |
226 | + this.tree.subscribe('labelClick', function(node) { | |
227 | + client.on_click_widget(node.data.id); | |
228 | + }); | |
229 | + this.tree.subscribe('expand', function(node) { return false; }); | |
230 | + this.tree.subscribe('collapse', function(node) { return false; }); | |
231 | + }, | |
232 | + | |
233 | + update: function(view_id) { | |
234 | + var tree = this.tree; | |
235 | + | |
236 | + $.ajax({ | |
237 | + dataType: 'json', | |
238 | + url: this.url + view_id, | |
239 | + success: function(data, textStatus) { | |
240 | + tree.removeChildren(tree.getRoot()); | |
241 | + tree.buildTreeFromObject(data); | |
242 | + tree.render(); | |
243 | + } | |
244 | + }); | |
245 | + }, | |
246 | + | |
247 | + render: function() {} | |
248 | + } | |
249 | +})(); |
@@ -14,36 +14,49 @@ | ||
14 | 14 | :address => '0.0.0.0', |
15 | 15 | :port => 8080, |
16 | 16 | #:port => 8080..8085, |
17 | - :pub_dir => "#{BASE_DIR}/pub", | |
17 | + #:pub_dir => "#{BASE_DIR}/pub", | |
18 | 18 | :app_dir => "#{BASE_DIR}/webapp", |
19 | 19 | } |
20 | 20 | |
21 | -CONFIG[:model] = { | |
21 | +CONFIG[:models] = { | |
22 | 22 | :table => { |
23 | - 'service' => {:row => 'Service', :class => 'ServiceTable'}, | |
24 | - 'user' => {:row => 'User', :class => 'UserTable' }, | |
25 | - 'usersession' => {:row => 'Session', :class => 'SessionTable'}, | |
26 | - 'admin' => {:row => 'User', :class => 'UserTable' }, | |
27 | - 'adminsession' => {:row => 'Session', :class => 'SessionTable'}, | |
28 | - 'content' => {:row => 'Content'}, | |
29 | - 'view' => {:row => 'View' }, | |
30 | - 'widget' => {:row => 'Widget' }, | |
23 | + 'admin_account' => {:row => 'User', :class => 'UserTable' }, | |
24 | + 'admin_service' => {:row => 'Service', :class => 'ServiceTable'}, | |
25 | + 'admin_session' => {:row => 'Session', :class => 'SessionTable'}, | |
26 | + 'admin_view' => {:row => 'View', :class => 'ViewTable' }, | |
27 | + 'admin_widget' => {:row => 'Widget' }, | |
28 | + 'admin_content' => {:row => 'Content'}, | |
29 | + | |
30 | + 'user_account' => {:row => 'User', :class => 'UserTable' }, | |
31 | + 'user_service' => {:row => 'Service', :class => 'ServiceTable'}, | |
32 | + 'user_session' => {:row => 'Session', :class => 'SessionTable'}, | |
33 | + 'user_view' => {:row => 'View' }, | |
34 | + 'user_widget' => {:row => 'Widget' }, | |
35 | + 'user_content' => {:row => 'Content'}, | |
31 | 36 | }, |
32 | 37 | |
33 | 38 | :relation => [ |
34 | - ['service.default_view', 'view' ], | |
35 | - ['user.content', 'content' ], | |
36 | - ['user.view', 'view' ], | |
37 | - ['user.session', 'usersession.user' ], | |
38 | - ['admin.content', 'content' ], | |
39 | - ['admin.view', 'view' ], | |
40 | - ['admin.session', 'adminsession.user'], | |
41 | - ['content.parent', 'content.node' ], | |
42 | - ['content.view', 'view' ], | |
43 | - ['view.parent', 'view.node' ], | |
44 | - ['view.resource', 'widget' ], | |
45 | - ['widget.parent', 'widget.node' ], | |
46 | - ['widget.view', 'view.widget' ], | |
39 | + ['admin_account.content', 'admin_content' ], | |
40 | + ['admin_account.view', 'admin_view' ], | |
41 | + ['admin_account.session', 'admin_session.user'], | |
42 | + ['admin_service.default_view', 'admin_view' ], | |
43 | + ['admin_content.parent', 'admin_content.node'], | |
44 | + ['admin_content.view', 'admin_view' ], | |
45 | + ['admin_view.parent', 'admin_view.node' ], | |
46 | + ['admin_view.model', 'admin_widget' ], | |
47 | + ['admin_widget.parent', 'admin_widget.node' ], | |
48 | + ['admin_widget.view', 'admin_view.widget' ], | |
49 | + | |
50 | + ['user_account.content', 'user_content' ], | |
51 | + ['user_account.view', 'user_view' ], | |
52 | + ['user_account.session', 'user_session.user'], | |
53 | + ['user_service.default_view', 'user_view' ], | |
54 | + ['user_content.parent', 'user_content.node'], | |
55 | + ['user_content.view', 'user_view' ], | |
56 | + ['user_view.parent', 'user_view.node' ], | |
57 | + ['user_view.model', 'user_widget' ], | |
58 | + ['user_widget.parent', 'user_widget.node' ], | |
59 | + ['user_widget.view', 'user_view.widget' ], | |
47 | 60 | ], |
48 | 61 | } |
49 | 62 |
@@ -35,7 +35,7 @@ | ||
35 | 35 | column Tenarai::DB::Reference.new('default_view') |
36 | 36 | index Tenarai::DB::Index.new('name_idx', :cols => ['name'], :unique => true) |
37 | 37 | |
38 | - attr_reader :model | |
38 | + attr_reader :models | |
39 | 39 | attr_reader :request |
40 | 40 | attr_reader :response |
41 | 41 |
@@ -51,12 +51,12 @@ | ||
51 | 51 | build_uri(view, nil, query) |
52 | 52 | end |
53 | 53 | |
54 | - def dispatch(model, param, request, response) | |
55 | - @model = model | |
54 | + def dispatch(models, view, params, request, response) | |
55 | + @models = models | |
56 | 56 | @request = request |
57 | 57 | @response = response |
58 | - @view = @model['view'].fetch(param.shift) || default_view | |
59 | - @view.render(self, param) | |
58 | + @view = view.fetch_by_name(params.shift) || default_view | |
59 | + @view.render(self, params) | |
60 | 60 | rescue Service::Error => err |
61 | 61 | err.exec(self) |
62 | 62 | end |
@@ -1,42 +0,0 @@ | ||
1 | -# | |
2 | -# Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> | |
3 | -# | |
4 | - | |
5 | -Tenarai::ClassLoader.load_class('Widget') | |
6 | - | |
7 | -class Widget | |
8 | - class Resource < ::Widget | |
9 | - extend Tenarai::ClassLoader | |
10 | - | |
11 | - property :model | |
12 | - | |
13 | - attr_reader :content | |
14 | - attr_reader :model | |
15 | - | |
16 | - def init(builder, service, _id=nil) | |
17 | - super(builder, service) | |
18 | - | |
19 | - if property[:model].nil? | |
20 | - property[:model] = self.class.name.split('::').pop.downcase | |
21 | - end | |
22 | - | |
23 | - @model = @service.models[property[:model]] | |
24 | - @content = @model.fetch(_id) | |
25 | - end | |
26 | - | |
27 | - def uri(view, *params, &block) | |
28 | - query = Tenarai::CGI::Query.new | |
29 | - block.call(query) if block_given? | |
30 | - build_uri(view, params, query) | |
31 | - end | |
32 | - | |
33 | - def widget_uri(path, query, params=[]) | |
34 | - params = [@content.id] if params.empty? && @content | |
35 | - if view.resource.path.include?(self) | |
36 | - path << params.join(',') | |
37 | - else | |
38 | - query[html_id] = params.join(',') | |
39 | - end | |
40 | - end | |
41 | - end | |
42 | -end |
@@ -124,9 +124,9 @@ | ||
124 | 124 | var Event = YAHOO.util.Event; |
125 | 125 | |
126 | 126 | Event.onDOMReady(function() { |
127 | - new YAHOO.widget.Layout({ | |
127 | + (new YAHOO.widget.Layout({ | |
128 | 128 | units: [#{units}] |
129 | - }).render(); | |
129 | + })).render(); | |
130 | 130 | }); |
131 | 131 | })(); |
132 | 132 | END |
@@ -31,13 +31,13 @@ | ||
31 | 31 | head.code <<-END |
32 | 32 | (function() { |
33 | 33 | (new YAHOO.widget.TreeView('#{html_id}', [ |
34 | - {type:"text", label:"List 0", children: [ | |
35 | - {type:"text", label:"List 0-0", children: [ | |
34 | + {type:"text", label:"List 0", expanded: true, children: [ | |
35 | + {type:"text", label:"List 0-0", expanded: true, children: [ | |
36 | 36 | "item 0-0-0", |
37 | 37 | "item 0-0-1" |
38 | 38 | ]}, |
39 | - {type:"text", label:"item 0-1", children: [ | |
40 | - {type:"text", label:"elsewhere", href:"www.elsewhere.com", target:"_new", title:"go elsewhere", children: [ | |
39 | + {type:"text", label:"item 0-1", expanded: true, children: [ | |
40 | + {type:"text", label:"elsewhere", expanded: true, children: [ | |
41 | 41 | "item 0-1-0", |
42 | 42 | "item 0-1-1" |
43 | 43 | ]} |
@@ -0,0 +1,36 @@ | ||
1 | +# | |
2 | +# Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> | |
3 | +# | |
4 | + | |
5 | +Tenarai::ClassLoader.load_class('Widget::Widget') | |
6 | + | |
7 | +require 'simple-json' | |
8 | + | |
9 | +class Widget | |
10 | + class Widget | |
11 | + class JSON < ::Widget | |
12 | + extend Tenarai::ClassLoader | |
13 | + | |
14 | + def render | |
15 | + nodes = [] | |
16 | + map = {} | |
17 | + model.each do |val| | |
18 | + node = { | |
19 | + 'type' => 'text', | |
20 | + 'label' => val.class.name, | |
21 | + 'expanded' => true, | |
22 | + 'id' => val.id | |
23 | + } | |
24 | + map[val.id] = node | |
25 | + if val.parent.nil? | |
26 | + nodes << node | |
27 | + else | |
28 | + map[val.parent.id]['children'] ||= [] | |
29 | + map[val.parent.id]['children'] << node | |
30 | + end | |
31 | + end | |
32 | + @service.response.body << JsonBuilder.new.build(nodes) | |
33 | + end | |
34 | + end | |
35 | + end | |
36 | +end |
@@ -2,21 +2,21 @@ | ||
2 | 2 | # Copyright (C) 2007, 2008 Jun Kikuchi <kikuchi@bonnou.com> |
3 | 3 | # |
4 | 4 | |
5 | -Tenarai::ClassLoader.load_class('Widget::Resource') | |
5 | +Tenarai::ClassLoader.load_class('Widget::Model') | |
6 | 6 | |
7 | 7 | class Widget |
8 | - class Widget < Widget::Resource | |
8 | + class Widget < Widget::Model | |
9 | 9 | extend Tenarai::ClassLoader |
10 | 10 | |
11 | - resource 'Widget::View' | |
11 | + model 'Widget::View' | |
12 | 12 | |
13 | 13 | def init(builder, service, id=nil) |
14 | 14 | super |
15 | - @content ||= resource.content.widget.root | |
15 | + @content ||= model.content.widget.root | |
16 | 16 | end |
17 | 17 | |
18 | 18 | def each(&block) |
19 | - resource.content.widget.each(&block) | |
19 | + model.content.widget.each(&block) | |
20 | 20 | end |
21 | 21 | end |
22 | 22 | end |
@@ -0,0 +1,41 @@ | ||
1 | +# | |
2 | +# Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> | |
3 | +# | |
4 | + | |
5 | +Tenarai::ClassLoader.load_class('Widget') | |
6 | + | |
7 | +class Widget | |
8 | + class Model < ::Widget | |
9 | + extend Tenarai::ClassLoader | |
10 | + | |
11 | + property :model | |
12 | + | |
13 | + attr_reader :models | |
14 | + attr_reader :content | |
15 | + | |
16 | + def init(builder, service, _id=nil) | |
17 | + super(builder, service) | |
18 | + @models = service.models[property[:model]] | |
19 | + @content = if @models | |
20 | + @models.fetch(_id) | |
21 | + else | |
22 | + nil | |
23 | + end | |
24 | + end | |
25 | + | |
26 | + def uri(view, *params, &block) | |
27 | + query = Tenarai::CGI::Query.new | |
28 | + block.call(query) if block_given? | |
29 | + build_uri(view, params, query) | |
30 | + end | |
31 | + | |
32 | + def widget_uri(path, query, params=[]) | |
33 | + params = [@content.id] if params.empty? && @content | |
34 | + if view.resource.path.include?(self) | |
35 | + path << params.join(',') | |
36 | + else | |
37 | + query[html_id] = params.join(',') | |
38 | + end | |
39 | + end | |
40 | + end | |
41 | +end |
@@ -0,0 +1,38 @@ | ||
1 | +# | |
2 | +# Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> | |
3 | +# | |
4 | + | |
5 | +Tenarai::ClassLoader.load_class('Widget::View') | |
6 | + | |
7 | +require 'simple-json' | |
8 | + | |
9 | +class Widget | |
10 | + class View | |
11 | + class JSON < ::Widget | |
12 | + extend Tenarai::ClassLoader | |
13 | + | |
14 | + def render | |
15 | + nodes = [] | |
16 | + model.models.query('WHERE parent is NULL ORDER BY name') do |view| | |
17 | + nodes << build_tree(view) | |
18 | + end | |
19 | + @service.response.body << JsonBuilder.new.build(nodes) | |
20 | + end | |
21 | + | |
22 | + def build_tree(view) | |
23 | + node = { | |
24 | + 'type' => 'text', | |
25 | + 'label' => view.name, | |
26 | + 'expanded' => true, | |
27 | + 'id' => view.id | |
28 | + } | |
29 | + view.node.each do |val| | |
30 | + node['children'] ||= [] | |
31 | + node['children'] << build_tree(val) | |
32 | + end | |
33 | + node | |
34 | + end | |
35 | + private :build_tree | |
36 | + end | |
37 | + end | |
38 | +end |
@@ -2,10 +2,10 @@ | ||
2 | 2 | # Copyright (C) 2007, 2008 Jun Kikuchi <kikuchi@bonnou.com> |
3 | 3 | # |
4 | 4 | |
5 | -Tenarai::ClassLoader.load_class('Widget::Resource') | |
5 | +Tenarai::ClassLoader.load_class('Widget::Model') | |
6 | 6 | |
7 | 7 | class Widget |
8 | - class View < Widget::Resource | |
8 | + class View < Widget::Model | |
9 | 9 | extend Tenarai::ClassLoader |
10 | 10 | |
11 | 11 | def init(builder, service, id=nil) |
@@ -2,23 +2,21 @@ | ||
2 | 2 | # Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> |
3 | 3 | # |
4 | 4 | # |
5 | -Tenarai::ClassLoader.load_class('Widget::Widget::Google::AJAX') | |
5 | +Tenarai::ClassLoader.load_class('Widget::Google::AJAX') | |
6 | 6 | |
7 | 7 | class Widget |
8 | - class Widget | |
9 | - class Google | |
10 | - class AJAX | |
11 | - class Load | |
12 | - extend Tenarai::ClassLoader | |
8 | + class Google | |
9 | + class AJAX | |
10 | + class Load < ::Widget | |
11 | + extend Tenarai::ClassLoader | |
13 | 12 | |
14 | - property :name | |
15 | - property :version | |
13 | + property :name | |
14 | + property :version | |
16 | 15 | |
17 | - def render_html(head, body) | |
18 | - head.tag('script', 'type' => 'text/javascript').code( | |
19 | - "google.load('#{property[:name]}', '#{property[:version]}');" | |
20 | - ) | |
21 | - end | |
16 | + def render_html(head, body) | |
17 | + head.tag('script', 'type' => 'text/javascript').code( | |
18 | + "google.load('#{property[:name]}', '#{property[:version]}');" | |
19 | + ) | |
22 | 20 | end |
23 | 21 | end |
24 | 22 | end |
@@ -5,26 +5,25 @@ | ||
5 | 5 | Tenarai::ClassLoader.load_class('Widget') |
6 | 6 | |
7 | 7 | class Widget |
8 | - class Widget | |
9 | - class Google | |
10 | - class AJAX < ::Widget | |
11 | - extend Tenarai::ClassLoader | |
8 | + class Google | |
9 | + class AJAX < ::Widget | |
10 | + extend Tenarai::ClassLoader | |
12 | 11 | |
13 | - SRC = 'http://www.google.com/jsapi' | |
12 | + SRC = 'http://www.google.com/jsapi' | |
14 | 13 | |
15 | - property :key | |
14 | + property :key | |
16 | 15 | |
17 | - def render_html(head, body) | |
18 | - head.tag( | |
19 | - 'script', | |
20 | - 'type' => 'text/javascript', | |
21 | - 'src' => if property[:key].empty? | |
22 | - Widget::Widget::Jsapi::SRC | |
23 | - else | |
24 | - Widget::Widget::Jsapi::SRC + "?key=#{key}" | |
25 | - end | |
26 | - ).text('') | |
27 | - end | |
16 | + def render_html(head, body) | |
17 | + head.tag( | |
18 | + 'script', | |
19 | + 'type' => 'text/javascript', | |
20 | + 'src' => if property[:key].to_s.empty? | |
21 | + Widget::Google::AJAX::SRC | |
22 | + else | |
23 | + Widget::Google::AJAX::SRC + "?key=#{key}" | |
24 | + end | |
25 | + ).text('') | |
26 | + super | |
28 | 27 | end |
29 | 28 | end |
30 | 29 | end |
@@ -0,0 +1,19 @@ | ||
1 | +# | |
2 | +# Copyright (C) 2008 Jun Kikuchi <kikuchi@bonnou.com> | |
3 | +# | |
4 | + | |
5 | +class ViewTable < Tenarai::DB::Table | |
6 | + extend Tenarai::ClassLoader | |
7 | + | |
8 | + class << self | |
9 | + def create_row(class_name, table, row) | |
10 | + Tenarai::ClassLoader[class_name].new(table, row) | |
11 | + end | |
12 | + end | |
13 | + | |
14 | + def fetch_by_name(name) | |
15 | + query('WHERE name = ?', name) do |view| | |
16 | + return view | |
17 | + end | |
18 | + end | |
19 | +end |
@@ -9,7 +9,7 @@ | ||
9 | 9 | |
10 | 10 | meta[:property] = {} |
11 | 11 | meta[:css] = Tenarai::Container.new |
12 | - meta[:resource] = nil | |
12 | + meta[:model] = nil | |
13 | 13 | |
14 | 14 | class << self |
15 | 15 | def create_row(class_name, table, row) |
@@ -33,13 +33,13 @@ | ||
33 | 33 | css 'a:hover' |
34 | 34 | end |
35 | 35 | |
36 | - def resource(klass) | |
37 | - meta[:resource] = klass | |
36 | + def model(klass) | |
37 | + meta[:model] = klass | |
38 | 38 | end |
39 | 39 | |
40 | - def fetch_resource(widget) | |
40 | + def fetch_model(widget) | |
41 | 41 | widget.path.reverse.each do |val| |
42 | - return val if val.class.name == meta[:resource] | |
42 | + return val if val.class.name == meta[:model] | |
43 | 43 | end |
44 | 44 | nil |
45 | 45 | end |
@@ -46,8 +46,8 @@ | ||
46 | 46 | |
47 | 47 | def inherited(subclass) |
48 | 48 | super |
49 | - subclass.meta[:resource] = subclass.name.sub(/\w+?$/, '').sub(/::+?$/, '') | |
50 | - subclass.meta[:resource] = nil if subclass.meta[:resource].empty? | |
49 | + subclass.meta[:model] = subclass.name.sub(/\w+?$/, '').sub(/::+?$/, '') | |
50 | + subclass.meta[:model] = nil if subclass.meta[:model].empty? | |
51 | 51 | end |
52 | 52 | end |
53 | 53 |
@@ -54,12 +54,14 @@ | ||
54 | 54 | column Tenarai::DB::Reference.new('view') |
55 | 55 | column Tenarai::DB::Serialize.new('property') |
56 | 56 | column Tenarai::DB::Serialize.new('css') |
57 | - index Tenarai::DB::Index.new('build_idx', :cols => ['view', 'min']) | |
57 | + index Tenarai::DB::Index.new('build_idx', :cols => ['view', 'min', 'max']) | |
58 | 58 | |
59 | 59 | property :html_attr_id |
60 | 60 | property :html_attr_class |
61 | 61 | |
62 | - attr_reader :resource | |
62 | + attr_reader :builder | |
63 | + attr_reader :service | |
64 | + attr_reader :model | |
63 | 65 | |
64 | 66 | def initialize(table, row={}) |
65 | 67 | super |
@@ -67,26 +69,26 @@ | ||
67 | 69 | self.css ||= Tenarai::Container.new |
68 | 70 | end |
69 | 71 | |
70 | - def init(builder, service, *param) | |
71 | - @builder = builder | |
72 | - @service = service | |
73 | - @resource = self.class.fetch_resource(self) || @service | |
72 | + def init(builder, service, *params) | |
73 | + @builder = builder | |
74 | + @service = service | |
75 | + @model = self.class.fetch_model(self) || @service | |
74 | 76 | end |
75 | 77 | |
76 | 78 | def each_widget(&block) |
77 | - @builder.each(self, &block) | |
79 | + builder.each(self, &block) | |
78 | 80 | end |
79 | 81 | |
80 | - def uri(view, *param, &block) | |
81 | - resource.uri(view, *param, &block) | |
82 | + def uri(view, *params, &block) | |
83 | + model.uri(view, *params, &block) | |
82 | 84 | end |
83 | 85 | |
84 | - def build_uri(view, param, query) | |
85 | - @service.build_uri( | |
86 | + def build_uri(view, params, query) | |
87 | + service.build_uri( | |
86 | 88 | view, |
87 | 89 | path.inject([]) do |ret, val| |
88 | 90 | if val.equal?(self) |
89 | - val.widget_uri(ret, query, param) | |
91 | + val.widget_uri(ret, query, params) | |
90 | 92 | else |
91 | 93 | val.widget_uri(ret, query) |
92 | 94 | end |
@@ -96,7 +98,7 @@ | ||
96 | 98 | ) |
97 | 99 | end |
98 | 100 | |
99 | - def widget_uri(path, query, param=[]) | |
101 | + def widget_uri(path, query, params=[]) | |
100 | 102 | end |
101 | 103 | |
102 | 104 | def css_id(val=nil) |
@@ -162,9 +164,9 @@ | ||
162 | 164 | end |
163 | 165 | end |
164 | 166 | |
165 | - def get(param=Tenarai::CGI::Query.new) | |
167 | + def get(params=Tenarai::CGI::Query.new) | |
166 | 168 | end |
167 | 169 | |
168 | - def post(param=Tenari::CGI::Query.new) | |
170 | + def post(params=Tenari::CGI::Query.new) | |
169 | 171 | end |
170 | 172 | end |
@@ -5,57 +5,17 @@ | ||
5 | 5 | class ServiceTable < Tenarai::DB::Table |
6 | 6 | extend Tenarai::ClassLoader |
7 | 7 | |
8 | - MIME = { | |
9 | - '.html' => 'text/html', | |
10 | - '.htm' => 'text/html', | |
11 | - '.txt' => 'text/plain', | |
12 | - '.js' => 'text/javascript', | |
13 | - '.css' => 'text/css', | |
14 | - '.gif' => 'image/gif', | |
15 | - '.swf' => 'application/x-shockwave-flash', | |
16 | - } | |
17 | - | |
18 | - attr_accessor :pub_dir | |
19 | - | |
20 | - def fetch_by_name(name) | |
21 | - query('WHERE name = ?', name) do |service| | |
22 | - return service | |
8 | + class << self | |
9 | + def create_row(class_name, table, row) | |
10 | + Tenarai::ClassLoader[class_name].new(table, row) | |
23 | 11 | end |
24 | 12 | end |
25 | 13 | |
26 | - def dispatch(model, request, response) | |
27 | - path_info = if request['path_info'].nil? | |
28 | - '/' | |
29 | - else | |
30 | - if request['path_info'].empty? | |
31 | - request['script_name'] | |
32 | - else | |
33 | - request['path_info'] | |
34 | - end | |
35 | - end | |
36 | - | |
37 | - _nil, name, *param = path_info.split('/') | |
38 | - | |
39 | - if s = fetch_by_name(name || 'index') | |
14 | + def dispatch(name, view, models, params, request, response) | |
15 | + response.code = 404 | |
16 | + query('WHERE name = ?', name) do |service| | |
40 | 17 | response.code = 200 |
41 | - s.dispatch(model, param, request, response) | |
42 | - else | |
43 | - response.code = 404 | |
44 | - | |
45 | - return if @pub_dir.nil? | |
46 | - | |
47 | - dir = File.expand_path(@pub_dir) | |
48 | - file = File.expand_path(dir + path_info) | |
49 | - | |
50 | - return unless (%r!^#{dir}!.match(file) && File.file?(file)) | |
51 | - | |
52 | - content_type = MIME[File.extname(file).downcase] | |
53 | - | |
54 | - unless content_type.nil? | |
55 | - response.code = 200 | |
56 | - response['content-type'] = content_type | |
57 | - response.body << File.open(file).read | |
58 | - end | |
18 | + service.dispatch(models, view, params, request, response) | |
59 | 19 | end |
60 | 20 | end |
61 | 21 | end |
@@ -2,7 +2,7 @@ | ||
2 | 2 | # Copyright (C) 2007, 2008 Jun Kikuchi <kikuchi@bonnou.com> |
3 | 3 | # |
4 | 4 | |
5 | -Tenarai::ClassLoader.load_class('Widget::Resource') | |
5 | +Tenarai::ClassLoader.load_class('Widget::Model') | |
6 | 6 | |
7 | 7 | class View |
8 | 8 | class Builder |
@@ -49,7 +49,7 @@ | ||
49 | 49 | else |
50 | 50 | widget[nil] = val |
51 | 51 | end |
52 | - if val.is_a?(::Widget::Resource) && view.resource.path.include?(val) | |
52 | + if val.is_a?(::Widget::Model) && view.model.path.include?(val) | |
53 | 53 | val.init(self, service, param.shift) |
54 | 54 | else |
55 | 55 | if rs.key?(val) |
@@ -44,7 +44,7 @@ | ||
44 | 44 | end |
45 | 45 | |
46 | 46 | column Tenarai::DB::String.new('name', :length => 255) |
47 | - column Tenarai::DB::Reference.new('resource') | |
47 | + column Tenarai::DB::Reference.new('model') | |
48 | 48 | column Tenarai::DB::Reference.new( |
49 | 49 | 'widget', |
50 | 50 | :multiple => true, |
@@ -51,7 +51,7 @@ | ||
51 | 51 | :class => ::View::Widget |
52 | 52 | ) |
53 | 53 | |
54 | - def build(service, param) | |
54 | + def build(service, params) | |
55 | 55 | ws, rs = {}, {} |
56 | 56 | |
57 | 57 | if service.request.method == 'post' |
@@ -74,7 +74,7 @@ | ||
74 | 74 | |
75 | 75 | widget.each do |val| |
76 | 76 | if val.path.size == 1 |
77 | - val.init(service, *param.to_s.split(',').map do |val| | |
77 | + val.init(service, *params.to_s.split(',').map do |val| | |
78 | 78 | Tenarai::CGI.decode(val) |
79 | 79 | end) |
80 | 80 | else |
@@ -86,17 +86,17 @@ | ||
86 | 86 | self |
87 | 87 | end |
88 | 88 | |
89 | - def render(service, param=[]) | |
90 | - Tenarai::ClassLoader['View::Builder'].new(self, service, param).render | |
89 | + def render(service, params=[]) | |
90 | + Tenarai::ClassLoader['View::Builder'].new(self, service, params).render | |
91 | 91 | end |
92 | 92 | |
93 | - alias :_resource :resource | |
93 | + alias :_model :model | |
94 | 94 | |
95 | - def resource | |
96 | - _resource || parent.resource | |
95 | + def model | |
96 | + _model || parent.model | |
97 | 97 | end |
98 | 98 | |
99 | - def resource=(val) | |
100 | - _resource = val | |
99 | + def model=(val) | |
100 | + _model = val | |
101 | 101 | end |
102 | 102 | end |