spiderfw 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,64 @@
1
+ .asmContainer {
2
+ /* container that surrounds entire asmSelect widget */
3
+ }
4
+
5
+ .asmSelect {
6
+ /* the newly created regular 'select' */
7
+ display: inline;
8
+ }
9
+
10
+ .asmOptionDisabled {
11
+ /* disabled options in new select */
12
+ color: #999;
13
+ }
14
+
15
+ .asmHighlight {
16
+ /* the highlight span */
17
+ padding: 0;
18
+ margin: 0 0 0 1em;
19
+ }
20
+
21
+ .asmList {
22
+ /* html list that contains selected items */
23
+ margin: 1em 8em 2em 8em;
24
+ position: relative;
25
+ display: block;
26
+ padding-left: 0;
27
+ list-style: none;
28
+ clear: both;
29
+ }
30
+
31
+ .asmListItem {
32
+ /* li item from the html list above */
33
+ position: relative;
34
+ margin-left: 0;
35
+ padding-left: 0;
36
+ list-style: none;
37
+ background: #ddd;
38
+ border: 1px solid #bbb;
39
+ width: 100%;
40
+ margin: 0 0 -1px 0;
41
+ line-height: 1em;
42
+ }
43
+
44
+ .asmListItem:hover {
45
+ background-color: #e5e5e5;
46
+ }
47
+
48
+ .asmListItemLabel {
49
+ /* this is a span that surrounds the text in the item, except for the remove link */
50
+ padding: 5px;
51
+ display: block;
52
+ }
53
+
54
+ .asmListSortable .asmListItemLabel {
55
+ cursor: move;
56
+ }
57
+
58
+ .asmListItemRemove {
59
+ /* the remove link in each list item */
60
+ position: absolute;
61
+ right: 0;
62
+ top: 0;
63
+ padding: 5px;
64
+ }
@@ -0,0 +1,407 @@
1
+ /*
2
+ * Alternate Select Multiple (asmSelect) 1.0.4a beta - jQuery Plugin
3
+ * http://www.ryancramer.com/projects/asmselect/
4
+ *
5
+ * Copyright (c) 2009 by Ryan Cramer - http://www.ryancramer.com
6
+ *
7
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
8
+ * and GPL (GPL-LICENSE.txt) licenses.
9
+ *
10
+ */
11
+
12
+ (function($) {
13
+
14
+ $.fn.asmSelect = function(customOptions) {
15
+
16
+ var options = {
17
+
18
+ listType: 'ol', // Ordered list 'ol', or unordered list 'ul'
19
+ sortable: false, // Should the list be sortable?
20
+ highlight: false, // Use the highlight feature?
21
+ animate: false, // Animate the the adding/removing of items in the list?
22
+ addItemTarget: 'bottom', // Where to place new selected items in list: top or bottom
23
+ hideWhenAdded: false, // Hide the option when added to the list? works only in FF
24
+ debugMode: false, // Debug mode keeps original select visible
25
+
26
+ removeLabel: 'remove', // Text used in the "remove" link
27
+ highlightAddedLabel: 'Added: ', // Text that precedes highlight of added item
28
+ highlightRemovedLabel: 'Removed: ', // Text that precedes highlight of removed item
29
+
30
+ containerClass: 'asmContainer', // Class for container that wraps this widget
31
+ selectClass: 'asmSelect', // Class for the newly created <select>
32
+ optionDisabledClass: 'asmOptionDisabled', // Class for items that are already selected / disabled
33
+ listClass: 'asmList', // Class for the list ($ol)
34
+ listSortableClass: 'asmListSortable', // Another class given to the list when it is sortable
35
+ listItemClass: 'asmListItem', // Class for the <li> list items
36
+ listItemLabelClass: 'asmListItemLabel', // Class for the label text that appears in list items
37
+ removeClass: 'asmListItemRemove', // Class given to the "remove" link
38
+ highlightClass: 'asmHighlight' // Class given to the highlight <span>
39
+
40
+ };
41
+
42
+ $.extend(options, customOptions);
43
+
44
+ return this.each(function(index) {
45
+
46
+ var $original = $(this); // the original select multiple
47
+ var $container; // a container that is wrapped around our widget
48
+ var $select; // the new select we have created
49
+ var $ol; // the list that we are manipulating
50
+ var buildingSelect = false; // is the new select being constructed right now?
51
+ var ieClick = false; // in IE, has a click event occurred? ignore if not
52
+ var ignoreOriginalChangeEvent = false; // originalChangeEvent bypassed when this is true
53
+
54
+ function init() {
55
+
56
+ // initialize the alternate select multiple
57
+
58
+ // this loop ensures uniqueness, in case of existing asmSelects placed by ajax (1.0.3)
59
+ while($("#" + options.containerClass + index).size() > 0) index++;
60
+
61
+ $select = $("<select></select>")
62
+ .addClass(options.selectClass)
63
+ .attr('name', options.selectClass + index)
64
+ .attr('id', options.selectClass + index);
65
+
66
+ $selectRemoved = $("<select></select>");
67
+
68
+ $ol = $("<" + options.listType + "></" + options.listType + ">")
69
+ .addClass(options.listClass)
70
+ .attr('id', options.listClass + index);
71
+
72
+ $container = $("<div></div>")
73
+ .addClass(options.containerClass)
74
+ .attr('id', options.containerClass + index);
75
+
76
+ buildSelect();
77
+
78
+ $select.change(selectChangeEvent)
79
+ .click(selectClickEvent);
80
+
81
+ $original.change(originalChangeEvent)
82
+ .wrap($container).before($select).before($ol);
83
+
84
+ if(options.sortable) makeSortable();
85
+
86
+ if($.browser.msie && $.browser.version < 8) $ol.css('display', 'inline-block'); // Thanks Matthew Hutton
87
+ }
88
+
89
+ function makeSortable() {
90
+
91
+ // make any items in the selected list sortable
92
+ // requires jQuery UI sortables, draggables, droppables
93
+
94
+ $ol.sortable({
95
+ items: 'li.' + options.listItemClass,
96
+ handle: '.' + options.listItemLabelClass,
97
+ axis: 'y',
98
+ update: function(e, data) {
99
+
100
+ var updatedOptionId;
101
+
102
+ $(this).children("li").each(function(n) {
103
+
104
+ $option = $('#' + $(this).attr('rel'));
105
+
106
+ if($(this).is(".ui-sortable-helper")) {
107
+ updatedOptionId = $option.attr('id');
108
+ return;
109
+ }
110
+
111
+ $original.append($option);
112
+ });
113
+
114
+ if(updatedOptionId) triggerOriginalChange(updatedOptionId, 'sort');
115
+ }
116
+
117
+ }).addClass(options.listSortableClass);
118
+ }
119
+
120
+ function selectChangeEvent(e) {
121
+
122
+ // an item has been selected on the regular select we created
123
+ // check to make sure it's not an IE screwup, and add it to the list
124
+
125
+ if($.browser.msie && $.browser.version < 7 && !ieClick) return;
126
+ var id = $(this).children("option:selected").slice(0,1).attr('rel');
127
+ addListItem(id);
128
+ ieClick = false;
129
+ triggerOriginalChange(id, 'add'); // for use by user-defined callbacks
130
+ }
131
+
132
+ function selectClickEvent() {
133
+
134
+ // IE6 lets you scroll around in a select without it being pulled down
135
+ // making sure a click preceded the change() event reduces the chance
136
+ // if unintended items being added. there may be a better solution?
137
+
138
+ ieClick = true;
139
+ }
140
+
141
+ function originalChangeEvent(e) {
142
+
143
+ // select or option change event manually triggered
144
+ // on the original <select multiple>, so rebuild ours
145
+
146
+ if(ignoreOriginalChangeEvent) {
147
+ ignoreOriginalChangeEvent = false;
148
+ return;
149
+ }
150
+
151
+ $select.empty();
152
+ $ol.empty();
153
+ buildSelect();
154
+
155
+ // opera has an issue where it needs a force redraw, otherwise
156
+ // the items won't appear until something else forces a redraw
157
+ if($.browser.opera) $ol.hide().fadeIn("fast");
158
+ }
159
+
160
+ function buildSelect() {
161
+
162
+ // build or rebuild the new select that the user
163
+ // will select items from
164
+
165
+ buildingSelect = true;
166
+
167
+ // add a first option to be the home option / default selectLabel
168
+ $select.prepend("<option>" + $original.attr('title') + "</option>");
169
+
170
+ $original.children("option").each(function(n) {
171
+
172
+ var $t = $(this);
173
+ var id;
174
+
175
+ if(!$t.attr('id')) $t.attr('id', 'asm' + index + 'option' + n);
176
+ id = $t.attr('id');
177
+
178
+ if($t.is(":selected")) {
179
+ addListItem(id);
180
+ addSelectOption(id, true);
181
+ } else {
182
+ addSelectOption(id);
183
+ }
184
+ });
185
+
186
+ if(!options.debugMode) $original.hide(); // IE6 requires this on every buildSelect()
187
+ selectFirstItem();
188
+ buildingSelect = false;
189
+ }
190
+
191
+ function addSelectOption(optionId, disabled) {
192
+
193
+ // add an <option> to the <select>
194
+ // used only by buildSelect()
195
+
196
+ if(disabled == undefined) var disabled = false;
197
+
198
+ var $O = $('#' + optionId);
199
+ var $option = $("<option>" + $O.text() + "</option>")
200
+ .val($O.val())
201
+ .attr('rel', optionId);
202
+
203
+ if(disabled) disableSelectOption($option);
204
+
205
+ $select.append($option);
206
+ }
207
+
208
+ function selectFirstItem() {
209
+
210
+ // select the firm item from the regular select that we created
211
+
212
+ $select.children(":eq(0)").attr("selected", true);
213
+ }
214
+
215
+ function disableSelectOption($option) {
216
+
217
+ // make an option disabled, indicating that it's already been selected
218
+ // because safari is the only browser that makes disabled items look 'disabled'
219
+ // we apply a class that reproduces the disabled look in other browsers
220
+
221
+ $option.addClass(options.optionDisabledClass)
222
+ .attr("selected", false)
223
+ .attr("disabled", true);
224
+
225
+ if(options.hideWhenAdded) $option.hide();
226
+ if($.browser.msie) $select.hide().show(); // this forces IE to update display
227
+ }
228
+
229
+ function enableSelectOption($option) {
230
+
231
+ // given an already disabled select option, enable it
232
+
233
+ $option.removeClass(options.optionDisabledClass)
234
+ .attr("disabled", false);
235
+
236
+ if(options.hideWhenAdded) $option.show();
237
+ if($.browser.msie) $select.hide().show(); // this forces IE to update display
238
+ }
239
+
240
+ function addListItem(optionId) {
241
+
242
+ // add a new item to the html list
243
+
244
+ var $O = $('#' + optionId);
245
+
246
+ if(!$O) return; // this is the first item, selectLabel
247
+
248
+ var $removeLink = $("<a></a>")
249
+ .attr("href", "#")
250
+ .addClass(options.removeClass)
251
+ .prepend(options.removeLabel)
252
+ .click(function() {
253
+ dropListItem($(this).parent('li').attr('rel'));
254
+ return false;
255
+ });
256
+
257
+ var $itemLabel = $("<span></span>")
258
+ .addClass(options.listItemLabelClass)
259
+ .html($O.html());
260
+
261
+ var $item = $("<li></li>")
262
+ .attr('rel', optionId)
263
+ .addClass(options.listItemClass)
264
+ .append($itemLabel)
265
+ .append($removeLink)
266
+ .hide();
267
+
268
+ if(!buildingSelect) {
269
+ if($O.is(":selected")) return; // already have it
270
+ $O.attr('selected', true);
271
+ }
272
+
273
+ if(options.addItemTarget == 'top' && !buildingSelect) {
274
+ $ol.prepend($item);
275
+ if(options.sortable) $original.prepend($O);
276
+ } else {
277
+ $ol.append($item);
278
+ if(options.sortable) $original.append($O);
279
+ }
280
+
281
+ addListItemShow($item);
282
+
283
+ disableSelectOption($("[rel=" + optionId + "]", $select));
284
+
285
+ if(!buildingSelect) {
286
+ setHighlight($item, options.highlightAddedLabel);
287
+ selectFirstItem();
288
+ if(options.sortable) $ol.sortable("refresh");
289
+ }
290
+
291
+ }
292
+
293
+ function addListItemShow($item) {
294
+
295
+ // reveal the currently hidden item with optional animation
296
+ // used only by addListItem()
297
+
298
+ if(options.animate && !buildingSelect) {
299
+ $item.animate({
300
+ opacity: "show",
301
+ height: "show"
302
+ }, 100, "swing", function() {
303
+ $item.animate({
304
+ height: "+=2px"
305
+ }, 50, "swing", function() {
306
+ $item.animate({
307
+ height: "-=2px"
308
+ }, 25, "swing");
309
+ });
310
+ });
311
+ } else {
312
+ $item.show();
313
+ }
314
+ }
315
+
316
+ function dropListItem(optionId, highlightItem) {
317
+
318
+ // remove an item from the html list
319
+
320
+ if(highlightItem == undefined) var highlightItem = true;
321
+ var $O = $('#' + optionId);
322
+
323
+ $O.attr('selected', false);
324
+ $item = $ol.children("li[rel=" + optionId + "]");
325
+
326
+ dropListItemHide($item);
327
+ enableSelectOption($("[rel=" + optionId + "]", options.removeWhenAdded ? $selectRemoved : $select));
328
+
329
+ if(highlightItem) setHighlight($item, options.highlightRemovedLabel);
330
+
331
+ triggerOriginalChange(optionId, 'drop');
332
+
333
+ }
334
+
335
+ function dropListItemHide($item) {
336
+
337
+ // remove the currently visible item with optional animation
338
+ // used only by dropListItem()
339
+
340
+ if(options.animate && !buildingSelect) {
341
+
342
+ $prevItem = $item.prev("li");
343
+
344
+ $item.animate({
345
+ opacity: "hide",
346
+ height: "hide"
347
+ }, 100, "linear", function() {
348
+ $prevItem.animate({
349
+ height: "-=2px"
350
+ }, 50, "swing", function() {
351
+ $prevItem.animate({
352
+ height: "+=2px"
353
+ }, 100, "swing");
354
+ });
355
+ $item.remove();
356
+ });
357
+
358
+ } else {
359
+ $item.remove();
360
+ }
361
+ }
362
+
363
+ function setHighlight($item, label) {
364
+
365
+ // set the contents of the highlight area that appears
366
+ // directly after the <select> single
367
+ // fade it in quickly, then fade it out
368
+
369
+ if(!options.highlight) return;
370
+
371
+ $select.next("#" + options.highlightClass + index).remove();
372
+
373
+ var $highlight = $("<span></span>")
374
+ .hide()
375
+ .addClass(options.highlightClass)
376
+ .attr('id', options.highlightClass + index)
377
+ .html(label + $item.children("." + options.listItemLabelClass).slice(0,1).text());
378
+
379
+ $select.after($highlight);
380
+
381
+ $highlight.fadeIn("fast", function() {
382
+ setTimeout(function() { $highlight.fadeOut("slow"); }, 50);
383
+ });
384
+ }
385
+
386
+ function triggerOriginalChange(optionId, type) {
387
+
388
+ // trigger a change event on the original select multiple
389
+ // so that other scripts can pick them up
390
+
391
+ ignoreOriginalChangeEvent = true;
392
+ $option = $("#" + optionId);
393
+
394
+ $original.trigger('change', [{
395
+ 'option': $option,
396
+ 'value': $option.val(),
397
+ 'id': optionId,
398
+ 'item': $ol.children("[rel=" + optionId + "]"),
399
+ 'type': type
400
+ }]);
401
+ }
402
+
403
+ init();
404
+ });
405
+ };
406
+
407
+ })(jQuery);
@@ -0,0 +1,132 @@
1
+ /*
2
+ * Auto Expanding Text Area (1.2.2)
3
+ * by Chrys Bader (www.chrysbader.com)
4
+ * chrysb@gmail.com
5
+ *
6
+ * Special thanks to:
7
+ * Jake Chapa - jake@hybridstudio.com
8
+ * John Resig - jeresig@gmail.com
9
+ *
10
+ * Copyright (c) 2008 Chrys Bader (www.chrysbader.com)
11
+ * Licensed under the GPL (GPL-LICENSE.txt) license.
12
+ *
13
+ *
14
+ * NOTE: This script requires jQuery to work. Download jQuery at www.jquery.com
15
+ *
16
+ */
17
+
18
+ (function(jQuery) {
19
+
20
+ var self = null;
21
+
22
+ jQuery.fn.autogrow = function(o)
23
+ {
24
+ return this.each(function() {
25
+ new jQuery.autogrow(this, o);
26
+ });
27
+ };
28
+
29
+
30
+ /**
31
+ * The autogrow object.
32
+ *
33
+ * @constructor
34
+ * @name jQuery.autogrow
35
+ * @param Object e The textarea to create the autogrow for.
36
+ * @param Hash o A set of key/value pairs to set as configuration properties.
37
+ * @cat Plugins/autogrow
38
+ */
39
+
40
+ jQuery.autogrow = function (e, o)
41
+ {
42
+ this.options = o || {};
43
+ this.dummy = null;
44
+ this.interval = null;
45
+ this.line_height = this.options.lineHeight || parseInt(jQuery(e).css('line-height'));
46
+ this.min_height = this.options.minHeight || parseInt(jQuery(e).css('min-height'));
47
+ this.max_height = this.options.maxHeight || parseInt(jQuery(e).css('max-height'));;
48
+ this.textarea = jQuery(e);
49
+
50
+ if(this.line_height == NaN)
51
+ this.line_height = 0;
52
+
53
+ // Only one textarea activated at a time, the one being used
54
+ this.init();
55
+ };
56
+
57
+ jQuery.autogrow.fn = jQuery.autogrow.prototype = {
58
+ autogrow: '1.2.2'
59
+ };
60
+
61
+ jQuery.autogrow.fn.extend = jQuery.autogrow.extend = jQuery.extend;
62
+
63
+ jQuery.autogrow.fn.extend({
64
+
65
+ init: function() {
66
+ var self = this;
67
+ this.textarea.css({overflow: 'hidden', display: 'block'});
68
+ this.textarea.bind('focus', function() { self.startExpand() } ).bind('blur', function() { self.stopExpand() });
69
+ this.checkExpand();
70
+ },
71
+
72
+ startExpand: function() {
73
+ var self = this;
74
+ this.interval = window.setInterval(function() {self.checkExpand()}, 400);
75
+ },
76
+
77
+ stopExpand: function() {
78
+ clearInterval(this.interval);
79
+ },
80
+
81
+ checkExpand: function() {
82
+
83
+ if (this.dummy == null)
84
+ {
85
+ this.dummy = jQuery('<div></div>');
86
+ this.dummy.css({
87
+ 'font-size' : this.textarea.css('font-size'),
88
+ 'font-family': this.textarea.css('font-family'),
89
+ 'width' : this.textarea.css('width'),
90
+ 'padding' : this.textarea.css('padding'),
91
+ 'line-height': this.line_height + 'px',
92
+ 'overflow-x' : 'hidden',
93
+ 'position' : 'absolute',
94
+ 'top' : 0,
95
+ 'left' : -9999
96
+ }).appendTo('body');
97
+ }
98
+
99
+ // Strip HTML tags
100
+ var html = this.textarea.val().replace(/(<|>)/g, '');
101
+
102
+ // IE is different, as per usual
103
+ if ($.browser.msie)
104
+ {
105
+ html = html.replace(/\n/g, '<BR>new');
106
+ }
107
+ else
108
+ {
109
+ html = html.replace(/\n/g, '<br>new');
110
+ }
111
+
112
+ if (this.dummy.html() != html)
113
+ {
114
+ this.dummy.html(html);
115
+
116
+ if (this.max_height > 0 && (this.dummy.height() + this.line_height > this.max_height))
117
+ {
118
+ this.textarea.css('overflow-y', 'auto');
119
+ }
120
+ else
121
+ {
122
+ this.textarea.css('overflow-y', 'hidden');
123
+ if (this.textarea.height() < this.dummy.height() + this.line_height || (this.dummy.height() < this.textarea.height()))
124
+ {
125
+ this.textarea.animate({height: (this.dummy.height() + this.line_height) + 'px'}, 100);
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ });
132
+ })(jQuery);
@@ -40,6 +40,7 @@ module Spider; module Components
40
40
  @scene.username = @request.user.to_s
41
41
  else
42
42
  @scene.username = _("guest")
43
+ @scene.guest = true
43
44
  end
44
45
  super
45
46
  end
@@ -6,7 +6,7 @@
6
6
  </div>
7
7
  <div class="tools">
8
8
  Benvenuto, { @username }.
9
- <a href="{ @logout_url }">Logout</a>
9
+ <a href="{ @logout_url }" sp:if="!@guest">Logout</a>
10
10
  </div>
11
11
  </div>
12
12
  <div class="current_section">
@@ -229,7 +229,7 @@ module Spider; module Components
229
229
  def parse_runtime_content(doc, src_path='')
230
230
  doc = super
231
231
  return doc if doc.children.empty?
232
- doc.root.xpath('sublist', Template.xml_namespaces).each do |sl|
232
+ doc.root.children_of_type('sublist').each do |sl|
233
233
  raise ArgumentError, "Sublist of #{@id} does not have an id" unless sl['id']
234
234
  @requested_sublists ||= []
235
235
  @requested_sublists << sl
@@ -2,6 +2,14 @@ Spider.defineWidget('Spider.Forms.Select', 'Spider.Forms.Input', {
2
2
 
3
3
  autoInit: true,
4
4
 
5
+ ready: function(){
6
+ if (this.el.is('select[multiple]')) this.el.asmSelect({
7
+ removeLabel: 'togli',
8
+ highlightAddedLabel: 'Aggiunto: ',
9
+ highlightRemovedLabel: 'Tolto: '
10
+ });
11
+ },
12
+
5
13
  onConnectedChange: function(connected, val){
6
14
  var params = {};
7
15
  params[connected] = val;
@@ -0,0 +1,8 @@
1
+ Spider.defineWidget('Spider.Forms.TextArea', 'Spider.Forms.Input', {
2
+
3
+ autoInit: true,
4
+
5
+ ready: function(){
6
+ this.el.autogrow();
7
+ }
8
+ });
@@ -6,7 +6,6 @@ module Spider; module Forms
6
6
  is_attr_accessor :multiple
7
7
  is_attr_accessor :blank_option, :type => TrueClass, :default => true
8
8
  is_attr_accessor :condition
9
- is_attr_accessor :show_element
10
9
  attr_accessor :data
11
10
 
12
11
  def widget_init(action='')
@@ -42,7 +41,6 @@ module Spider; module Forms
42
41
  @scene.data.condition.and(conn_cond)
43
42
  end
44
43
  @scene.values = {}
45
- @scene.strings = {}
46
44
  debug("SELECT VALUE:")
47
45
  debug(@value)
48
46
  @scene.selected = {}
@@ -54,18 +52,9 @@ module Spider; module Forms
54
52
  end
55
53
  @scene.data.each_index do |i|
56
54
  @scene.values[i] = @model.primary_keys.map{|k| @scene.data[i][k] }.join(',')
57
- @scene.strings[i] = format_string(@scene.data[i])
58
55
  end
59
56
  super
60
57
  end
61
-
62
- def format_string(obj)
63
- if (@show_element)
64
- return obj.get(@show_element).to_s
65
- else
66
- return obj.to_s
67
- end
68
- end
69
58
 
70
59
 
71
60
  def value=(val)
@@ -1,6 +1,8 @@
1
1
  <select name="{ @value_param }" sp:attr-if="@multiple,multiple" class="">
2
2
  <tpl:asset type="js" src="input.js" />
3
3
  <tpl:asset type="js" src="select.js" />
4
+ <tpl:asset type="js" app="core/components" src="js/jquery/plugins/asmselect/jquery.asmselect.js" if="@multiple" />
5
+ <tpl:asset type="css" app="core/components" src="js/jquery/plugins/asmselect/jquery.asmselect.css" if="@multiple" />
4
6
  <option sp:if="@blank_option"> </option>
5
7
  <option sp:each_index="@data |i|" value="{ @values[i] }" sp:attr-if="@selected[values[i]],selected">
6
8
  { @data[i].to_s }
@@ -3,7 +3,7 @@ module Spider; module Forms
3
3
  class TextArea < Input
4
4
  tag 'textarea'
5
5
  is_attr_accessor :rows, :type => Fixnum, :default => 6
6
- is_attr_accessor :cols, :type => Fixnum, :default => 40
6
+ is_attr_accessor :cols, :type => Fixnum, :default => 80
7
7
 
8
8
  end
9
9
 
@@ -1 +1,2 @@
1
- <textarea name="{ @name }" rows="{ @rows }" cols="{ @cols }">{ @value }</textarea>
1
+ <textarea name="{ @name }" rows="{ @rows }" cols="{ @cols }"><tpl:asset type="js" src="text_area.js" />
2
+ <tpl:asset type="js" app="core/components" src="js/jquery/plugins/autogrow/jquery.autogrow.js" />{ @value }</textarea>
@@ -30,11 +30,6 @@ class TestCommand < CmdParse::Command
30
30
 
31
31
  self_cmd = CmdParse::Command.new('self', false)
32
32
  self_cmd.short_desc = _("Run framework tests")
33
- self_cmd.options = CmdParse::OptionParserWrapper.new do |opt|
34
- opt.on("--files", _("Run only files containing the given string (or strings, comma separated)"), "-f"){ |f|
35
- @files = f
36
- }
37
- end
38
33
  self_cmd.set_execution_block do
39
34
  require 'test/unit/collector/dir'
40
35
  require 'test/unit'
@@ -40,6 +40,7 @@ class WebServerCommand < CmdParse::Command
40
40
  raise "Can't use cgi mode with SSL" if @ssl && @cgi
41
41
  @port ||= Spider.conf.get('webserver.port')
42
42
  @server_name ||= Spider.conf.get('http.server')
43
+ @pid_file = Spider.paths[:var]+'/run/server.pid'
43
44
  puts _("Using webserver %s") % @server_name if $verbose
44
45
  puts _("Listening on port %s") % @port if $verbose
45
46
  server = Spider::HTTP.const_get(servers[@server_name]).new
@@ -62,6 +63,10 @@ class WebServerCommand < CmdParse::Command
62
63
  server.shutdown
63
64
  ssl_server.shutdown if ssl_server
64
65
  Spider.shutdown
66
+ begin
67
+ File.unlink(@pid_file)
68
+ rescue Errno::ENOENT
69
+ end
65
70
  }
66
71
  trap('TERM', &do_shutdown)
67
72
  trap('INT', &do_shutdown)
@@ -71,12 +76,11 @@ class WebServerCommand < CmdParse::Command
71
76
  }
72
77
  if (@daemonize)
73
78
  forked = Spider.fork do
74
- File.new(Spider.paths[:var]+'/run/server.pid', w) do |f|
79
+ File.open(@pid_file, 'w') do |f|
75
80
  f.write(Process.pid)
76
81
  end
77
82
  $0 = 'spider-server'
78
83
  start.call
79
- @runner.join
80
84
  end
81
85
  Process.detach(forked)
82
86
  else
@@ -9,43 +9,42 @@ module Spider
9
9
 
10
10
  def setup
11
11
  unless @sessions
12
- @sync ||= Sync.new
12
+ @mutex ||= Mutex.new
13
13
  @sessions ||= Hash.new
14
14
  end
15
15
  super
16
16
  end
17
17
 
18
18
  def []=(sid, data)
19
- @sync.lock(Sync::EX)
20
- @sessions[sid] = {
21
- :data => data,
22
- :mtime => Time.now
19
+ @mutex.synchronize {
20
+ @sessions[sid] = {
21
+ :data => data,
22
+ :mtime => Time.now
23
+ }
23
24
  }
24
- @sync.lock(Sync::UN)
25
25
  end
26
26
 
27
27
  def [](sid)
28
28
  check_purge
29
- @sync.lock(Sync::SH)
30
- sess = @sessions[sid] ? @sessions[sid][:data] : nil
31
- @sync.lock(Sync::UN)
32
- sess
29
+ @mutex.synchronize{
30
+ @sessions[sid] ? @sessions[sid][:data] : nil
31
+ }
33
32
  end
34
33
 
35
34
  def purge(life)
36
- @sync.lock(Sync::EX)
37
- @sessions.each do |sid, session|
38
- if (session[:mtime] + life < Time.now)
39
- @sessions.delete(sid)
35
+ @mutex.synchronize{
36
+ @sessions.each do |sid, session|
37
+ if (session[:mtime] + life < Time.now)
38
+ @sessions.delete(sid)
39
+ end
40
40
  end
41
- end
42
- @sync.lock(Sync::UN)
41
+ }
43
42
  end
44
43
 
45
44
  def delete(sid)
46
- @sync.lock(Sync::EX)
47
- @sessions.delete(sid)
48
- @sync.lock(Sync::UN)
45
+ @mutex.synchronize{
46
+ @sessions.delete(sid)
47
+ }
49
48
  end
50
49
 
51
50
  end
@@ -196,6 +196,8 @@ module Spider; module Model
196
196
  parts = attributes[:integrated_from].split('.')
197
197
  attributes[:integrated_from] = @elements[parts[0].to_sym]
198
198
  attributes[:integrated_from_element] = parts[1].to_sym if parts[1]
199
+ elsif (attributes[:integrated_from].is_a?(Symbol))
200
+ attributes[:integrated_from] = @elements[attributes[:integrated_from]]
199
201
  end
200
202
  if (!attributes[:integrated_from_element])
201
203
  attributes[:integrated_from_element] = name
@@ -503,6 +505,11 @@ module Spider; module Model
503
505
  # this will not have the desired effect; remove and redefine the element instead.
504
506
  def self.element_attributes(element_name, attributes)
505
507
  elements[element_name].attributes.merge!(attributes)
508
+ if attributes[:primary_key] && !@primary_keys.include?(elements[element_name])
509
+ @primary_keys << elements[element_name]
510
+ elsif !attributes[:primary_key]
511
+ @primary_keys.delete(elements[element_name])
512
+ end
506
513
  end
507
514
 
508
515
  # Defines a multiple element. Equivalent to calling
@@ -982,6 +989,12 @@ module Spider; module Model
982
989
  end
983
990
  if (values)
984
991
  if (values.is_a? Hash)
992
+ values.keys.select{ |k|
993
+ k = k.name if k.is_a?(Element)
994
+ self.class.elements[k.to_sym] && self.class.elements[k.to_sym].primary_key?
995
+ }.each do |k|
996
+ set!(k, values[k])
997
+ end
985
998
  values.each do |key, val|
986
999
  set!(key, val)
987
1000
  end
@@ -26,13 +26,15 @@ module Spider; module Model
26
26
  def get(model, values)
27
27
  @objects[model] ||= {}
28
28
  pks = {}
29
+ has_pks = false
29
30
  model.primary_keys.each do |k|
30
31
  # dereference integrated primary keys
31
32
  pks[k.name] = (k.integrated? && values[k.integrated_from.name]) ?
32
33
  values[k.integrated_from.name].get(k.integrated_from_element) :
33
34
  values[k.name]
34
- raise IdentityMapperException, "Can't get without all primary keys" unless pks[k.name]
35
+ has_pks = true if pks[k.name]
35
36
  end
37
+ raise IdentityMapperException, "Can't get without all primary keys" unless has_pks
36
38
  pks.extend(HashComparison)
37
39
  obj = (@objects[model][pks] ||= model.new(pks))
38
40
  pks.each{ |k, v| obj.element_loaded(k) }
@@ -111,7 +111,7 @@ module Spider; module Model; module Storage; module Db
111
111
  return value.to_datetime if type == DateTime
112
112
  return value.to_date # FIXME: check what is returned, here we espect an OCI8::Date
113
113
  when 'Spider::DataTypes::Text'
114
- value = value ? value.read : ''
114
+ value = value.read if value.respond_to?(:read)
115
115
  end
116
116
  return super(type, value)
117
117
  end
@@ -245,7 +245,7 @@ module Spider; module Model; module Storage; module Db
245
245
  # end
246
246
  transformed = "O#{replace_cnt += 1}"
247
247
  replaced_fields[field.to_s] = transformed
248
- if (field.type == 'CLOB')
248
+ if (field.is_a?(Spider::Model::Storage::Db::Field) && field.type == 'CLOB')
249
249
  field = "CAST(#{field} as varchar2(100))"
250
250
  end
251
251
  query[:keys] << "#{field} AS #{transformed}"
@@ -17,6 +17,7 @@ module Spider; module Model; module Storage; module Db
17
17
  @retry = Spider.conf.get('storage.db.pool.retry')
18
18
  @connections = []
19
19
  @free_connections = []
20
+ @thread_connections = {}
20
21
  # if Spider.runmode == 'devel'
21
22
  # Thread.new do
22
23
  # loop do
@@ -38,12 +39,16 @@ module Spider; module Model; module Storage; module Db
38
39
  def get_connection
39
40
  Thread.current[:db_connections] ||= {}
40
41
  @connection_mutex.synchronize do
42
+ #Spider.logger.debug("DB Pool (#{Thread.current}): trying to get connection")
41
43
  if conn = Thread.current[:db_connections][@connection_params]
42
- # Spider.logger.debug("DB Pool (#{Thread.current}): returning thread connection #{conn}")
44
+ #Spider.logger.debug("DB Pool (#{Thread.current}): returning thread connection #{conn}")
43
45
  @free_connections.delete(conn)
44
46
  conn
45
47
  else
46
- Thread.current[:db_connections][@connection_params] = _checkout
48
+ conn = _checkout
49
+ Thread.current[:db_connections][@connection_params] = conn
50
+ @thread_connections[Thread.current.object_id] = [conn, Time.now]
51
+ conn
47
52
  end
48
53
  end
49
54
  end
@@ -56,10 +61,11 @@ module Spider; module Model; module Storage; module Db
56
61
 
57
62
  def release(conn)
58
63
  @connection_mutex.synchronize do
59
- # Spider.logger.debug("DB Pool (#{Thread.current}): releasing #{conn}")
64
+ #Spider.logger.debug("DB Pool (#{Thread.current}): releasing #{conn}")
60
65
  @free_connections << conn
61
- @queue.signal
62
66
  Thread.current[:db_connections].delete(@connection_params)
67
+ @thread_connections.delete(Thread.current.object_id)
68
+ @queue.signal
63
69
  end
64
70
  end
65
71
 
@@ -79,6 +85,9 @@ module Spider; module Model; module Storage; module Db
79
85
 
80
86
  private
81
87
 
88
+ def _release
89
+ end
90
+
82
91
  def _checkout
83
92
  # Spider.logger.debug("DB Pool (#{Thread.current}): checkout (max: #{@max_size})")
84
93
  1.upto(@retry) do
@@ -89,18 +98,54 @@ module Spider; module Model; module Storage; module Db
89
98
  else
90
99
  Spider.logger.debug "#{Thread.current} WAITING FOR CONNECTION, #{@queue.count_waiters} IN QUEUE"
91
100
  unless @queue.wait(@timeout)
92
- raise StorageException, "Unable to get a db connection in #{@timeout} seconds" if @timeout
101
+ clear_stale_connections
102
+ create_new_connection if @free_connections.empty? && @connections.length < @max_size
103
+ if @free_connections.empty?
104
+ Spider.logger.error "#{Thread.current} GOT TIRED WAITING, #{@queue.count_waiters} IN QUEUE"
105
+ raise StorageException, "Unable to get a db connection in #{@timeout} seconds" if @timeout
106
+ end
93
107
  end
94
108
  end
95
109
  else
96
110
  # Spider.logger.debug("DB Pool (#{Thread.current}): had free connection")
97
111
  end
98
112
  conn = @free_connections.pop
99
- if @provider.connection_alive?(conn)
100
- # Spider.logger.debug("DB Pool (#{Thread.current}): returning #{conn} (#{@free_connections.length} free)")
113
+ while conn && !@provider.connection_alive?(conn)
114
+ Spider.logger.warn("DB Pool (#{Thread.current}): connection #{conn} dead")
115
+ remove_connection(conn)
116
+ conn = nil
117
+ conn = @free_connections.pop unless @free_connections.empty?
118
+ end
119
+ if conn
120
+ #Spider.logger.debug("DB Pool (#{Thread.current}): returning #{conn} (#{@free_connections.length} free)")
101
121
  return conn
102
- else
103
- remove(conn)
122
+ end
123
+ end
124
+ raise StorageException, "#{Thread.current} unable to get a connection after #{@retry} retries."
125
+ end
126
+
127
+ def clear_stale_connections
128
+ @connection_mutex.synchronize do
129
+ keys = Set.new(@thread_connections.keys)
130
+ Thread.list.each do |thread|
131
+ keys.delete(thread.object_id) if thread.alive?
132
+ end
133
+ keys.each do |thread_id|
134
+ conn, time = @thread_connections[thread_id]
135
+ Spider.logger.error("Thread #{thread_id} died without releasing connection #{conn} (acquired at #{time})")
136
+ if @provider.connection_alive?(conn)
137
+ @free_connections << conn
138
+ else
139
+ remove_connection(conn)
140
+ end
141
+ @thread_connections.delete(thread_id)
142
+ end
143
+ @thread_connections.each do |thread_id, conn_data|
144
+ conn, time = conn_data
145
+ diff = Time.now - time
146
+ if diff > 60
147
+ Spider.logger.warn("Thread #{thread_id} has been holding connection #{conn} for #{diff} seconds.")
148
+ end
104
149
  end
105
150
  end
106
151
  end
data/spider.gemspec CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "spiderfw"
5
- s.version = "0.5.3"
5
+ s.version = "0.5.4"
6
6
  s.date = "2010-02-18"
7
7
  s.summary = "A (web) framework"
8
8
  s.email = "abmajor7@gmail.com"
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.default_executable = 'spider'
29
29
  s.add_dependency("cmdparse", ["> 2.0.0"])
30
30
  s.add_dependency("gettext", ["> 2.0.0"])
31
- s.add_dependency("hpricot", ["> 0.8"])
31
+ s.add_dependency("hpricot", ["= 0.6"])
32
32
  s.add_dependency("json", ["> 1.1"])
33
33
  s.add_dependency("uuid", ["> 2.0"])
34
34
  s.add_dependency("rufus-scheduler", ["> 1.0"])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spiderfw
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Pirlik
@@ -38,9 +38,9 @@ dependencies:
38
38
  version_requirement:
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - ">"
41
+ - - "="
42
42
  - !ruby/object:Gem::Version
43
- version: "0.8"
43
+ version: "0.6"
44
44
  version:
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: json
@@ -367,6 +367,9 @@ files:
367
367
  - apps/core/components/public/js/jquery/jquery-ui/index.html
368
368
  - apps/core/components/public/js/jquery/jquery-ui/js/jquery-1.3.2.min.js
369
369
  - apps/core/components/public/js/jquery/jquery-ui/js/jquery-ui-1.7.2.custom.min.js
370
+ - apps/core/components/public/js/jquery/plugins/asmselect/jquery.asmselect.css
371
+ - apps/core/components/public/js/jquery/plugins/asmselect/jquery.asmselect.js
372
+ - apps/core/components/public/js/jquery/plugins/autogrow/jquery.autogrow.js
370
373
  - apps/core/components/public/js/jquery/plugins/jquery-autocomplete/changelog.txt
371
374
  - apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.css
372
375
  - apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.js
@@ -511,6 +514,7 @@ files:
511
514
  - apps/core/forms/public/input.js
512
515
  - apps/core/forms/public/search_select.js
513
516
  - apps/core/forms/public/select.js
517
+ - apps/core/forms/public/text_area.js
514
518
  - apps/core/forms/tags/element_label.erb
515
519
  - apps/core/forms/tags/element_row.erb
516
520
  - apps/core/forms/tags/row.erb