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 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-ruby gem, you'll gain persistent sessions and this warning message will disappear:
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://www.sequel.org]
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-ruby*:: SQLite[http://www.sqlite.org] is a light-weight SQL library. Recommended for development and small projects.
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
- * http://github.com/rsence/rsence
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.7
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 function
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
- * refreshLable function
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 mouseUp listeners.
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
- /* Alternative right button detection, wrapper for the mouseDown method */
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
- // EVENT.mouseDown(e, false);
896
- Event.stop(e);
897
-
898
- /***
899
-
900
- IMPLEMENT SEPARATE CONTEXT-MENU EVENT HANDLING HERE
901
-
902
- ***/
903
-
904
- // if(Event.isLeftClick(e)){
905
- // EVENT.status[EVENT.button2] = false;
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 _time = new Date().getTime(), i;
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
- } else {
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(this.elemId,_obj.props[i].prop,
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;
@@ -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.right -= _bounds.left;
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
- for(var i=0;i<this.listItems.length;i++){
60
- if(this.listItems[i][0]===this.value){
61
- this.setLabel( this.listItems[i][1] );
62
- return;
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.menuItemView.die();
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
- ## Responds to get-requests
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
- support_gzip = (request.header.has_key?('accept-encoding') and request.header['accept-encoding'].include?('gzip'))
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?('user-agent')
46
- ua = request.header['user-agent']
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
- ## Split path into an array for determining what to serve
54
- request_uri = '/'+request.path.match( /^#{::RSence.config[:broker_urls][:h]}(.*)$/ )[1]
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
- ## Special rules for the special browser
68
- if request.path.include?('.htc')
69
- req_file = request_path[3]
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
- # this file is a part of iefix, it injects calls to
72
- # the ie rendering engine to override stupid behavior
73
- # when changing element properties
74
- if request_path.include?('ie_css_element.htc')
75
- response.status = 200
76
- response['Content-Type'] = 'text/x-component'
77
- ## Usually, we don't need it, because the client framework does calls when needed
78
- response.body = %{<PUBLIC:COMPONENT lightWeight="true"></PUBLIC:COMPONENT>}
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
- # this file is a part of iefix, it injects calls to
84
- # the ie rendering engine to override stupid behavior
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
- # Other htc requests are invalid
97
- else
98
- response.status = 503
99
- response.body = '503 - Invalid Request'
100
- end
101
-
102
- ## Serve compiled client javascript component files:
103
- elsif req_type == 'js'
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
- # the file-specific identifier ('core', 'basic' etc)
107
- req_file = request_path[3][0..-4]
119
+ # these browsers have issues with gzipped js content
120
+ support_gzip = support_gzip?( ua )
108
121
 
109
- if not @client_cache.js_cache.has_key?( req_file )
110
- response.status = 404
111
- response.body = '/* 404 - Not Found */'
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.status = 200
115
- response['Content-Type'] = 'text/javascript; charset=utf-8'
116
-
117
- # these browsers have issues with gzipped js content
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
- ## Serve client theme files
138
- elsif req_type == 'themes'
139
-
140
- # Get the name of the theme
141
- theme_name = request_path[3]
142
-
143
- # Get the theme resource type (html/css/gfx)
144
- theme_part = request_path[4].to_sym
145
-
146
- # Get the theme resource identifier
147
- req_file = request_path[5]
148
-
149
- # checks for theme name
150
- has_theme = @client_cache.theme_cache.has_key?( theme_name )
151
-
152
- # checks for theme part (css/html/gfx)
153
- has_theme_part = has_theme and @client_cache.theme_cache[theme_name].has_key?( theme_part )
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
- # puts "has_theme_part: #{@client_cache.theme_cache[theme_name][theme_part.to_sym].inspect}"
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 not has_theme_file and req_file == 'common.css'
162
- response.status = 200
163
- response['Content-Type'] = 'text/css'
164
- response.body = ''
190
+ if theme_part == :gfx
191
+ support_gzip = false
192
+ else
193
+ support_gzip = support_gzip?( ua )
165
194
  end
166
195
 
167
- if not has_theme
168
- response.status = 404
169
- response.body = '404 - Theme Not Found'
170
- puts "Theme #{theme_name} not found, avail: #{@client_cache.theme_cache.keys.join(', ')}" if RSence.args[:verbose]
171
- elsif not has_theme_part
172
- response.status = 503
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
- response.status = 200
181
-
182
- file_ext = req_file.split('.')[-1]
183
- response['Content-Type'] = {
184
- 'html' => 'text/html; charset=utf-8',
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
- title: Welcome App
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: 5
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 1
9
- - 7
10
- version: 2.1.7
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-03 00:00:00 +02:00
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.1
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.