radiant-forum-extension 1.2.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/README.md +37 -56
  2. data/VERSION +1 -1
  3. data/app/controllers/forum_base_controller.rb +78 -0
  4. data/app/controllers/forums_controller.rb +5 -34
  5. data/app/controllers/posts_controller.rb +61 -153
  6. data/app/controllers/topics_controller.rb +6 -63
  7. data/app/helpers/forum_helper.rb +24 -48
  8. data/app/models/forum.rb +5 -18
  9. data/app/models/post.rb +53 -55
  10. data/app/models/post_attachment.rb +9 -6
  11. data/app/models/topic.rb +20 -113
  12. data/app/views/admin/reader_configuration/_edit_forum.html.haml +4 -2
  13. data/app/views/admin/reader_configuration/_forum.html.haml +6 -2
  14. data/app/views/forums/_forum.html.haml +14 -20
  15. data/app/views/forums/_latest.html.haml +12 -0
  16. data/app/views/forums/_standard_parts.html.haml +49 -0
  17. data/app/views/forums/index.html.haml +16 -24
  18. data/app/views/forums/show.html.haml +18 -28
  19. data/app/views/pages/_comment.html.haml +1 -1
  20. data/app/views/posts/_attachments.html.haml +13 -0
  21. data/app/views/posts/_confirm_delete.html.haml +10 -0
  22. data/app/views/posts/_context.html.haml +16 -0
  23. data/app/views/posts/_edit_links.html.haml +11 -0
  24. data/app/views/posts/_form.html.haml +40 -2
  25. data/app/views/posts/_latest.html.haml +10 -17
  26. data/app/views/posts/_new_attachment.html.haml +2 -0
  27. data/app/views/posts/_post.html.haml +25 -60
  28. data/app/views/posts/{_search.html.haml → _search_form.html.haml} +1 -1
  29. data/app/views/posts/_uploader.html.haml +2 -2
  30. data/app/views/posts/edit.html.haml +26 -13
  31. data/app/views/posts/index.html.haml +19 -27
  32. data/app/views/posts/new.html.haml +27 -7
  33. data/app/views/posts/remove.html.haml +27 -0
  34. data/app/views/posts/show.html.haml +20 -7
  35. data/app/views/readers/_forum_messages.html.haml +20 -0
  36. data/app/views/readers/_messages_summary.html.haml +3 -0
  37. data/app/views/topics/_context.html.haml +12 -0
  38. data/app/views/topics/_latest.html.haml +14 -16
  39. data/app/views/topics/_locked.html.haml +1 -1
  40. data/app/views/topics/_replies.html.haml +6 -0
  41. data/app/views/topics/_reply.html.haml +23 -0
  42. data/app/views/topics/_topic.html.haml +14 -43
  43. data/app/views/topics/_voices.html.haml +5 -0
  44. data/app/views/topics/index.html.haml +11 -24
  45. data/app/views/topics/show.html.haml +25 -38
  46. data/config/initializers/radiant_config.rb +5 -1
  47. data/config/locales/en.yml +73 -27
  48. data/config/routes.rb +3 -4
  49. data/db/migrate/20101222160900_page_posts.rb +21 -0
  50. data/db/migrate/20101222163605_no_comment_forum.rb +10 -0
  51. data/db/migrate/20110105103827_topic_merely_associative.rb +23 -0
  52. data/db/migrate/20110111080550_detach_observer.rb +11 -0
  53. data/db/migrate/20110127113852_import_attachments.rb +9 -0
  54. data/forum_extension.rb +12 -13
  55. data/lib/commentable_model.rb +98 -0
  56. data/lib/forum_page.rb +2 -22
  57. data/lib/forum_reader_sessions_controller.rb +14 -0
  58. data/lib/forum_readers_controller.rb +2 -9
  59. data/lib/forum_red_cloth3.rb +23 -4
  60. data/lib/forum_red_cloth4.rb +23 -4
  61. data/lib/forum_tags.rb +298 -194
  62. data/lib/sanitize/config/forum.rb +47 -0
  63. data/public/images/furniture/blank.png +0 -0
  64. data/public/images/furniture/emoticons.png +0 -0
  65. data/public/javascripts/forum.js +349 -93
  66. data/public/javascripts/jquery.tools.min.js +195 -0
  67. data/public/punymce/blank.htm +1 -0
  68. data/public/punymce/css/content.css +4 -0
  69. data/public/punymce/css/editor.css +58 -0
  70. data/public/punymce/i18n/sv.js +28 -0
  71. data/public/punymce/img/icons.gif +0 -0
  72. data/public/punymce/img/icons_uncompressed.png +0 -0
  73. data/public/punymce/plugins/bbcode.js +1 -0
  74. data/public/punymce/plugins/bbcode_src.js +50 -0
  75. data/public/punymce/plugins/editsource/css/editor.css +3 -0
  76. data/public/punymce/plugins/editsource/editsource.js +1 -0
  77. data/public/punymce/plugins/editsource/editsource_src.js +81 -0
  78. data/public/punymce/plugins/editsource/img/icons.gif +0 -0
  79. data/public/punymce/plugins/emoticons/css/content.css +13 -0
  80. data/public/punymce/plugins/emoticons/css/editor.css +17 -0
  81. data/public/punymce/plugins/emoticons/emoticons.js +1 -0
  82. data/public/punymce/plugins/emoticons/emoticons_src.js +303 -0
  83. data/public/punymce/plugins/emoticons/img/emoticons.gif +0 -0
  84. data/public/punymce/plugins/emoticons/img/emoticons.png +0 -0
  85. data/public/punymce/plugins/emoticons/img/trans.gif +0 -0
  86. data/public/punymce/plugins/entities.js +1 -0
  87. data/public/punymce/plugins/entities_src.js +37 -0
  88. data/public/punymce/plugins/forceblocks.js +1 -0
  89. data/public/punymce/plugins/forceblocks_src.js +465 -0
  90. data/public/punymce/plugins/forcenl.js +1 -0
  91. data/public/punymce/plugins/forcenl_src.js +26 -0
  92. data/public/punymce/plugins/image/css/editor.css +1 -0
  93. data/public/punymce/plugins/image/image.js +1 -0
  94. data/public/punymce/plugins/image/image_src.js +30 -0
  95. data/public/punymce/plugins/image/img/icons.gif +0 -0
  96. data/public/punymce/plugins/link/css/editor.css +2 -0
  97. data/public/punymce/plugins/link/img/icons.gif +0 -0
  98. data/public/punymce/plugins/link/link.js +1 -0
  99. data/public/punymce/plugins/link/link_src.js +36 -0
  100. data/public/punymce/plugins/paste.js +1 -0
  101. data/public/punymce/plugins/paste_src.js +169 -0
  102. data/public/punymce/plugins/protect.js +1 -0
  103. data/public/punymce/plugins/protect_src.js +30 -0
  104. data/public/punymce/plugins/safari2x.js +1 -0
  105. data/public/punymce/plugins/safari2x_src.js +284 -0
  106. data/public/punymce/plugins/tabfocus.js +1 -0
  107. data/public/punymce/plugins/tabfocus_src.js +45 -0
  108. data/public/punymce/plugins/textcolor/css/editor.css +7 -0
  109. data/public/punymce/plugins/textcolor/img/icons.gif +0 -0
  110. data/public/punymce/plugins/textcolor/textcolor.js +1 -0
  111. data/public/punymce/plugins/textcolor/textcolor_src.js +73 -0
  112. data/public/punymce/puny_mce.js +1 -0
  113. data/public/punymce/puny_mce_full.js +1 -0
  114. data/public/punymce/puny_mce_src.js +1460 -0
  115. data/public/stylesheets/sass/forum.sass +175 -169
  116. data/radiant-forum-extension.gemspec +81 -19
  117. data/spec/controllers/admin/forums_controller_spec.rb +2 -2
  118. data/spec/controllers/forums_controller_spec.rb +3 -6
  119. data/spec/controllers/posts_controller_spec.rb +76 -59
  120. data/spec/controllers/topics_controller_spec.rb +4 -95
  121. data/spec/datasets/forum_readers_dataset.rb +1 -0
  122. data/spec/datasets/forums_dataset.rb +91 -10
  123. data/spec/lib/commentable_model_spec.rb +88 -0
  124. data/spec/lib/forum_page_spec.rb +2 -34
  125. data/spec/lib/forum_site_spec.rb +2 -3
  126. data/spec/lib/forum_tags_spec.rb +35 -0
  127. data/spec/models/forum_spec.rb +31 -20
  128. data/spec/models/post_spec.rb +40 -39
  129. data/spec/models/topic_spec.rb +29 -71
  130. data/spec/spec_helper.rb +10 -1
  131. metadata +84 -22
  132. data/app/views/posts/_reply.html.haml +0 -35
  133. data/app/views/posts/_upload.html.haml +0 -2
  134. data/app/views/posts/preview.html.haml +0 -32
  135. data/app/views/posts/search.html.haml +0 -63
  136. data/app/views/posts/search.rss.builder +0 -14
  137. data/app/views/topics/_form.html.haml +0 -30
  138. data/app/views/topics/_help.html.haml +0 -8
  139. data/app/views/topics/comments.html.haml +0 -6
  140. data/app/views/topics/edit.html.haml +0 -26
  141. data/app/views/topics/new.html.haml +0 -56
  142. data/spec/datasets/forum_pages_dataset.rb +0 -11
  143. data/spec/datasets/posts_dataset.rb +0 -31
  144. data/spec/datasets/topics_dataset.rb +0 -37
@@ -0,0 +1,47 @@
1
+ #--
2
+ # Copyright (c) 2011 Ryan Grove <ryan@wonko.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the 'Software'), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in all
12
+ # copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ # SOFTWARE.
21
+ #++
22
+
23
+ class Sanitize
24
+ module Config
25
+ FORUM = {
26
+ :elements => %w[
27
+ a b blockquote br caption cite code dd del dl dt em h1 h2 h3 h4 h5 h6 i img li ol p pre small span strike strong sub sup u ul
28
+ ],
29
+
30
+ :attributes => {
31
+ :all => ['dir', 'lang', 'title', 'class'],
32
+ 'a' => ['href'],
33
+ 'blockquote' => ['cite'],
34
+ 'img' => ['align', 'alt', 'height', 'src', 'width'],
35
+ 'ol' => ['start', 'reversed', 'type'],
36
+ 'ul' => ['type']
37
+ },
38
+
39
+ :protocols => {
40
+ 'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]},
41
+ 'blockquote' => {'cite' => ['http', 'https', :relative]},
42
+ 'del' => {'cite' => ['http', 'https', :relative]},
43
+ 'img' => {'src' => ['http', 'https', :relative]},
44
+ }
45
+ }
46
+ end
47
+ end
Binary file
@@ -14,107 +14,143 @@
14
14
  }
15
15
  });
16
16
 
17
+ // general-purpose event blocker
18
+ function squash(e) {
19
+ if(e) {
20
+ e.preventDefault();
21
+ e.stopPropagation();
22
+ if (e.target) e.target.blur();
23
+ }
24
+ }
25
+
26
+ function RemoteAction (url, holder) {
27
+ var self = this;
28
+ $.extend(self, {
29
+ url: url,
30
+ holder: holder,
31
+ container: null,
32
+ form: null,
33
+
34
+ getForm: function () {
35
+ if (self.showing()) self.hide();
36
+ else if (self.form) self.show();
37
+ else {
38
+ self.wait();
39
+ self.container.load(self.url, self.captureForm);
40
+ }
41
+ },
42
+ captureForm: function () {
43
+ self.form = self.container.find('form');
44
+ self.form.submit(self.submitForm);
45
+ self.form.find('div.upload_stack').upload_stack();
46
+ self.form.find('a.cancel').click(self.cancel);
47
+ self.form.find("textarea.toolbarred").add_editor({});
48
+
49
+ self.unwait();
50
+ self.show();
51
+ },
52
+ submitForm: function (event) {
53
+ var ajaxable = true;
54
+ self.container.find('input:file').each(function () {
55
+ var file = $(this).val();
56
+ if (file && file != "") ajaxable = false;
57
+ });
58
+ if (ajaxable) {
59
+ squash(event);
60
+ self.form.find('textarea.toolbarred').read_editor();
61
+ $.post(self.form.attr('action'), self.form.serialize(), self.finish);
62
+ } else {
63
+ return true; // allow event through so that uploads are sent by normal HTTP POST
64
+ }
65
+ },
66
+ cancel: function (event) {
67
+ squash(event);
68
+ self.unwait();
69
+ self.hide();
70
+ },
71
+ finish: function (results) {
72
+ self.unwait();
73
+ var newpost = holder.replaceWith(results);
74
+ newpost.blush();
75
+ },
76
+ show: function () {
77
+ self.unwait();
78
+ self.holder.hide();
79
+ self.container.show();
80
+ },
81
+ hide: function () {
82
+ self.container.hide();
83
+ self.holder.show();
84
+ },
85
+ showing: function () {
86
+ return self.container.is(':visible');
87
+ },
88
+ wait: function () {
89
+ holder.wait();
90
+ },
91
+ unwait: function () {
92
+ holder.unwait();
93
+ }
94
+ });
95
+
96
+ self.container = $('<div class="post_form" />').hide();
97
+ self.holder.append(self.container);
98
+ }
99
+
100
+
17
101
  function EditablePost(container, conf) {
18
102
  var self = this;
19
103
  $.extend(self, {
20
104
  container: container,
21
- edit_links: $(container.find('a.edit_post')),
22
- edit_url: null,
23
- form: null,
24
- stumbit: null,
25
- textarea: null,
26
- showing: false,
27
- header: null,
28
- wrapper: null,
29
- body_holder: null,
30
- form_holder: null,
31
- form_waiter: null,
32
- uploader: null,
33
-
34
- initEdit: function(event) {
35
- squash(event);
36
- self.edit_links.addClass('waiting');
37
- if (self.showing) self.cancelEdit(); // toggle form off again
38
- else if (self.form_holder) self.showForm(); // show previously-loaded form
39
- else { // load form
40
- self.header = $(container.find('.post_header'));
41
- self.wrapper = $(container.find('.post_wrapper'));
42
- self.body_holder = $(container.find('.post_body'));
43
- self.edit_links = $(container.find('a.edit_post'));
44
- self.getForm();
45
- }
46
- },
47
-
48
- getForm: function () {
49
- if (self.edit_url) self.reusableFormHolder().load(self.edit_url, self.captureForm);
105
+ wrapper: container.find('.post_wrapper'),
106
+ head: container.find('.post_header'),
107
+ body: container.find('.post_body'),
108
+ actions: {},
109
+ addAction: function (url) {
110
+ if (!self.actions[url]) self.actions[url] = new RemoteAction(url, self);
111
+ return self.actions[url];
50
112
  },
51
-
52
- // lazy-load a container div that is then held in memory instead of
53
- // going back to the server for another edit form
54
- reusableFormHolder: function () {
55
- if (self.form_holder) return self.form_holder;
56
- self.form_holder = $('<div class="post_form" />');
57
- self.wrapper.prepend(self.form_holder);
58
- return self.form_holder;
59
- },
60
-
61
- captureForm: function () {
62
- self.form = self.form_holder.find('form');
63
- self.textarea = self.form.find('textarea');
64
- self.form_holder.find('a.cancel').click(self.cancelEdit);
65
- self.uploader = new UploadStack(self.form_holder.find('div.upload_stack'));
66
- self.stumbit = self.form_holder.find('div.buttons');
67
- self.form.submit(self.sendForm);
68
- self.showForm();
69
- },
70
-
71
- showForm: function () {
72
- self.edit_links.removeClass('waiting');
73
- self.body_holder.hide();
74
- self.reusableFormHolder().show();
75
- self.showing = true;
113
+ showAction: function (url) {
114
+ $.each(self.actions, function (key, action) { action.hide(); });
115
+ self.actions[url].getForm();
76
116
  },
77
-
78
- hideForm: function () {
79
- self.reusableFormHolder().hide();
80
- self.body_holder.show();
81
- self.showing = false;
117
+ append: function (el) {
118
+ return self.wrapper.append(el);
82
119
  },
83
-
84
- sendForm: function (event) {
85
- self.form_waiter = $('<p class="waiting">Please wait</p>');
86
- self.stumbit.hide();
87
- self.form_waiter.after(self.stumbit);
88
-
89
- console.log("uploader:", self.uploader, self.uploader.hasUploads());
90
-
91
- if (self.uploader && self.uploader.hasUploads()) {
92
- console.log("yes uploads");
93
- return true; // can't send uploads over xmlhttp so we allow the event to pass through
94
- } else {
95
- console.log("no uploads");
96
- squash(event);
97
- $.post(self.form.attr('action'), self.form.serialize(), self.finishEdit);
98
- }
120
+ replaceWith: function (post) {
121
+ self.container.replaceWith(post);
122
+ return $(post).editable_post();
99
123
  },
100
-
101
- cancelEdit: function (event) {
124
+ show: function () {
125
+ self.body.show();
126
+ },
127
+ hide: function () {
128
+ self.body.hide();
129
+ },
130
+ toggle: function (event) {
102
131
  squash(event);
103
- self.edit_links.removeClass('waiting');
104
- self.hideForm();
132
+ if (self.body.is(":visible")) self.hide();
133
+ else self.show();
105
134
  },
106
-
107
- finishEdit: function (results) {
108
- self.form_holder.remove();
109
- self.form_holder = null;
110
- self.container.html(results);
111
- self.body_holder.animate( { backgroundColor: 'pink' }, 200).animate( { backgroundColor: 'white' }, 1000);
112
- self.container.editable_post();
135
+ wait: function () {
136
+ self.container.addClass('waiting');
137
+ },
138
+ unwait: function () {
139
+ self.container.removeClass('waiting');
140
+ self.container.find('a').removeClass('waiting');
113
141
  }
114
142
  });
115
-
116
- self.edit_url = self.edit_links.attr('href');
117
- self.edit_links.click(self.initEdit);
143
+
144
+ container.find('a.remote').each(function () {
145
+ var a = $(this);
146
+ var href = a.attr('href');
147
+ self.addAction(href);
148
+ a.click(function (event) {
149
+ squash(event);
150
+ a.addClass('waiting');
151
+ self.showAction(href);
152
+ });
153
+ });
118
154
  }
119
155
 
120
156
  $.tools.post = {
@@ -122,14 +158,50 @@
122
158
 
123
159
  }
124
160
  };
125
-
161
+
126
162
  $.fn.editable_post = function(conf) {
127
163
  conf = $.extend({}, $.tools.post.conf, conf);
128
164
  this.each(function() {
129
- el = new EditablePost($(this), conf);
165
+ new EditablePost($(this), conf);
130
166
  });
131
167
  return this;
132
168
  };
169
+
170
+ function FirstPost(container, conf) {
171
+ var self = this;
172
+ $.extend(self, {
173
+ head: container.find('.post_header'),
174
+ body: container.find('.post_body'),
175
+ shower: null,
176
+ show: function () {
177
+ self.body.slideDown();
178
+ self.shower.text('Hide first post');
179
+ },
180
+ hide: function () {
181
+ self.body.slideUp();
182
+ self.shower.text('Show first post');
183
+ },
184
+ toggle: function (event) {
185
+ squash(event);
186
+ if (self.body.is(":visible")) self.hide();
187
+ else self.show();
188
+ }
189
+ });
190
+
191
+ self.shower = $('<a href="#" class="shower">Hide first post</a>').appendTo(self.head.find('p.context'));
192
+ self.shower.click(self.toggle);
193
+ if ($('a.prev_page').length > 0) self.hide();
194
+ }
195
+
196
+ $.fn.first_post = function() {
197
+ this.each(function() {
198
+ new FirstPost($(this));
199
+ });
200
+ return this;
201
+ };
202
+
203
+
204
+
133
205
 
134
206
  function UploadStack(container) {
135
207
  var self = this;
@@ -143,7 +215,10 @@
143
215
  addUpload: function(event) {
144
216
  squash(event);
145
217
  var upload_field = self.file_field.clone();
218
+ var nest_id = self.uploadCount() + 1;
146
219
  var container = $('<li class="attachment">' + upload_field.val() + '</li>');
220
+ upload_field.attr("id", upload_field.attr('id').replace(/\d+/, nest_id));
221
+ upload_field.attr("name", upload_field.attr('name').replace(/\d+/, nest_id));
147
222
  container.append(upload_field);
148
223
  container.add_remover();
149
224
  container.appendTo(self.uploads_list).slideDown('slow');
@@ -151,12 +226,20 @@
151
226
  self.selector.find('a').text = 'attach another file';
152
227
  },
153
228
 
229
+ attachmentCount: function () {
230
+ return self.attachments_list.find('li').length;
231
+ },
232
+
154
233
  hasAttachments: function () {
155
234
  return self.attachments_list.find('li').length > 0;
156
235
  },
157
236
 
237
+ uploadCount: function () {
238
+ return self.uploads_list.find('li').length;
239
+ },
240
+
158
241
  hasUploads: function () {
159
- return self.uploads_list.find('li').length > 0;
242
+ return self.uploadCount() > 0;
160
243
  }
161
244
  });
162
245
  self.attachments_list.find('li').add_remover();
@@ -183,16 +266,189 @@
183
266
  var remover = $('<a href="#" class="remove">remove</a>');
184
267
  remover.click(function (event) {
185
268
  squash(event);
186
- self.slideUp('500', function() { self.remove(); });
269
+ self.slideUp('500', function() {
270
+ self.find('input.checkbox').attr('checked', true);
271
+ self.find('input.filefield').remove();
272
+ });
187
273
  });
188
274
  self.append(remover);
189
275
  });
190
276
  return self;
191
277
  };
192
278
 
279
+ $.fn.add_editor = function() {
280
+ this.each(function() {
281
+ var self = $(this);
282
+ var editor = new punymce.Editor({
283
+ id : self.attr('id'),
284
+ plugins : 'Link,Image,Emoticons,EditSource',
285
+ toolbar : 'bold,italic,link,unlink,image,emoticons,editsource',
286
+ width : 510,
287
+ height : 375,
288
+ resize : true
289
+ });
290
+ self.data('editor', editor);
291
+ });
292
+ return this;
293
+ };
294
+
295
+ $.fn.read_editor = function() {
296
+ this.each(function() {
297
+ var self = $(this);
298
+ if (self.data('editor')) {
299
+ self.val(self.data('editor').getContent());
300
+ }
301
+ });
302
+ return this;
303
+ };
304
+
305
+ /*
306
+ * jQuery Color Animations
307
+ * Copyright 2007 John Resig
308
+ * Released under the MIT and GPL licenses.
309
+ * syntax corrected but otherwise untouched.
310
+ */
311
+
312
+ // We override the animation for all of these color styles
313
+ $.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i, attr) {
314
+ $.fx.step[attr] = function(fx) {
315
+ if (!fx.colorInit) {
316
+ fx.start = getColor(fx.elem, attr);
317
+ fx.end = getRGB(fx.end);
318
+ fx.colorInit = true;
319
+ }
320
+
321
+ fx.elem.style[attr] = "rgb(" + [
322
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10), 255), 0),
323
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10), 255), 0),
324
+ Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10), 255), 0)
325
+ ].join(",") + ")";
326
+ }
327
+ });
328
+
329
+ // Color Conversion functions from highlightFade
330
+ // By Blair Mitchelmore
331
+ // http://jquery.offput.ca/highlightFade/
332
+
333
+ // Parse strings looking for color tuples [255,255,255]
334
+ function getRGB(color) {
335
+ var result;
336
+
337
+ // Check if we're already dealing with an array of colors
338
+ if ( color && color.constructor == Array && color.length == 3 )
339
+ return color;
340
+
341
+ // Look for rgb(num,num,num)
342
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
343
+ return [parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10)];
344
+
345
+ // Look for rgb(num%,num%,num%)
346
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
347
+ return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
348
+
349
+ // Look for #a0b1c2
350
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
351
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
352
+
353
+ // Look for #fff
354
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
355
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
356
+
357
+ // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
358
+ if (result = /rgba\(0, 0, 0, 0\)/.exec(color))
359
+ return colors['transparent'];
360
+
361
+ // Otherwise, we're most likely dealing with a named color
362
+ return colors[jQuery.trim(color).toLowerCase()];
363
+ }
364
+
365
+ function getColor(elem, attr) {
366
+ var color;
367
+
368
+ do {
369
+ color = $.curCSS(elem, attr);
370
+
371
+ // Keep going until we find an element that has color, or we hit the body
372
+ if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
373
+ break;
374
+
375
+ attr = "backgroundColor";
376
+ } while ( elem = elem.parentNode );
377
+
378
+ return getRGB(color);
379
+ };
380
+
381
+ // Some named colors to work with
382
+ // From Interface by Stefan Petre
383
+ // http://interface.eyecon.ro/
384
+
385
+ var colors = {
386
+ aqua:[0,255,255],
387
+ azure:[240,255,255],
388
+ beige:[245,245,220],
389
+ black:[0,0,0],
390
+ blue:[0,0,255],
391
+ brown:[165,42,42],
392
+ cyan:[0,255,255],
393
+ darkblue:[0,0,139],
394
+ darkcyan:[0,139,139],
395
+ darkgrey:[169,169,169],
396
+ darkgreen:[0,100,0],
397
+ darkkhaki:[189,183,107],
398
+ darkmagenta:[139,0,139],
399
+ darkolivegreen:[85,107,47],
400
+ darkorange:[255,140,0],
401
+ darkorchid:[153,50,204],
402
+ darkred:[139,0,0],
403
+ darksalmon:[233,150,122],
404
+ darkviolet:[148,0,211],
405
+ fuchsia:[255,0,255],
406
+ gold:[255,215,0],
407
+ green:[0,128,0],
408
+ indigo:[75,0,130],
409
+ khaki:[240,230,140],
410
+ lightblue:[173,216,230],
411
+ lightcyan:[224,255,255],
412
+ lightgreen:[144,238,144],
413
+ lightgrey:[211,211,211],
414
+ lightpink:[255,182,193],
415
+ lightyellow:[255,255,224],
416
+ lime:[0,255,0],
417
+ magenta:[255,0,255],
418
+ maroon:[128,0,0],
419
+ navy:[0,0,128],
420
+ olive:[128,128,0],
421
+ orange:[255,165,0],
422
+ pink:[255,192,203],
423
+ purple:[128,0,128],
424
+ violet:[128,0,128],
425
+ red:[255,0,0],
426
+ silver:[192,192,192],
427
+ white:[255,255,255],
428
+ yellow:[255,255,0],
429
+ transparent: [255,255,255]
430
+ };
431
+
432
+ $.fn.blush = function(color, duration) {
433
+ color = color || "#FFFF9C";
434
+ duration = duration || 1500;
435
+ var backto = this.css("background-color");
436
+ if (backto == "" || backto == 'transparent') backto = '#ffffff';
437
+ this.css("background-color", color).animate({"background-color": backto}, duration);
438
+ };
439
+
440
+
193
441
  })(jQuery);
194
442
 
195
443
  $(function() {
196
444
  $(".post").editable_post({});
445
+ $(".post.first").first_post({});
197
446
  $(".upload_stack").upload_stack({});
447
+ $(".toolbarred").add_editor({});
448
+ $("input:submit").live('click', function (event) {
449
+ var self = $(this);
450
+ self.after('<span class="waiting">Please wait</span>');
451
+ self.hide();
452
+ return true;
453
+ });
198
454
  });