rsence 2.1.7 → 2.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/INSTALL.rdoc +8 -4
- data/VERSION +1 -1
- data/js/controls/stringview/stringview.js +16 -2
- data/js/controls/textcontrol/textcontrol.js +69 -4
- data/js/core/elem/elem.js +6 -0
- data/js/foundation/control/eventresponder/eventresponder.js +27 -1
- data/js/foundation/eventmanager/eventmanager.js +34 -16
- data/js/foundation/view/morphanimation/morphanimation.js +9 -5
- data/js/foundation/view/view.js +28 -4
- data/js/lists/checkboxlist/checkboxlist.js +9 -0
- data/js/lists/listitems/listitems.js +16 -0
- data/js/lists/radiobuttonlist/radiobuttonlist.js +17 -1
- data/js/menus/minimenu/minimenu.js +9 -5
- data/lib/http/request.rb +6 -1
- data/lib/http/response.rb +3 -1
- data/plugins/client_pkg/lib/client_pkg_serve.rb +191 -157
- data/setup/welcome/gui/welcome.yaml +1 -1
- metadata +5 -5
data/INSTALL.rdoc
CHANGED
@@ -84,7 +84,7 @@ Replace the +\my_projects+ path with the path to the directory where you want to
|
|
84
84
|
* Using just defaults, the following URL should work: http://127.0.0.1:8001
|
85
85
|
|
86
86
|
==== Windows limitations
|
87
|
-
If you install the sqlite dll and the sqlite3
|
87
|
+
If you install the sqlite dll and the sqlite3 gem, you'll gain persistent sessions and this warning message will disappear:
|
88
88
|
`Warning: Session database is not available. Can't use persistent sessions`
|
89
89
|
|
90
90
|
It's not a dependency in the default install, because it's not strictly required and makes the first installation much easier. Also, you can use any other database supported by Sequel instead of Sqlite.
|
@@ -134,10 +134,10 @@ This not only enables SessionStorage (persistent sessions between RSence restart
|
|
134
134
|
* *jscompress*:: C-optimized Javascript compression and obfuscation library developed for RSence specifically
|
135
135
|
* *html_min*:: C-optimized HTML whitespace removal library developed for RSence specfically
|
136
136
|
* *cssmin*:: CSS whitespace removal library
|
137
|
-
* *sequel*[http://
|
137
|
+
* *sequel*[http://sequel.rubyforge.org/]
|
138
138
|
* Generic SQL database ORM
|
139
139
|
* A Sequel driver for your preferred database is also needed:
|
140
|
-
* *sqlite3
|
140
|
+
* *sqlite3*:: SQLite[http://www.sqlite.org] is a light-weight SQL library. Recommended for development and small projects.
|
141
141
|
* Other database adapters compatible with Sequel are fine. Just configure RSence accordingly.
|
142
142
|
* *highline*:: Console-based menu prompt system by the init command.
|
143
143
|
* rmagick
|
@@ -151,6 +151,7 @@ The primary installation method of RSence is via RubyGems.
|
|
151
151
|
|
152
152
|
To ensure your RubyGems is up-to-date, run:
|
153
153
|
sudo gem update --system
|
154
|
+
* gem update --system is disabled in Debian based systems, such as Ubuntu. It's okay to skip on these systems.
|
154
155
|
|
155
156
|
Even if RubyGems is up-to-date, ensure your installed gems are up-to-date, some of these are updated frequently. This will also update RSence release versions to the most recent version, if installed.
|
156
157
|
sudo gem update
|
@@ -160,8 +161,11 @@ Even if RubyGems is up-to-date, ensure your installed gems are up-to-date, some
|
|
160
161
|
This will install RSence via RubyGems, the preferred method. All dependencies are installed too, except for the ones you already might have installed.
|
161
162
|
gem install rsence
|
162
163
|
|
164
|
+
Debian/Ubuntu users need to include /var/lib/gems/1.8/bin into PATH either by adding it directly there or by making a symbolic link:
|
165
|
+
sudo ln -s /var/lib/gems/1.8/bin/rsence /usr/local/bin/rsence
|
166
|
+
|
163
167
|
Optionally, you might want to contribute to RSence development, just clone or fork the GIT repository on Github:
|
164
|
-
|
168
|
+
http://github.com/rsence/rsence
|
165
169
|
|
166
170
|
When installed, ensure it works by exploring the help of the 'rsence' command, like:
|
167
171
|
rsence
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.8
|
@@ -22,8 +22,21 @@ HStringView = HControl.extend({
|
|
22
22
|
|
23
23
|
componentName: "stringview",
|
24
24
|
|
25
|
+
defaultEvents: {
|
26
|
+
contextMenu: true
|
27
|
+
},
|
28
|
+
|
29
|
+
/** = Description
|
30
|
+
* HStringView allows the default contextMenu action.
|
31
|
+
*
|
32
|
+
**/
|
33
|
+
contextMenu: function(){
|
34
|
+
return true;
|
35
|
+
},
|
36
|
+
|
25
37
|
/** = Description
|
26
|
-
* setStyle
|
38
|
+
* The setStyle method of HStringView applies only to the value
|
39
|
+
* element (not the whole component).
|
27
40
|
*
|
28
41
|
**/
|
29
42
|
setStyle: function(_name, _value, _cacheOverride) {
|
@@ -35,7 +48,8 @@ HStringView = HControl.extend({
|
|
35
48
|
},
|
36
49
|
|
37
50
|
/** = Description
|
38
|
-
*
|
51
|
+
* The refreshLabel of HStringView sets a tool tip.
|
52
|
+
* Applied by the setLabel method and the label attribute of options.
|
39
53
|
*
|
40
54
|
**/
|
41
55
|
refreshLabel: function() {
|
@@ -22,7 +22,8 @@ HTextControl = HControl.extend({
|
|
22
22
|
componentName: "textcontrol",
|
23
23
|
|
24
24
|
defaultEvents: {
|
25
|
-
textEnter: true
|
25
|
+
textEnter: true,
|
26
|
+
contextMenu: true
|
26
27
|
},
|
27
28
|
|
28
29
|
controlDefaults: (HControlDefaults.extend({
|
@@ -30,6 +31,13 @@ HTextControl = HControl.extend({
|
|
30
31
|
refreshOnInput: true
|
31
32
|
})),
|
32
33
|
|
34
|
+
/** = Description
|
35
|
+
* The contextMenu event for text input components is not prevented by default.
|
36
|
+
**/
|
37
|
+
contextMenu: function(){
|
38
|
+
return true;
|
39
|
+
},
|
40
|
+
|
33
41
|
/** = Description
|
34
42
|
* The refreshLabel method sets the title property of the text
|
35
43
|
* field, essentially creating a tooltip using the label.
|
@@ -60,7 +68,50 @@ HTextControl = HControl.extend({
|
|
60
68
|
/** This flag is true, when the text input field has focus.
|
61
69
|
**/
|
62
70
|
hasTextFocus: false,
|
63
|
-
|
71
|
+
|
72
|
+
_getChangeEventFn: function(){
|
73
|
+
var _this = this;
|
74
|
+
return function(e){
|
75
|
+
if(_this._clipboardEventTimer){
|
76
|
+
clearTimeout( this._clipboardEventTimer );
|
77
|
+
}
|
78
|
+
_this._clipboardEventTimer = setTimeout( function(){_this.clipboardEvent();}, 200 );
|
79
|
+
return true;
|
80
|
+
};
|
81
|
+
},
|
82
|
+
|
83
|
+
_updateValueFromField: function(){
|
84
|
+
this.setValue(
|
85
|
+
this.validateText(
|
86
|
+
this.getTextFieldValue()
|
87
|
+
)
|
88
|
+
);
|
89
|
+
},
|
90
|
+
|
91
|
+
_clipboardEventTimer: null,
|
92
|
+
clipboardEvent: function(){
|
93
|
+
this._updateValueFromField();
|
94
|
+
clearTimeout( this._clipboardEventTimer );
|
95
|
+
this._clipboardEventTimer = null;
|
96
|
+
},
|
97
|
+
|
98
|
+
_changeEventFn: null,
|
99
|
+
_clearChangeEventFn: function(){
|
100
|
+
if( this._changeEventFn ){
|
101
|
+
Event.stopObserving( ELEM.get(this.markupElemIds.value), 'paste', this._changeEventFn );
|
102
|
+
Event.stopObserving( ELEM.get(this.markupElemIds.value), 'cut', this._changeEventFn );
|
103
|
+
this._changeEventFn = null;
|
104
|
+
}
|
105
|
+
},
|
106
|
+
_setChangeEventFn: function(){
|
107
|
+
if( this._changeEventFn ){
|
108
|
+
this._clearChangeEventFn();
|
109
|
+
}
|
110
|
+
this._changeEventFn = this._getChangeEventFn();
|
111
|
+
Event.observe( ELEM.get(this.markupElemIds.value), 'paste', this._changeEventFn );
|
112
|
+
Event.observe( ELEM.get(this.markupElemIds.value), 'cut', this._changeEventFn );
|
113
|
+
},
|
114
|
+
|
64
115
|
/** = Description
|
65
116
|
* Special event for text entry components.
|
66
117
|
* Called when the input field gains focus.
|
@@ -68,6 +119,7 @@ HTextControl = HControl.extend({
|
|
68
119
|
**/
|
69
120
|
textFocus: function(){
|
70
121
|
this.hasTextFocus = true;
|
122
|
+
this._setChangeEventFn();
|
71
123
|
return true;
|
72
124
|
},
|
73
125
|
|
@@ -78,12 +130,19 @@ HTextControl = HControl.extend({
|
|
78
130
|
**/
|
79
131
|
textBlur: function(){
|
80
132
|
this.hasTextFocus = false;
|
133
|
+
this._clearChangeEventFn();
|
134
|
+
this._updateValueFromField();
|
81
135
|
if(this.options.refreshOnBlur){
|
82
136
|
this.refreshValue();
|
83
137
|
}
|
84
138
|
return true;
|
85
139
|
},
|
86
|
-
|
140
|
+
|
141
|
+
onIdle: function(){
|
142
|
+
this.hasTextFocus && this._updateValueFromField();
|
143
|
+
this.base();
|
144
|
+
},
|
145
|
+
|
87
146
|
/** = Description
|
88
147
|
* refreshValue function
|
89
148
|
*
|
@@ -148,6 +207,11 @@ HTextControl = HControl.extend({
|
|
148
207
|
return '---'+Math.round((1+Math.random())*10000)+'---';
|
149
208
|
},
|
150
209
|
|
210
|
+
die: function(){
|
211
|
+
this._clearChangeEventFn();
|
212
|
+
this.base();
|
213
|
+
},
|
214
|
+
|
151
215
|
/** = Description
|
152
216
|
* Returns the selection (or text cursor position) of the input element
|
153
217
|
* as an +Array+ like +[ startOffset, endOffset ]+.
|
@@ -283,7 +347,8 @@ HNumericTextControl = HTextControl.extend({
|
|
283
347
|
|
284
348
|
defaultEvents: {
|
285
349
|
mouseWheel: true,
|
286
|
-
textEnter: true
|
350
|
+
textEnter: true,
|
351
|
+
contextMenu: true
|
287
352
|
},
|
288
353
|
|
289
354
|
/** Uses the mouseWheel event to step up/down the value.
|
data/js/core/elem/elem.js
CHANGED
@@ -1016,6 +1016,9 @@ ELEM = {
|
|
1016
1016
|
if (_key === 'opacity') {
|
1017
1017
|
_this.setOpacity(_id, _value);
|
1018
1018
|
}
|
1019
|
+
else if ( !_elems[_id] ){
|
1020
|
+
console.log('ELEM#setStyle: Not a element! id:',_id,', key:',_key,', value:', _value);
|
1021
|
+
}
|
1019
1022
|
else {
|
1020
1023
|
_this._setElementStyle( _elems[_id], _key, _cached[_key] );
|
1021
1024
|
}
|
@@ -1203,6 +1206,9 @@ ELEM = {
|
|
1203
1206
|
if (_key === 'opacity') {
|
1204
1207
|
_this.setOpacity(_id, _cached[_key]);
|
1205
1208
|
}
|
1209
|
+
else if ( !_elem ){
|
1210
|
+
console.log('ELEM#_flushStyleCache: Not a element! id:',_id,', key:',_key,', value:', _value);
|
1211
|
+
}
|
1206
1212
|
else {
|
1207
1213
|
_this._setElementStyle( _elem, _key, _cached[_key] );
|
1208
1214
|
}
|
@@ -153,7 +153,8 @@ HEventResponder = HClass.extend({
|
|
153
153
|
textEnter: false,
|
154
154
|
click: false,
|
155
155
|
resize: false,
|
156
|
-
doubleClick: false
|
156
|
+
doubleClick: false,
|
157
|
+
contextMenu: false
|
157
158
|
} ).extend( this.defaultEvents ).extend( _events?_events:{} ).nu();
|
158
159
|
}
|
159
160
|
this.events.ctrl = this;
|
@@ -284,6 +285,24 @@ HEventResponder = HClass.extend({
|
|
284
285
|
return this;
|
285
286
|
},
|
286
287
|
|
288
|
+
/** = Description
|
289
|
+
* Registers or releases event listening for contextMenu events depending on
|
290
|
+
* the value of the flag argument.
|
291
|
+
*
|
292
|
+
* = Parameters
|
293
|
+
* +_flag+:: Set the contextMenu event listening on/off (true/false) for
|
294
|
+
* the component instance.
|
295
|
+
*
|
296
|
+
* = Returns
|
297
|
+
* +self+
|
298
|
+
*
|
299
|
+
**/
|
300
|
+
setContextMenu: function(_flag) {
|
301
|
+
this.events.contextMenu = _flag;
|
302
|
+
this.setEvents();
|
303
|
+
return this;
|
304
|
+
},
|
305
|
+
|
287
306
|
/** = Description
|
288
307
|
* Registers or releases event listening for mouseUp events depending on the
|
289
308
|
* value of the flag argument.
|
@@ -537,6 +556,13 @@ HEventResponder = HClass.extend({
|
|
537
556
|
**/
|
538
557
|
doubleClick: function(x,y,_isLeftButton){},
|
539
558
|
|
559
|
+
/** = Description
|
560
|
+
* Default contextMenu event responder method. Does nothing by default.
|
561
|
+
* Extend to return true to allow the default action of the browser.
|
562
|
+
*
|
563
|
+
**/
|
564
|
+
contextMenu: function(x,y,_isLeftButton){},
|
565
|
+
|
540
566
|
/** = Description
|
541
567
|
* Default mouseDown event responder method. Does nothing by default.
|
542
568
|
*
|
@@ -28,7 +28,8 @@ EVENT = {
|
|
28
28
|
mouseWheel: false,
|
29
29
|
resize: false,
|
30
30
|
textEnter: false,
|
31
|
-
doubleClick: false
|
31
|
+
doubleClick: false,
|
32
|
+
contextMenu: false
|
32
33
|
},
|
33
34
|
|
34
35
|
/** = Description
|
@@ -841,14 +842,13 @@ EVENT = {
|
|
841
842
|
**/
|
842
843
|
doubleClick: function(e) {
|
843
844
|
var _this = EVENT,
|
844
|
-
_didEndDrag = false,
|
845
845
|
x = _this.status[_this.crsrX],
|
846
846
|
y = _this.status[_this.crsrY],
|
847
847
|
_elemId,
|
848
848
|
_ctrl,
|
849
849
|
i = 0;
|
850
850
|
_this._modifiers(e);
|
851
|
-
// Check for
|
851
|
+
// Check for doubleClick listeners.
|
852
852
|
for (i = 0; i !== _this.focused.length; i++) {
|
853
853
|
if (_this.focused[i] === true) {
|
854
854
|
if (_this.focusOptions[i].doubleClick === true) {
|
@@ -890,20 +890,38 @@ EVENT = {
|
|
890
890
|
}
|
891
891
|
},
|
892
892
|
|
893
|
-
|
893
|
+
/** Mid-level context menu manager.
|
894
|
+
* Gets called on the onContextMenu event.
|
895
|
+
* Delegates the following call to the high-level event receivers of all
|
896
|
+
* enabled controls registered, depending on the events they registered:
|
897
|
+
* - contextMenu
|
898
|
+
*
|
899
|
+
* Just make a component return true to allow the browser's default action.
|
900
|
+
*
|
901
|
+
**/
|
894
902
|
contextMenu: function(e) {
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
903
|
+
var _this = EVENT,
|
904
|
+
x = _this.status[_this.crsrX],
|
905
|
+
y = _this.status[_this.crsrY],
|
906
|
+
_preventDefault = true,
|
907
|
+
_elemId,
|
908
|
+
_ctrl,
|
909
|
+
i = 0;
|
910
|
+
_this._modifiers(e);
|
911
|
+
// Check for contextMenu listeners.
|
912
|
+
for (i = 0; i !== _this.focused.length; i++) {
|
913
|
+
if (_this.focused[i] === true) {
|
914
|
+
if (_this.focusOptions[i].contextMenu === true) {
|
915
|
+
if( _this.focusOptions[i].ctrl.contextMenu() ){
|
916
|
+
_preventDefault = false;
|
917
|
+
}
|
918
|
+
}
|
919
|
+
}
|
920
|
+
}
|
921
|
+
if( _preventDefault ){
|
922
|
+
Event.stop( e );
|
923
|
+
}
|
924
|
+
return true;
|
907
925
|
},
|
908
926
|
|
909
927
|
|
@@ -168,7 +168,9 @@ HMorphAnimation = HClass.extend({
|
|
168
168
|
// --Moves the view for one step. This gets called repeatedly when the animation++
|
169
169
|
// --is happening.++
|
170
170
|
_animateStep: function(_obj) {
|
171
|
-
var
|
171
|
+
var
|
172
|
+
_time = new Date().getTime(),
|
173
|
+
i;
|
172
174
|
if (_time < _obj.startTime + _obj.duration) {
|
173
175
|
var _cTime = _time - _obj.startTime;
|
174
176
|
|
@@ -189,14 +191,16 @@ HMorphAnimation = HClass.extend({
|
|
189
191
|
if(_unit){
|
190
192
|
_propNow += _unit;
|
191
193
|
}
|
192
|
-
ELEM.setStyle(this.elemId,_key, _propNow);
|
194
|
+
ELEM.setStyle( this.elemId, _key, _propNow );
|
193
195
|
}
|
194
196
|
}
|
195
|
-
|
196
|
-
|
197
|
+
}
|
198
|
+
else {
|
197
199
|
// Animation is done, clear the interval and finalize the animation.
|
198
200
|
for (i = 0; i < _obj.props.length; i++) {
|
199
|
-
ELEM.setStyle(
|
201
|
+
ELEM.setStyle(
|
202
|
+
this.elemId,
|
203
|
+
_obj.props[i].prop,
|
200
204
|
_obj.props[i].to + _obj.props[i].unit);
|
201
205
|
}
|
202
206
|
this._animationDone = true;
|
data/js/foundation/view/view.js
CHANGED
@@ -1047,6 +1047,20 @@ HView = HClass.extend({
|
|
1047
1047
|
},
|
1048
1048
|
|
1049
1049
|
setStyles: function(_styles){
|
1050
|
+
var _stylesObjType = COMM.Values.type(_styles);
|
1051
|
+
if(_stylesObjType==='a'){
|
1052
|
+
this.setStylesArray(_styles);
|
1053
|
+
}
|
1054
|
+
else if(_stylesObjType==='h'){
|
1055
|
+
this.setStylesHash(_styles);
|
1056
|
+
}
|
1057
|
+
else {
|
1058
|
+
console.log('HView#setStyles: Invalid data, expected array or hash; type: '+h+', data:',_styles);
|
1059
|
+
}
|
1060
|
+
return this;
|
1061
|
+
},
|
1062
|
+
|
1063
|
+
setStylesArray: function(_styles){
|
1050
1064
|
var
|
1051
1065
|
_styleItem, _styleKey, _styleValue, i = 0;
|
1052
1066
|
for(;i<_styles.length;i++){
|
@@ -1058,6 +1072,16 @@ HView = HClass.extend({
|
|
1058
1072
|
return this;
|
1059
1073
|
},
|
1060
1074
|
|
1075
|
+
setStylesHash: function(_styles){
|
1076
|
+
var
|
1077
|
+
_styleKey, _styleValue;
|
1078
|
+
for(_styleKey in _styles){
|
1079
|
+
_styleValue = _styles[_styleKey];
|
1080
|
+
this.setStyle(_styleKey,_styleValue);
|
1081
|
+
}
|
1082
|
+
return this;
|
1083
|
+
},
|
1084
|
+
|
1061
1085
|
/** = Description
|
1062
1086
|
* Returns a style of the main DOM element of the component.
|
1063
1087
|
* Utilizes +ELEM+ cache to perform the action.
|
@@ -1252,6 +1276,9 @@ HView = HClass.extend({
|
|
1252
1276
|
this.stopAnimation();
|
1253
1277
|
// Delete the children first.
|
1254
1278
|
var _childViewId, i;
|
1279
|
+
if(!this.views){
|
1280
|
+
console.log('HView#die: no subviews for component name: ',this.componentName,', self:',this);
|
1281
|
+
}
|
1255
1282
|
while (this.views.length !== 0) {
|
1256
1283
|
_childViewId = this.views[0];
|
1257
1284
|
this.destroyView(_childViewId);
|
@@ -1365,10 +1392,7 @@ HView = HClass.extend({
|
|
1365
1392
|
// Could be cached.
|
1366
1393
|
var _bounds = new HRect(this.rect);
|
1367
1394
|
|
1368
|
-
_bounds.
|
1369
|
-
_bounds.left = 0;
|
1370
|
-
_bounds.bottom -= _bounds.top;
|
1371
|
-
_bounds.top = 0;
|
1395
|
+
_bounds.offsetTo(0,0);
|
1372
1396
|
|
1373
1397
|
return _bounds;
|
1374
1398
|
},
|
@@ -129,12 +129,21 @@ HCheckboxList = HControl.extend({
|
|
129
129
|
this.refreshValue();
|
130
130
|
},
|
131
131
|
|
132
|
+
_listItemResponder: null,
|
133
|
+
setListItemResponder: function(_listItemResponder){
|
134
|
+
this._listItemResponder = _listItemResponder;
|
135
|
+
},
|
136
|
+
|
132
137
|
/** = Description
|
133
138
|
* Sets listItems and ListItemViews to null and calls
|
134
139
|
* the inherited destructor.
|
135
140
|
*
|
136
141
|
**/
|
137
142
|
die: function(){
|
143
|
+
if(this._listItemResponder){
|
144
|
+
this._listItemResponder.die();
|
145
|
+
this._listItemResponder = null;
|
146
|
+
}
|
138
147
|
this.listItems = null;
|
139
148
|
this.listItemViews = null;
|
140
149
|
this.base();
|
@@ -28,6 +28,12 @@ HListItems = HValueResponder.extend({
|
|
28
28
|
|
29
29
|
constructor: function( _rect, _parent, _options ){
|
30
30
|
this.parent = _parent;
|
31
|
+
if ( this.parent.setListItemResponder ){
|
32
|
+
this.parent.setListItemResponder( this );
|
33
|
+
}
|
34
|
+
else {
|
35
|
+
console.log('Warning; parent does not respond to setListItemResponder');
|
36
|
+
}
|
31
37
|
if (_options instanceof Object) {
|
32
38
|
if (_options['valueObj'] !== undefined) {
|
33
39
|
_options.valueObj.bind( this );
|
@@ -38,6 +44,16 @@ HListItems = HValueResponder.extend({
|
|
38
44
|
}
|
39
45
|
}
|
40
46
|
},
|
47
|
+
|
48
|
+
die: function() {
|
49
|
+
var _this = this;
|
50
|
+
if(_this.valueObj){
|
51
|
+
_this.valueObj.unbind(_this);
|
52
|
+
_this.valueObj = null;
|
53
|
+
}
|
54
|
+
_this.value = null;
|
55
|
+
},
|
56
|
+
|
41
57
|
_warningMessage: function(_messageText){
|
42
58
|
console.log("Warning; HListItems: "+_messageText);
|
43
59
|
},
|
@@ -66,13 +66,26 @@ HRadioButtonList = HControl.extend({
|
|
66
66
|
);
|
67
67
|
},
|
68
68
|
|
69
|
+
_listItemResponder: null,
|
70
|
+
setListItemResponder: function(_listItemResponder){
|
71
|
+
this._listItemResponder = _listItemResponder;
|
72
|
+
},
|
73
|
+
|
69
74
|
/** = Description
|
70
75
|
* Destructor. Sets listItems and listItemViews to null and initiates
|
71
76
|
* destructor for radioButtonIndexValue.
|
72
77
|
*
|
73
78
|
**/
|
74
79
|
die: function(){
|
80
|
+
if(this._listItemResponder){
|
81
|
+
this._listItemResponder.die();
|
82
|
+
this._listItemResponder = null;
|
83
|
+
}
|
84
|
+
this.radioButtonIndexValue && this.radioButtonIndexValue.die();
|
75
85
|
this.listItems = null;
|
86
|
+
for(var i=0;i<this.listItemViews.length;i++){
|
87
|
+
this.listItemViews[i].die();
|
88
|
+
}
|
76
89
|
this.listItemViews = null;
|
77
90
|
this.radioButtonIndexValue && this.radioButtonIndexValue.die();
|
78
91
|
this.base();
|
@@ -90,6 +103,9 @@ HRadioButtonList = HControl.extend({
|
|
90
103
|
},
|
91
104
|
refresh: function(){
|
92
105
|
var _listItems = this.parent.listItems;
|
106
|
+
if(_listItems === undefined || _listItems === null){
|
107
|
+
return;
|
108
|
+
}
|
93
109
|
if(_listItems[ this.value ] !== undefined){
|
94
110
|
this.parent.setValue( _listItems[ this.value ][0] );
|
95
111
|
}
|
@@ -100,7 +116,7 @@ HRadioButtonList = HControl.extend({
|
|
100
116
|
|
101
117
|
refreshValue: function(){
|
102
118
|
var _value = this.value;
|
103
|
-
if ( this.listItems.length !== 0 && this['valueMatrix'] !== undefined ) {
|
119
|
+
if ( this.listItems && this.listItems.length !== 0 && this['valueMatrix'] !== undefined ) {
|
104
120
|
if ( this.radioButtonResponder === false ){
|
105
121
|
this.radioButtonIndexValue = HValue.nu( false, 0 );
|
106
122
|
this.radioButtonIndexValue.bind( this.valueMatrix );
|
@@ -56,10 +56,12 @@ HMiniMenu = HRadioButtonList.extend({
|
|
56
56
|
|
57
57
|
refreshValue: function(){
|
58
58
|
this.base();
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
if(this.listItems && this.listItems.length !== 0 && this['valueMatrix'] !== undefined ) {
|
60
|
+
for(var i=0;i<this.listItems.length;i++){
|
61
|
+
if(this.listItems[i][0]===this.value){
|
62
|
+
this.setLabel( this.listItems[i][1] );
|
63
|
+
return;
|
64
|
+
}
|
63
65
|
}
|
64
66
|
}
|
65
67
|
},
|
@@ -97,8 +99,10 @@ HMiniMenu = HRadioButtonList.extend({
|
|
97
99
|
},
|
98
100
|
|
99
101
|
die: function(){
|
100
|
-
this.
|
102
|
+
this.valueMatrix = null;
|
103
|
+
var _menuItemView = this.menuItemView;
|
101
104
|
this.base();
|
105
|
+
_menuItemView.die();
|
102
106
|
},
|
103
107
|
|
104
108
|
drawSubviews: function(){
|
data/lib/http/request.rb
CHANGED
@@ -17,8 +17,13 @@ module RSence
|
|
17
17
|
# than the Rack::Request it's extending.
|
18
18
|
class Request < Rack::Request
|
19
19
|
attr_reader :header, :path, :query
|
20
|
+
class RequestHeader < Hash
|
21
|
+
def [](key)
|
22
|
+
super(key.downcase)
|
23
|
+
end
|
24
|
+
end
|
20
25
|
def initialize(env)
|
21
|
-
@header =
|
26
|
+
@header = RequestHeader.new
|
22
27
|
super
|
23
28
|
env2header()
|
24
29
|
@path = path_info()
|
data/lib/http/response.rb
CHANGED
@@ -17,13 +17,15 @@ module RSence
|
|
17
17
|
# Adds the + method "operator" to an extended Array.
|
18
18
|
# Used for pushing http body data.
|
19
19
|
class ResponseBody < Array
|
20
|
+
|
20
21
|
def push( body_data )
|
21
22
|
super( sanitize_body_data( body_data ) )
|
22
23
|
end
|
23
24
|
def sanitize_body_data( body_data )
|
24
|
-
if body_data.class == String
|
25
|
+
if body_data.class == String or body_data.class == GZString
|
25
26
|
return body_data
|
26
27
|
elsif body_data.respond_to?(:to_s)
|
28
|
+
warn "WARNING: RSence::Response::ResponseBody -> body_data is not a string. It's a #{body_data.class}, with the following contents: #{body_data.inspect}" if RSence.args[:verbose]
|
27
29
|
return body_data.to_s
|
28
30
|
else
|
29
31
|
warn "Invalid response data: #{body_data.inspect[0..100]}"
|
@@ -21,204 +21,238 @@ module ClientPkgServe
|
|
21
21
|
return time.gmtime.strftime('%a, %d %b %Y %H:%M:%S %Z')
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
def get( request, response, session )
|
26
|
-
|
24
|
+
def build_busy_wait
|
27
25
|
while @build_busy
|
28
26
|
puts "-- build not finished, waiting.. --"
|
29
27
|
sleep 0.1
|
30
28
|
end
|
31
|
-
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_headers( response )
|
32
32
|
# Sets the response date header to the current time:
|
33
33
|
response['Date'] = httime( Time.now )
|
34
34
|
|
35
35
|
# Controls caching with headers based on the configuration
|
36
36
|
if ::RSence.config[:cache_maximize]
|
37
37
|
response['Expires'] = httime(Time.now+::RSence.config[:cache_expire])
|
38
|
-
|
39
38
|
else
|
40
39
|
response['Cache-Control'] = 'no-cache'
|
41
40
|
end
|
42
|
-
|
43
|
-
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_ua( request )
|
44
|
+
support_gzip = (request.header.has_key?('Accept-Encoding') and request.header['Accept-Encoding'].include?('gzip'))
|
44
45
|
support_gzip = false if ::RSence.config[:no_gzip]
|
45
|
-
if request.header.has_key?('
|
46
|
-
ua = request.header['
|
46
|
+
if request.header.has_key?('User-Agent')
|
47
|
+
ua = request.header['User-Agent']
|
47
48
|
is_symbian = ua.include?("SymbianOS")
|
48
49
|
is_safari = ((not is_symbian) and ua.include?("WebKit"))
|
49
50
|
is_msie = ((not ua.include?("Opera")) and ua.include?("MSIE"))
|
50
51
|
is_msie6 = ((not ua.include?("Opera")) and ua.include?("MSIE 6.0"))
|
51
52
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
request_path = request_uri.split( '/' )
|
57
|
-
|
58
|
-
## Requested type of client resource (js/themes)
|
59
|
-
req_type = request_path[2]
|
60
|
-
|
61
|
-
if not ['js','themes'].include? req_type
|
62
|
-
req_rev = req_type
|
63
|
-
req_type = request_path[3]
|
64
|
-
request_path.delete_at(2)
|
53
|
+
if is_safari
|
54
|
+
version = ua.split( 'WebKit/' )[1].split('.')[0].to_i
|
55
|
+
else
|
56
|
+
version = 0 # not used for others
|
65
57
|
end
|
58
|
+
return {
|
59
|
+
:symbian => is_symbian,
|
60
|
+
:safari => is_safari,
|
61
|
+
:msie => is_msie,
|
62
|
+
:msie6 => is_msie6,
|
63
|
+
:version => version
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def support_gzip?( ua )
|
68
|
+
doesnt_support = ( ua[:msie6] or ua[:symbian] or (ua[:safari] and ua[:version] < 533) )
|
69
|
+
return ( not doesnt_support )
|
70
|
+
end
|
71
|
+
|
72
|
+
def serve_htc( request, response, request_path )
|
73
|
+
req_file = request_path[3]
|
66
74
|
|
67
|
-
|
68
|
-
|
69
|
-
|
75
|
+
# this file is a part of iefix, it injects calls to
|
76
|
+
# the ie rendering engine to override stupid behavior
|
77
|
+
# when changing element properties
|
78
|
+
if request_path.include?('ie_css_element.htc')
|
79
|
+
response.status = 200
|
80
|
+
response['Content-Type'] = 'text/x-component'
|
81
|
+
## Usually, we don't need it, because the client framework does calls when needed
|
82
|
+
response.body = %{<PUBLIC:COMPONENT lightWeight="true"></PUBLIC:COMPONENT>}
|
70
83
|
|
71
|
-
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
## Enable it to call iefix automatically whenever element properties are changed
|
81
|
-
#response.body = %{<PUBLIC:COMPONENT lightWeight="true">\r\n<script type="text/javascript">\r\ntry{element.attachEvent("onpropertychange",iefix.htcElementEntry);}catch(e){}\r\n</script>\r\n</PUBLIC:COMPONENT>}
|
84
|
+
## Enable it to call iefix automatically whenever element properties are changed
|
85
|
+
#response.body = %{<PUBLIC:COMPONENT lightWeight="true">\r\n<script type="text/javascript">\r\ntry{element.attachEvent("onpropertychange",iefix.htcElementEntry);}catch(e){}\r\n</script>\r\n</PUBLIC:COMPONENT>}
|
86
|
+
|
87
|
+
# this file is a part of iefix, it injects calls to
|
88
|
+
# the ie rendering engine to override stupid behavior
|
89
|
+
# when changing style properties
|
90
|
+
elsif request_path.include?('ie_css_style.htc')
|
91
|
+
response.status = 200
|
92
|
+
response['Content-Type'] = 'text/x-component'
|
82
93
|
|
83
|
-
|
84
|
-
|
85
|
-
# when changing style properties
|
86
|
-
elsif request_path.include?('ie_css_style.htc')
|
87
|
-
response.status = 200
|
88
|
-
response['Content-Type'] = 'text/x-component'
|
89
|
-
|
90
|
-
## Usually, we don't need it, because the client framework does calls when needed
|
91
|
-
response.body = %{<PUBLIC:COMPONENT lightWeight="true"></PUBLIC:COMPONENT>}
|
92
|
-
|
93
|
-
## Enable it to call iefix automatically whenever element properties are changed
|
94
|
-
#response.body = %{<PUBLIC:COMPONENT lightWeight="true">\r\n<script type="text/javascript">\r\ntry{element.attachEvent("onreadystatechange",iefix.htcStyleEntry);}catch(e){}\r\n</script>\r\n</PUBLIC:COMPONENT>}
|
94
|
+
## Usually, we don't need it, because the client framework does calls when needed
|
95
|
+
response.body = %{<PUBLIC:COMPONENT lightWeight="true"></PUBLIC:COMPONENT>}
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
97
|
+
## Enable it to call iefix automatically whenever element properties are changed
|
98
|
+
#response.body = %{<PUBLIC:COMPONENT lightWeight="true">\r\n<script type="text/javascript">\r\ntry{element.attachEvent("onreadystatechange",iefix.htcStyleEntry);}catch(e){}\r\n</script>\r\n</PUBLIC:COMPONENT>}
|
99
|
+
|
100
|
+
# Other htc requests are invalid
|
101
|
+
else
|
102
|
+
response.status = 503
|
103
|
+
response.body = '503 - Invalid Request'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def serve_js( request, response, request_path, ua )
|
108
|
+
# the file-specific identifier ('core', 'basic' etc)
|
109
|
+
req_file = request_path[3][0..-4]
|
110
|
+
|
111
|
+
if not @client_cache.js_cache.has_key?( req_file )
|
112
|
+
response.status = 404
|
113
|
+
response.body = '/* 404 - Not Found */'
|
114
|
+
else
|
104
115
|
|
116
|
+
response.status = 200
|
117
|
+
response['Content-Type'] = 'text/javascript; charset=utf-8'
|
105
118
|
|
106
|
-
#
|
107
|
-
|
119
|
+
# these browsers have issues with gzipped js content
|
120
|
+
support_gzip = support_gzip?( ua )
|
108
121
|
|
109
|
-
if
|
110
|
-
response
|
111
|
-
response
|
122
|
+
if support_gzip
|
123
|
+
#response['Transfer-Encoding'] = 'chunked,gzip'
|
124
|
+
response['Last-Modified'] = @client_cache.last_modified
|
125
|
+
body = @client_cache.gz_cache[ req_file ]+"\r\n\r\n"
|
126
|
+
response['Content-Length'] = body.bytesize.to_s
|
127
|
+
response['Content-Encoding'] = 'gzip'
|
128
|
+
response.body = body
|
112
129
|
else
|
113
130
|
|
114
|
-
response
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
support_gzip = false if (is_safari or is_msie or is_symbian)
|
131
|
+
response['Last-Modified'] = @client_cache.last_modified
|
132
|
+
body = @client_cache.js_cache[ req_file ]
|
133
|
+
response['Content-Length'] = body.bytesize.to_s
|
134
|
+
response.body = body
|
119
135
|
|
120
|
-
if support_gzip
|
121
|
-
#response['Transfer-Encoding'] = 'chunked,gzip'
|
122
|
-
response['Last-Modified'] = @client_cache.last_modified
|
123
|
-
body = @client_cache.gz_cache[ req_file ]+"\r\n\r\n"
|
124
|
-
response['Content-Length'] = body.bytesize.to_s
|
125
|
-
response['Content-Encoding'] = 'gzip'
|
126
|
-
response.body = body
|
127
|
-
else
|
128
|
-
|
129
|
-
response['Last-Modified'] = @client_cache.last_modified
|
130
|
-
body = @client_cache.js_cache[ req_file ]
|
131
|
-
response['Content-Length'] = body.bytesize.to_s
|
132
|
-
response.body = body
|
133
|
-
|
134
|
-
end
|
135
136
|
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def serve_theme( request, response, request_path, ua )
|
141
|
+
# Get the name of the theme
|
142
|
+
theme_name = request_path[3]
|
136
143
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
144
|
+
# Get the theme resource type (html/css/gfx)
|
145
|
+
theme_part = request_path[4].to_sym
|
146
|
+
|
147
|
+
# Get the theme resource identifier
|
148
|
+
req_file = request_path[5]
|
149
|
+
|
150
|
+
# checks for theme name
|
151
|
+
has_theme = @client_cache.theme_cache.has_key?( theme_name )
|
152
|
+
|
153
|
+
# checks for theme part (css/html/gfx)
|
154
|
+
has_theme_part = ( has_theme and @client_cache.theme_cache[theme_name].has_key?( theme_part ) )
|
155
|
+
|
156
|
+
# checks for theme file
|
157
|
+
has_theme_file = ( has_theme_part and @client_cache.theme_cache[theme_name][theme_part].has_key?( req_file ) )
|
158
|
+
|
159
|
+
if not has_theme_file and req_file == 'common.css'
|
160
|
+
response.status = 200
|
161
|
+
response['Content-Type'] = 'text/css'
|
162
|
+
response.body = ''
|
163
|
+
end
|
164
|
+
|
165
|
+
if not has_theme
|
166
|
+
response.status = 404
|
167
|
+
response.body = '404 - Theme Not Found'
|
168
|
+
puts "Theme #{theme_name} not found, avail: #{@client_cache.theme_cache.keys.join(', ')}" if RSence.args[:verbose]
|
169
|
+
elsif not has_theme_part
|
170
|
+
response.status = 503
|
171
|
+
response.body = '503 - Invalid Theme Part Request'
|
172
|
+
elsif not has_theme_file
|
173
|
+
response.status = 404
|
174
|
+
response.body = '404 - Theme Resource Not Found'
|
175
|
+
puts "File not found, avail: #{@client_cache.theme_cache[theme_name][theme_part].keys.join(', ')}" if RSence.args[:verbose]
|
176
|
+
else
|
155
177
|
|
156
|
-
|
157
|
-
# checks for theme file
|
158
|
-
has_theme_file = has_theme_part and @client_cache.theme_cache[theme_name][theme_part].has_key?( req_file )
|
178
|
+
response.status = 200
|
159
179
|
|
180
|
+
file_ext = req_file.split('.')[-1]
|
181
|
+
response['Content-Type'] = {
|
182
|
+
'html' => 'text/html; charset=utf-8',
|
183
|
+
'css' => 'text/css; charset=utf-8',
|
184
|
+
'png' => 'image/png',
|
185
|
+
'jpg' => 'image/jpeg',
|
186
|
+
'gif' => 'image/gif',
|
187
|
+
'swf' => 'application/x-shockwave-flash'
|
188
|
+
}[file_ext]
|
160
189
|
|
161
|
-
if
|
162
|
-
|
163
|
-
|
164
|
-
|
190
|
+
if theme_part == :gfx
|
191
|
+
support_gzip = false
|
192
|
+
else
|
193
|
+
support_gzip = support_gzip?( ua )
|
165
194
|
end
|
166
195
|
|
167
|
-
if
|
168
|
-
response
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
response.
|
173
|
-
response.body = '503 - Invalid Theme Part Request'
|
174
|
-
elsif not has_theme_file
|
175
|
-
response.status = 404
|
176
|
-
response.body = '404 - Theme Resource Not Found'
|
177
|
-
puts "File not found, avail: #{@client_cache.theme_cache[theme_name][theme_part].keys.join(', ')}" if RSence.args[:verbose]
|
196
|
+
if support_gzip
|
197
|
+
response['Last-Modified'] = @client_cache.last_modified
|
198
|
+
body = @client_cache.theme_cache[theme_name][theme_part][ req_file+'.gz' ]
|
199
|
+
response['Content-Length'] = body.bytesize.to_s
|
200
|
+
response['Content-Encoding'] = 'gzip'
|
201
|
+
response.body = body
|
178
202
|
else
|
179
203
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
'css' => 'text/css; charset=utf-8',
|
186
|
-
'png' => 'image/png',
|
187
|
-
'jpg' => 'image/jpeg',
|
188
|
-
'gif' => 'image/gif',
|
189
|
-
'swf' => 'application/x-shockwave-flash'
|
190
|
-
}[file_ext]
|
191
|
-
|
192
|
-
support_gzip = false if theme_part == :gfx
|
193
|
-
support_gzip = false if (is_safari or is_msie or is_symbian)
|
194
|
-
|
195
|
-
if support_gzip
|
196
|
-
response['Last-Modified'] = @client_cache.last_modified
|
197
|
-
body = @client_cache.theme_cache[theme_name][theme_part][ req_file+'.gz' ]
|
198
|
-
response['Content-Length'] = body.bytesize.to_s
|
199
|
-
response['Content-Encoding'] = 'gzip'
|
200
|
-
response.body = body
|
201
|
-
else
|
202
|
-
|
203
|
-
# Special IE6 condition to serve gifs instead of png's, because it works much better
|
204
|
-
# than using the ActiveX alpha filter hack
|
205
|
-
if is_msie6 and req_file[-4..-1] == '.png'
|
206
|
-
ie6_req_png2gif = req_file.gsub('.png','-ie6.gif')
|
207
|
-
req_file = ie6_req_png2gif if @client_cache.theme_cache[theme_name][theme_part].include?(ie6_req_png2gif)
|
208
|
-
end
|
209
|
-
|
210
|
-
response['Last-Modified'] = @client_cache.last_modified
|
211
|
-
body = @client_cache.theme_cache[theme_name][theme_part][ req_file ]
|
212
|
-
if body.nil?
|
213
|
-
warn "ClientPkgServe#get: empty body for #{request.path}"
|
214
|
-
body = ''
|
215
|
-
end
|
216
|
-
response['Content-Length'] = body.bytesize.to_s
|
217
|
-
response.body = body
|
218
|
-
|
204
|
+
# Special IE6 condition to serve gifs instead of png's, because it works much better
|
205
|
+
# than using the ActiveX alpha filter hack
|
206
|
+
if ua[:msie6] and req_file[-4..-1] == '.png'
|
207
|
+
ie6_req_png2gif = req_file.gsub('.png','-ie6.gif')
|
208
|
+
req_file = ie6_req_png2gif if @client_cache.theme_cache[theme_name][theme_part].include?(ie6_req_png2gif)
|
219
209
|
end
|
220
|
-
end
|
221
210
|
|
211
|
+
response['Last-Modified'] = @client_cache.last_modified
|
212
|
+
body = @client_cache.theme_cache[theme_name][theme_part][ req_file ]
|
213
|
+
if body.nil?
|
214
|
+
warn "ClientPkgServe#get: empty body for #{request.path}"
|
215
|
+
body = ''
|
216
|
+
end
|
217
|
+
response['Content-Length'] = body.bytesize.to_s
|
218
|
+
response.body = body
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
## Responds to get-requests
|
225
|
+
def get( request, response, session )
|
226
|
+
|
227
|
+
build_busy_wait
|
228
|
+
|
229
|
+
ua = check_ua( request )
|
230
|
+
|
231
|
+
set_headers( response )
|
232
|
+
|
233
|
+
## Split path into an array for determining what to serve
|
234
|
+
request_uri = '/'+request.path.match( /^#{::RSence.config[:broker_urls][:h]}(.*)$/ )[1]
|
235
|
+
|
236
|
+
request_path = request_uri.split( '/' )
|
237
|
+
|
238
|
+
## Requested type of client resource (js/themes)
|
239
|
+
req_type = request_path[2]
|
240
|
+
|
241
|
+
unless ['js','themes'].include? req_type
|
242
|
+
req_rev = req_type
|
243
|
+
req_type = request_path[3]
|
244
|
+
request_path.delete_at(2)
|
245
|
+
end
|
246
|
+
|
247
|
+
## Special rules for the special browser
|
248
|
+
if request.path.end_with?('.htc')
|
249
|
+
serve_htc( request, response, request_path )
|
250
|
+
## Serve compiled client javascript component files:
|
251
|
+
elsif req_type == 'js'
|
252
|
+
serve_js( request, response, request_path, ua )
|
253
|
+
## Serve client theme files
|
254
|
+
elsif req_type == 'themes'
|
255
|
+
serve_theme( request, response, request_path, ua )
|
222
256
|
end
|
223
257
|
|
224
258
|
end
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
class: RSence.GUIApp
|
19
19
|
# Each class takes a number of options for its constructor.
|
20
20
|
options:
|
21
|
-
|
21
|
+
label: Welcome App
|
22
22
|
# The subviews use the class defined above as their parent component.
|
23
23
|
subviews:
|
24
24
|
# The sheet is used as the main visual container for the gui in this app.
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rsence
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 2.1.
|
9
|
+
- 8
|
10
|
+
version: 2.1.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Riassence Inc.
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-01-
|
18
|
+
date: 2011-01-28 00:00:00 +02:00
|
19
19
|
default_executable: rsence
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -403,7 +403,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
403
403
|
requirements: []
|
404
404
|
|
405
405
|
rubyforge_project: rsence-
|
406
|
-
rubygems_version: 1.4.
|
406
|
+
rubygems_version: 1.4.2
|
407
407
|
signing_key:
|
408
408
|
specification_version: 3
|
409
409
|
summary: Release 2.1 version of the RSence framework.
|