spiderfw 0.5.10 → 0.5.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/CHANGELOG +6 -0
  2. data/Rakefile +4 -1
  3. data/VERSION +1 -0
  4. data/apps/core/auth/controllers/login_controller.rb +10 -1
  5. data/apps/core/auth/controllers/mixins/auth_helper.rb +4 -2
  6. data/apps/core/auth/views/login.shtml +1 -1
  7. data/apps/core/components/_init.rb +1 -1
  8. data/apps/core/components/public/css/spider.css +0 -1
  9. data/apps/core/components/public/js/jquery/plugins/jquery.form.js +2 -1
  10. data/apps/core/components/public/js/list.js +14 -126
  11. data/apps/core/components/public/js/plugins/plugin.js +7 -0
  12. data/apps/core/components/public/js/plugins/sortable.js +124 -0
  13. data/apps/core/components/public/js/spider.js +212 -51
  14. data/apps/core/components/widgets/list/list.shtml +1 -0
  15. data/apps/core/components/widgets/table/table.rb +1 -1
  16. data/apps/core/components/widgets/table/table.shtml +3 -3
  17. data/apps/core/components/widgets/tabs/tabs.rb +70 -22
  18. data/apps/core/components/widgets/tabs/tabs.shtml +8 -2
  19. data/apps/core/forms/widgets/inputs/file_input/file_input.rb +4 -1
  20. data/data/locale/it/LC_MESSAGES/cms.mo +0 -0
  21. data/data/locale/it/LC_MESSAGES/spider.mo +0 -0
  22. data/data/locale/it/LC_MESSAGES/spider_files.mo +0 -0
  23. data/data/locale/it/LC_MESSAGES/spider_images.mo +0 -0
  24. data/lib/spiderfw.rb +3 -1
  25. data/lib/spiderfw/config/configuration.rb +3 -1
  26. data/lib/spiderfw/controller/controller.rb +8 -0
  27. data/lib/spiderfw/controller/mixins/static_content.rb +14 -2
  28. data/lib/spiderfw/controller/mixins/visual.rb +25 -25
  29. data/lib/spiderfw/controller/page_controller.rb +3 -0
  30. data/lib/spiderfw/controller/request.rb +4 -1
  31. data/lib/spiderfw/controller/session.rb +7 -6
  32. data/lib/spiderfw/create.rb +10 -1
  33. data/lib/spiderfw/model/base_model.rb +104 -57
  34. data/lib/spiderfw/model/condition.rb +9 -1
  35. data/lib/spiderfw/model/data_type.rb +15 -0
  36. data/lib/spiderfw/model/datatypes/uuid.rb +5 -0
  37. data/lib/spiderfw/model/extended_models/managed.rb +2 -2
  38. data/lib/spiderfw/model/mappers/db_mapper.rb +46 -21
  39. data/lib/spiderfw/model/mappers/mapper.rb +34 -8
  40. data/lib/spiderfw/model/mixins/list.rb +2 -0
  41. data/lib/spiderfw/model/mixins/tree.rb +2 -1
  42. data/lib/spiderfw/model/mixins/versioned.rb +12 -9
  43. data/lib/spiderfw/model/model.rb +72 -0
  44. data/lib/spiderfw/model/query_set.rb +7 -0
  45. data/lib/spiderfw/model/storage/base_storage.rb +5 -1
  46. data/lib/spiderfw/model/storage/db/adapters/mysql.rb +5 -2
  47. data/lib/spiderfw/model/storage/db/adapters/oci8.rb +9 -3
  48. data/lib/spiderfw/model/storage/db/db_storage.rb +8 -5
  49. data/lib/spiderfw/model/sync.rb +12 -6
  50. data/lib/spiderfw/requires.rb +1 -0
  51. data/lib/spiderfw/templates/blocks/parent_context.rb +26 -0
  52. data/lib/spiderfw/templates/blocks/widget.rb +17 -7
  53. data/lib/spiderfw/templates/layout.rb +11 -1
  54. data/lib/spiderfw/templates/template.rb +36 -3
  55. data/lib/spiderfw/templates/template_blocks.rb +36 -26
  56. data/lib/spiderfw/utils/annotations.rb +2 -1
  57. data/lib/spiderfw/utils/thread_out.rb +15 -0
  58. data/lib/spiderfw/widget/widget.rb +58 -16
  59. data/spider.gemspec +3 -0
  60. metadata +143 -48
data/CHANGELOG ADDED
@@ -0,0 +1,6 @@
1
+ = 0.5.11
2
+ === 15 June, 2010
3
+ * Many bugfixes
4
+ * Javascript api enhancements, JS plugins
5
+ * Minor Visual refactoring
6
+ * Autogenerated DataTypes
data/Rakefile CHANGED
@@ -5,7 +5,10 @@ require 'pathname'
5
5
  def check_app_path(full, partial)
6
6
  p = Pathname.new(full)
7
7
  rel = p.relative_path_from(Pathname.new(Spider.paths[:core_apps]))
8
- return rel.to_s == partial
8
+ return true if rel.to_s == partial
9
+ rel = p.relative_path_from(Pathname.new(Spider.paths[:apps]))
10
+ return true if rel.to_s == partial
11
+ return false
9
12
  end
10
13
 
11
14
  desc "Update pot/po files. To update a single app, call rake updatepo[app_relative_path], where app_relative_path is the path relative to the apps folder (or 'spider')."
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.11
@@ -10,6 +10,10 @@ module Spider; module Auth
10
10
  nil
11
11
  end
12
12
 
13
+ def self.logout_redirect
14
+ nil
15
+ end
16
+
13
17
  def self.users=(val)
14
18
  @user_classes = val
15
19
  end
@@ -85,7 +89,12 @@ module Spider; module Auth
85
89
  def logout
86
90
  @request.session[:auth] = nil
87
91
  @scene.did_logout = true
88
- render('login')
92
+ red = self.class.logout_redirect
93
+ if red
94
+ redirect(red)
95
+ else
96
+ redirect('index')
97
+ end
89
98
  end
90
99
 
91
100
  end
@@ -11,7 +11,9 @@ module Spider; module Auth
11
11
 
12
12
  def before(action='', *arguments)
13
13
  @request.extend(RequestMethods)
14
- return super if action.index(Spider::Auth.route_url) == 0
14
+ super
15
+ return if action.index(Spider::Auth.route_url) == 0
16
+ return if respond_to?(:serving_static?) && serving_static?
15
17
  self.class.auth_require_users.each do |req|
16
18
  klasses, params = req
17
19
  klasses = [klasses] unless klasses.is_a?(Array)
@@ -64,7 +66,7 @@ module Spider; module Auth
64
66
  end
65
67
  @request.user = user
66
68
  end
67
- super
69
+
68
70
  end
69
71
 
70
72
  def try_rescue(exc)
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="login-page">
2
2
  <div class='error' sp:if='@failed_login'>
3
3
  Login errata
4
4
  </div>
@@ -16,7 +16,7 @@ require 'apps/core/components/widgets/crud/crud'
16
16
  require 'apps/core/components/widgets/menu/menu'
17
17
  require 'apps/core/components/widgets/admin/admin'
18
18
  require 'apps/core/components/widgets/confirm/confirm'
19
- # require 'apps/core/components/widgets/tabs/tabs'
19
+ require 'apps/core/components/widgets/tabs/tabs'
20
20
  require 'apps/core/components/widgets/list/list'
21
21
  require 'apps/core/components/widgets/switcher/switcher'
22
22
  require 'apps/core/components/widgets/month_calendar/month_calendar'
@@ -1,5 +1,4 @@
1
1
  .loading-empty{
2
- height: 40px;
3
2
  background: url(img/ajax-loader.gif) no-repeat 10px 10px;
4
3
  }
5
4
 
@@ -373,8 +373,9 @@ $.fn.ajaxSubmit = function(options) {
373
373
  * the form itself.
374
374
  */
375
375
  $.fn.ajaxForm = function(options) {
376
- return this.ajaxFormUnbind().bind('submit.form-plugin', function() {
376
+ return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
377
377
  $(this).ajaxSubmit(options);
378
+ e.stopPropagation();
378
379
  return false;
379
380
  }).bind('click.form-plugin', function(e) {
380
381
  var $el = $(e.target);
@@ -1,6 +1,8 @@
1
1
  Spider.defineWidget('Spider.Components.List', {
2
2
 
3
3
  autoInit: '.wdgt-Spider-Components-List',
4
+
5
+ includePlugins: [Spider.Sortable],
4
6
 
5
7
  startup: function(){
6
8
  },
@@ -12,7 +14,16 @@ Spider.defineWidget('Spider.Components.List', {
12
14
  this.listTagName = this.listEl.get(0).tagName;
13
15
  if (this.el.hasClass('collapsed')) options.collapsed = true;
14
16
  if (this.el.hasClass('tree')) $(this.listTagName, this.el).treeview(options);
15
- if (this.el.hasClass('sortable')) this.makeSortable();
17
+ if (this.el.hasClass('sortable')){
18
+ this.plugin(Spider.Sortable);
19
+ this.makeSortable({
20
+ listEl: this.listEl,
21
+ handle: '> span.desc',
22
+ helper: function(e,item) {
23
+ return $("<div class='treeview-helper'>"+item.find("span.desc").html()+"</div>");
24
+ }
25
+ });
26
+ }
16
27
 
17
28
  };
18
29
  this.ajaxify($('form, .paginator a', this.el));
@@ -22,141 +33,18 @@ Spider.defineWidget('Spider.Components.List', {
22
33
  update: function(){
23
34
  },
24
35
 
25
-
26
- makeSortable: function(){
27
- var options = {
28
- items: '>li',
29
- helper: function(e,item) {
30
- return $("<div class='treeview-helper'>"+item.find("span.desc").html()+"</div>");
31
- },
32
- handle: '> span.desc',
33
- update: this.handleSort.bind(this),
34
- receive: this.handleReceive.bind(this)
35
- };
36
- if (this.el.hasClass('tree')){
37
- options = $.extend(options, {
38
- //revert: true,
39
- sortIndication: {
40
- down: function(item) {
41
- item.before($('<li id="list-sort-indicator" />'));
42
- },
43
- up: function(item) {
44
- item.after($('<li id="list-sort-indicator" />'));
45
- },
46
- remove: function(item) {
47
- $('#list-sort-indicator').remove();
48
- }
49
- },
50
- start: function(e, ui) {
51
- console.log("Tree start:");
52
- console.log(e);
53
- console.log(ui);
54
- // ui.instance.element.treeview({update: ui.item});
55
- },
56
- update: this.handleTreeUpdate.bind(this)
57
- });
58
- this.listEl.sortableTree(options);
59
- // handles drops on non-subtrees nodes
60
- // debugger;
61
- $('.desc', this.el).droppable({
62
- accept: "li",
63
- hoverClass: "drop",
64
- tolerance: "pointer",
65
- // greedy: true,
66
- drop: this.handleTreeDrop.bind(this)
67
- // over: function(e,ui) {
68
- // ui.helper.css("outline", "1px dotted green");
69
- // },
70
- // out: function(e,ui) {
71
- // ui.helper.css("outline", "1px dotted red");
72
- // }
73
- });
74
- }
75
- else{
76
- this.listEl.sortable(options);
77
- }
78
- },
79
-
80
- handleSort: function(e, ui){
81
- if (ui.sender) return; // handled by handleReceive
82
- var item = ui.item;
83
- var pos = this.findLiPosition(item);
84
- if (pos == -1) return;
85
- if (this.listEl.data('sortable').fromOutside){ // hack to work around strange jquery ui behaviour...
86
- return this.acceptFromSender(null, ui.item, pos);
87
- }
88
- this.remote('sort', this.getSortItemId(item), pos);
89
- },
90
-
91
-
92
- handleReceive: function(e, ui){
93
- if (ui.sender == ui.item){
94
- // the item is received from a draggable, not from a list. For some reason the receiver is not
95
- // yet ready to find the position; will call acceptFromSender from handleSort.
96
- return;
97
- }
98
- var pos = this.findLiPosition(ui.item);
99
- return this.acceptFromSender(ui.sender, ui.item, pos);
100
- },
101
-
102
- handleTreeUpdate: function(e, ui){
103
- var parentId = this.getItemId(ui.item.parents('li.tree').eq(0));
104
- var prevId = this.getItemId(ui.item.prev('li.tree'));
105
- this.remote('tree_sort', this.getItemId(ui.item), parentId, prevId);
106
- },
107
-
108
- handleTreeDrop: function(e, ui){
109
- if (e.target.parentNode == ui.draggable[0]) return false; //dropped over itself
110
- console.log('dropped inside');
111
- var dropLi = $(e.target.parentNode);
112
- var subUl = $("> "+this.listTagName, dropLi);
113
- if (subUl.length == 0){
114
- subUl = $("<"+this.listTagName+" />").appendTo(dropLi);
115
- this.el.treeview({add: e.target.parentNode});
116
- }
117
- subUl.append(ui.draggable);
118
- var parentId = this.getItemId($(e.target).parents('li.tree').eq(0));
119
- var prevId = null;
120
- this.remote('tree_sort', this.getItemId(ui.draggable), parentId, prevId);
121
- return false;
122
- },
123
-
124
- acceptFromSender: function(sender, item, pos){
125
- console.error("Accept from sender must be implemented by the widget instance");
126
- },
127
-
128
- getItemId: function(li){
129
- return $('> .dataobject-key', li).text();
130
- },
131
-
132
36
  getItemById: function(id){
133
37
  var found = null;
134
38
  var self = this;
135
39
  $('>li', this.listEl).each(function(){
136
- if (self.getItemId(this) == id){
40
+ $this = $(this);
41
+ if ($this.dataObjectKey == id){
137
42
  found = $(this);
138
43
  return false;
139
44
  }
140
45
  });
141
46
  return found;
142
47
  },
143
-
144
- getSortItemId: function(li){
145
- var k = $('> .sort-key', li);
146
- if (k.length > 0) return k.text();
147
- return this.getItemId(li);
148
- },
149
-
150
- findLiPosition: function(item){
151
- var cnt = 1;
152
- var li = $('> li', this.listEl);
153
- li.each(function(){
154
- if (this == item.get(0)) return false;
155
- cnt++;
156
- });
157
- if (cnt > li.length) return -1; // the row was dropped outside
158
- return cnt;
159
- },
160
48
 
161
49
  sortResult: function(res){
162
50
  console.log(res);
@@ -0,0 +1,7 @@
1
+ Spider.Plugin = Class.extend({
2
+
3
+ init: function(widget){
4
+ this.widget = widget;
5
+ }
6
+
7
+ });
@@ -0,0 +1,124 @@
1
+ Spider.Sortable = Spider.Plugin.extend({
2
+
3
+
4
+ makeSortable: function(options){
5
+ var options = $.extend({
6
+ listSelector: 'ul',
7
+ items: '>li',
8
+ update: this.handleSort.bind(this),
9
+ receive: this.handleReceive.bind(this)
10
+ }, options);
11
+ this.listEl = options.listEl;
12
+ if (!this.listEl) this.listEl = $(options.listSelector, this.el);
13
+ if (this.el.hasClass('tree')){
14
+ options = $.extend(options, {
15
+ //revert: true,
16
+ sortIndication: {
17
+ down: function(item) {
18
+ item.before($('<li id="list-sort-indicator" />'));
19
+ },
20
+ up: function(item) {
21
+ item.after($('<li id="list-sort-indicator" />'));
22
+ },
23
+ remove: function(item) {
24
+ $('#list-sort-indicator').remove();
25
+ }
26
+ },
27
+ start: function(e, ui) {
28
+ console.log("Tree start:");
29
+ console.log(e);
30
+ console.log(ui);
31
+ // ui.instance.element.treeview({update: ui.item});
32
+ },
33
+ update: this.handleTreeUpdate.bind(this)
34
+ });
35
+ this.listEl.sortableTree(options);
36
+ // handles drops on non-subtrees nodes
37
+ // debugger;
38
+ $('.desc', this.el).droppable({
39
+ accept: "li",
40
+ hoverClass: "drop",
41
+ tolerance: "pointer",
42
+ // greedy: true,
43
+ drop: this.handleTreeDrop.bind(this)
44
+ // over: function(e,ui) {
45
+ // ui.helper.css("outline", "1px dotted green");
46
+ // },
47
+ // out: function(e,ui) {
48
+ // ui.helper.css("outline", "1px dotted red");
49
+ // }
50
+ });
51
+ }
52
+ else{
53
+ this.listEl.sortable(options);
54
+ }
55
+ },
56
+
57
+
58
+
59
+ handleSort: function(e, ui){
60
+ if (ui.sender) return; // handled by handleReceive
61
+ var item = ui.item;
62
+ var pos = this.findLiPosition(item);
63
+ if (pos == -1) return;
64
+ if (this.listEl.data('sortable').fromOutside){ // hack to work around strange jquery ui behaviour...
65
+ return this.acceptFromSender(null, ui.item, pos);
66
+ }
67
+ this.remote('sort', this.getSortItemId(item), pos);
68
+ },
69
+
70
+
71
+ handleReceive: function(e, ui){
72
+ if (ui.sender == ui.item){
73
+ // the item is received from a draggable, not from a list. For some reason the receiver is not
74
+ // yet ready to find the position; will call acceptFromSender from handleSort.
75
+ return;
76
+ }
77
+ var pos = this.findLiPosition(ui.item);
78
+ return this.acceptFromSender(ui.sender, ui.item, pos);
79
+ },
80
+
81
+ handleTreeUpdate: function(e, ui){
82
+ var parentId = ui.item.parents('li.tree').eq(0).dataObjectKey();
83
+ var prevId = ui.item.prev('li.tree').dataObjectKey();
84
+ this.remote('tree_sort', this.getItemId(ui.item), parentId, prevId);
85
+ },
86
+
87
+ handleTreeDrop: function(e, ui){
88
+ if (e.target.parentNode == ui.draggable[0]) return false; //dropped over itself
89
+ console.log('dropped inside');
90
+ var dropLi = $(e.target.parentNode);
91
+ var subUl = $("> "+this.listTagName, dropLi);
92
+ if (subUl.length == 0){
93
+ subUl = $("<"+this.listTagName+" />").appendTo(dropLi);
94
+ this.el.treeview({add: e.target.parentNode});
95
+ }
96
+ subUl.append(ui.draggable);
97
+ var parentId = $(e.target).parents('li.tree').eq(0).dataObjectKey();
98
+ var prevId = null;
99
+ this.remote('tree_sort', ui.draggable.dataObjectKey(), parentId, prevId);
100
+ return false;
101
+ },
102
+
103
+ acceptFromSender: function(sender, item, pos){
104
+ console.error("Accept from sender must be implemented by the widget instance");
105
+ },
106
+
107
+ findLiPosition: function(item){
108
+ var cnt = 1;
109
+ var li = $('> li', this.listEl);
110
+ li.each(function(){
111
+ if (this == item.get(0)) return false;
112
+ cnt++;
113
+ });
114
+ if (cnt > li.length) return -1; // the row was dropped outside
115
+ return cnt;
116
+ },
117
+
118
+ getSortItemId: function(li){
119
+ var k = $('> .sort-key', li);
120
+ if (k.length > 0) return k.text();
121
+ return li.dataObjectKey();
122
+ }
123
+
124
+ });
@@ -7,7 +7,7 @@ function $W(path){
7
7
  if (Spider.widgets[path]) return Spider.widgets[path];
8
8
  var wdgt_id = path.replace(/\//g, '-');
9
9
  var wdgt = $('#'+wdgt_id);
10
- if (!wdgt) return null;
10
+ if (wdgt.length == 0) return null;
11
11
  return Spider.Widget.initFromEl(wdgt);
12
12
  }
13
13
 
@@ -23,6 +23,8 @@ Spider.Widget = Class.extend({
23
23
  init: function(container, path, config){
24
24
  this.el = container;
25
25
  this.path = path;
26
+ var pathParts = path.split('/');
27
+ this.widgetId = pathParts[pathParts.length - 1];
26
28
  this.backend = new Spider.WidgetBackend(this);
27
29
  this.readyFunctions = [];
28
30
  config = $.extend({}, config);
@@ -30,15 +32,26 @@ Spider.Widget = Class.extend({
30
32
  this.model = config.model;
31
33
  Spider.widgets[path] = this;
32
34
  this.events = [];
35
+ this.onWidgetCallbacks = {};
36
+ this.widgets = {};
37
+ this.findWidgets();
33
38
  this.startup();
34
39
  this.ready();
35
40
  this.applyReady();
41
+ this.plugins = [];
42
+ if (this.includePlugins) for (var i=0; i<this.includePlugins.length; i++){
43
+ this.plugin(this.includePlugins[i]);
44
+ }
36
45
  },
37
46
 
38
47
  remote: function(){
39
48
  var args = Array.prototype.slice.call(arguments);
40
49
  var method = args.shift();
41
- return this.backend.send(method, args);
50
+ var options = {};
51
+ if ($.isFunction(args[args.length-1])){
52
+ options.callback = args.pop();
53
+ }
54
+ return this.backend.send(method, args, options);
42
55
  },
43
56
 
44
57
  onReady: function(callback){
@@ -53,9 +66,17 @@ Spider.Widget = Class.extend({
53
66
  },
54
67
 
55
68
  reload: function(params, callback){
69
+ if (!callback && $.isFunction(params)){
70
+ callback = params;
71
+ params = {};
72
+ }
56
73
  $C.loadWidget(this.path, params, callback);
57
74
  },
58
75
 
76
+ isLoaded: function(){
77
+ return !this.el.is(':empty');
78
+ },
79
+
59
80
  startup: function(){},
60
81
  ready: function(){},
61
82
  update: function(){},
@@ -63,6 +84,7 @@ Spider.Widget = Class.extend({
63
84
  replaceHTML: function(html){
64
85
  var el = $(html);
65
86
  this.el.html(el.html());
87
+ this.findWidgets();
66
88
  this.update();
67
89
  this.ready();
68
90
  Spider.newHTML(this.el);
@@ -72,11 +94,34 @@ Spider.Widget = Class.extend({
72
94
 
73
95
  replaceEl: function(el){
74
96
  this.el = el;
97
+ this.findWidgets();
75
98
  this.update();
76
99
  this.ready();
77
100
  this.applyReady();
78
101
  },
79
102
 
103
+ findWidgets: function(){
104
+ var self = this;
105
+ $('.widget', this.el).filter(function(index){
106
+ if ($(this).parents('.widget').get(0) == self.el.get(0)) return true;
107
+ return false;
108
+ }).each(function(){
109
+ var $this = $(this);
110
+ var w = $this.spiderWidget();
111
+ if (!self.widgets[w.widgetId]) self.addWidget(w.widgetId, w);
112
+ else self.widgets[w.widgetId].replaceEl($this);
113
+ });
114
+ },
115
+
116
+ addWidget: function(id, w){
117
+ this.widgets[id] = w;
118
+ if (this.onWidgetCallbacks[id]){
119
+ for (var i=0; i<this.onWidgetCallbacks[id].length; i++){
120
+ this.onWidgetCallbacks[id][i].call(this, w);
121
+ }
122
+ }
123
+ },
124
+
80
125
  paramName: function(key){
81
126
  var pathParts = this.path.split('/');
82
127
  var param = "_w";
@@ -91,69 +136,111 @@ Spider.Widget = Class.extend({
91
136
  },
92
137
 
93
138
 
139
+ ajaxifyAll: function(options){
140
+ var els = $('form:not(.ajaxified), a:not(.ajaxified)', this.el);
141
+ if (!options) options = {};
142
+ if (options.filter) els = els.filter(options.filter);
143
+ if (options.not) els = els.not(options.not);
144
+ this.ajaxify(els, options);
145
+ },
146
+
147
+ findWidgetsAjaxifiable: function(options){
148
+ var selfEl = this.el.get(0);
149
+ return $('form:not(.ajaxified), a:not(.ajaxified)', this.el).filter(function(index){
150
+ var p = $(this).parent();
151
+ while (p){
152
+ if (p.is('.widget')){
153
+ if (p.get(0) == selfEl) return true;
154
+ return false;
155
+ }
156
+ p = p.parent();
157
+ }
158
+ return false;
159
+ });
160
+ },
161
+
162
+
94
163
  ajaxify: function(el, options){
95
164
  var w = this;
165
+ if (!el || !el.eq){
166
+ options = el;
167
+ el = this.findWidgetsAjaxifiable();
168
+ }
96
169
  if (!options) options = {};
97
170
  el.each(function(){
98
171
  var $this = $(this);
99
172
  if (this.tagName == 'FORM'){
100
- w.ajaxifyForm($(this));
173
+ w.ajaxifyForm($(this), options);
101
174
  }
102
175
  else if (this.tagName == 'A'){
103
- $this.click(function(e){
104
- if (options.before){
105
- var res = options.before.apply(this);
106
- if (res === false) return false ;
107
- }
108
- e.preventDefault();
109
- var a = $(e.target);
110
- var url = $(this).attr('href');
111
- var parts = url.split('?');
112
- url = parts[0]; //+'.json';
113
- url += '?';
114
- if (parts[1]) url += parts[1]+'&';
115
- url += '_wt='+w.path;
116
- w.setLoading();
117
- $.ajax({
118
- url: url,
119
- type: 'GET',
120
- dataType: 'html',
121
- success: function(res){
122
- w.replaceHTML(res);
123
- w.removeLoading();
124
- }
125
- });
126
- });
176
+ w.ajaxifyLink($(this), options);
127
177
  }
128
178
  });
129
179
 
130
180
  },
131
181
 
132
- ajaxifyForm: function(form){
133
- var w = this;
182
+ ajaxifyForm: function(form, options){
134
183
  var isForm = form.get(0).tagName == 'FORM';
135
- $('input[type=submit]', form).click(function(e){
184
+ if (!options) options = {};
185
+ $('input[type=submit]', form).addClass('ajaxified').bind('click.ajaxify', function(e){
186
+ var $this = $(this);
187
+ var w = $this.parentWidget();
136
188
  e.preventDefault();
137
189
  w.setLoading();
138
- var submitName = $(this).attr('name');
139
- var submitValue = $(this).val();
190
+ var submitName = $this.attr('name');
191
+ var submitValue = $this.val();
140
192
  form.ajaxSubmit({
141
193
  dataType: 'html',
142
194
  semantic: !isForm,
143
195
  beforeSubmit: function(data, form, options){
144
196
  data.push({name: submitName, value: submitValue});
145
197
  data.push({name: '_wt', value: w.path});
198
+ if (options.before) options.before();
146
199
  },
147
200
  success: function(res){
148
201
  w.replaceHTML(res);
149
202
  w.removeLoading();
203
+ if (options.onLoad) options.onLoad(form);
204
+ w.trigger('ajaxifyLoad', form);
205
+ }
206
+ });
207
+ });
208
+ },
209
+
210
+ ajaxifyLink: function(a, options){
211
+ var w = this;
212
+ if (!options) options = {};
213
+ a.addClass('ajaxified').bind('click.ajaxify', function(e){
214
+ if (options.before){
215
+ var res = options.before.apply(w);
216
+ if (res === false) return false ;
217
+ }
218
+ e.preventDefault();
219
+ var a = $(e.target);
220
+ var url = $(this).attr('href');
221
+ var parts = url.split('?');
222
+ url = parts[0]; //+'.json';
223
+ url += '?';
224
+ if (parts[1]) url += parts[1]+'&';
225
+ url += '_wt='+w.path;
226
+ if (options.before) options.before();
227
+ w.setLoading();
228
+ $.ajax({
229
+ url: url,
230
+ type: 'GET',
231
+ dataType: 'html',
232
+ success: function(res){
233
+ w.replaceHTML(res);
234
+ w.removeLoading();
235
+ if (options.onLoad) options.onLoad(a);
236
+ w.trigger('ajaxifyLoad', a);
150
237
  }
151
238
  });
152
239
  });
153
240
  },
154
241
 
155
242
  setLoading: function(){
156
- if (this.el.is(':empty')){
243
+ if (this.el.is(':empty') || this.el.children().hasClass('empty-placeholder')){
157
244
  this.el.addClass('loading-empty');
158
245
  }
159
246
  else{
@@ -201,18 +288,73 @@ Spider.Widget = Class.extend({
201
288
  },
202
289
 
203
290
  bind: function(eventName, callback){
204
- if (!this.events[eventName]){
205
- this.events[eventName] = [];
291
+ var handleObj = {
292
+ callback: callback
293
+ };
294
+ if ( eventName.indexOf(".") > -1 ) {
295
+ var namespaces = eventName.split(".");
296
+ eventName = namespaces.shift();
297
+ handleObj.namespace = namespaces.slice(0).sort().join(".");
206
298
  }
207
- this.events[eventName].push(callback);
299
+ if (!this.events[eventName]) this.events[eventName] = [];
300
+ this.events[eventName].push(handleObj);
208
301
  },
209
302
 
303
+ on: function(eventName, callback){ return this.bind(eventName, callback); },
304
+
210
305
  trigger: function(eventName){
306
+ if ( eventName.indexOf(".") > -1 ) {
307
+ var namespaces = eventName.split(".");
308
+ eventName = namespaces.shift();
309
+ namespace = namespaces.slice(0).sort().join(".");
310
+ }
211
311
  if (!this.events[eventName]) return;
212
312
  var args = Array.prototype.slice.call(arguments, 1);
213
313
  for (var i=0; i < this.events[eventName].length; i++){
214
- this.events[eventName][i].apply(this, args);
314
+ this.events[eventName][i].callback.apply(this, args);
315
+ }
316
+ },
317
+
318
+ unbind: function(eventName){
319
+ var namespace = null;
320
+ if ( eventName.indexOf(".") > -1 ) {
321
+ var namespaces = eventName.split(".");
322
+ eventName = namespaces.shift();
323
+ namespace = namespaces.slice(0).sort().join(".");
215
324
  }
325
+ if (namespace){
326
+ for (var i=0; i<this.events[eventName].length; i++){
327
+ if (this.events[eventName][i].namespace == namespace){
328
+ this.events[eventName].splice(i);
329
+ }
330
+ }
331
+ }
332
+ else this.events[eventName] = [];
333
+ },
334
+
335
+ plugin: function(pClass, prop){
336
+ if (prop) pClass = pClass.extend(prop);
337
+ this.plugins[pClass] = new pClass(this);
338
+ var plugin = this.plugins[pClass];
339
+ for (var name in pClass.prototype){
340
+ if (name.substring(0, 1) == '_') continue;
341
+ if (typeof pClass.prototype[name] == "function" && !this[name]){
342
+ this[name] = function(name){
343
+ return function(){
344
+ return plugin[name].apply(this, arguments);
345
+ };
346
+ }(name);
347
+ }
348
+ }
349
+ },
350
+
351
+ widget: function(id){
352
+ return this.widgets[id];
353
+ },
354
+
355
+ onWidget: function(id, callback){
356
+ if (!this.onWidgetCallbacks[id]) this.onWidgetCallbacks[id] = [];
357
+ this.onWidgetCallbacks[id].push(callback);
216
358
  }
217
359
 
218
360
 
@@ -276,20 +418,29 @@ Spider.WidgetBackend = Class.extend({
276
418
  },
277
419
 
278
420
  send: function(method, args, options){
279
- var url = this.urlForMethod(method);
280
- for (var i=0; i<args.length; i++){
281
- url += '&_wp[]='+args[i];
282
- }
283
- var data = {};
284
- var callback = this.widget[method+'_response'];
285
- if (!callback) callback = function(){};
421
+ if (!options) options = {};
286
422
  var defaults = {
287
- url: url,
288
423
  type: 'POST',
289
- success: callback,
290
- data: data
424
+ dataType: 'json'
291
425
  };
292
426
  options = $.extend(defaults, options);
427
+ if (!options.format) options.format = options.dataType;
428
+ var url = this.baseUrl;
429
+ var data = {};
430
+ if ($.isPlainObject(args[0])) data = args[0];
431
+ else data = {'_wp': args};
432
+ $.extend(data, {
433
+ '_wt': this.widget.path,
434
+ '_we': method,
435
+ '_wf': options.format
436
+ });
437
+ var callback = this.widget[method+'_response'];
438
+ if (!callback) callback = options.callback;
439
+ if (!callback) callback = function(){};
440
+ delete(options.callback);
441
+ options.success = callback;
442
+ options.data = data;
443
+ options.url = url;
293
444
  $.ajax(options);
294
445
  }
295
446
 
@@ -327,6 +478,7 @@ Spider.defineWidget = function(name, parent, w){
327
478
  });
328
479
  });
329
480
  }
481
+ return widget;
330
482
  };
331
483
 
332
484
  Spider.Controller = Class.extend({
@@ -335,8 +487,14 @@ Spider.Controller = Class.extend({
335
487
  var url = ''+document.location;
336
488
  var slashPos = url.lastIndexOf('/');
337
489
  url = url.substr(0, slashPos);
338
- this.url = url;
490
+ this.setUrl(url);
339
491
  },
492
+
493
+ setUrl: function(url){
494
+ this.url = url;
495
+ this.publicUrl = this.url+'/public'; // FIXME
496
+ this.homeUrl = this.url+'/_h';
497
+ },
340
498
 
341
499
  remote: function(method, params, callback, options){
342
500
  var args = Array.prototype.slice.call(arguments);
@@ -345,8 +503,9 @@ Spider.Controller = Class.extend({
345
503
  var defaults = {
346
504
  url: url,
347
505
  type: 'POST',
348
- complete: callback,
349
- data: params
506
+ success: callback,
507
+ data: params,
508
+ dataType: 'json'
350
509
  };
351
510
  options = $.extend(defaults, options);
352
511
  $.ajax(options);
@@ -431,7 +590,9 @@ $(document).ready(function(){
431
590
 
432
591
  $.fn.spiderWidget = function(){
433
592
  if (!this.attr('id')) return;
434
- return $W(Spider.Widget.pathFromId(this.attr('id')));
593
+ var path = Spider.Widget.pathFromId(this.attr('id'));
594
+ if (Spider.widgets[path]) return Spider.widgets[path];
595
+ return Spider.Widget.initFromEl(this);
435
596
  };
436
597
 
437
598
  $.fn.parentWidget = function(){