wysihat-engine 0.1.0

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.
Files changed (61) hide show
  1. data/.gitignore +5 -0
  2. data/CHANGELOG +5 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +20 -0
  5. data/Rakefile +41 -0
  6. data/TODO +3 -0
  7. data/VERSION +1 -0
  8. data/app/controllers/wysihat_files_controller.rb +26 -0
  9. data/app/helpers/wysihat_files_helper.rb +2 -0
  10. data/app/models/wysihat_file.rb +3 -0
  11. data/app/views/wysihat_files/_form.html.erb +13 -0
  12. data/app/views/wysihat_files/_wysihat_file.html.erb +3 -0
  13. data/app/views/wysihat_files/index.html.erb +5 -0
  14. data/app/views/wysihat_files/new.html.erb +1 -0
  15. data/generators/wysihat/templates/images/arrow_redo.png +0 -0
  16. data/generators/wysihat/templates/images/arrow_undo.png +0 -0
  17. data/generators/wysihat/templates/images/b.png +0 -0
  18. data/generators/wysihat/templates/images/bl.png +0 -0
  19. data/generators/wysihat/templates/images/br.png +0 -0
  20. data/generators/wysihat/templates/images/closelabel.gif +0 -0
  21. data/generators/wysihat/templates/images/exclamation.png +0 -0
  22. data/generators/wysihat/templates/images/film.png +0 -0
  23. data/generators/wysihat/templates/images/image.png +0 -0
  24. data/generators/wysihat/templates/images/link.png +0 -0
  25. data/generators/wysihat/templates/images/loading.gif +0 -0
  26. data/generators/wysihat/templates/images/page_code.png +0 -0
  27. data/generators/wysihat/templates/images/page_white_flash.png +0 -0
  28. data/generators/wysihat/templates/images/paste_plain.png +0 -0
  29. data/generators/wysihat/templates/images/text_align_center.png +0 -0
  30. data/generators/wysihat/templates/images/text_align_left.png +0 -0
  31. data/generators/wysihat/templates/images/text_align_right.png +0 -0
  32. data/generators/wysihat/templates/images/text_bold.png +0 -0
  33. data/generators/wysihat/templates/images/text_heading_1.png +0 -0
  34. data/generators/wysihat/templates/images/text_heading_2.png +0 -0
  35. data/generators/wysihat/templates/images/text_heading_3.png +0 -0
  36. data/generators/wysihat/templates/images/text_italic.png +0 -0
  37. data/generators/wysihat/templates/images/text_list_bullets.png +0 -0
  38. data/generators/wysihat/templates/images/text_list_numbers.png +0 -0
  39. data/generators/wysihat/templates/images/text_strikethrough.png +0 -0
  40. data/generators/wysihat/templates/images/text_underline.png +0 -0
  41. data/generators/wysihat/templates/images/tl.png +0 -0
  42. data/generators/wysihat/templates/images/tr.png +0 -0
  43. data/generators/wysihat/templates/javascripts/facebox.js +190 -0
  44. data/generators/wysihat/templates/javascripts/wysihat.js +2098 -0
  45. data/generators/wysihat/templates/migrations/create_wysihat_files.rb +15 -0
  46. data/generators/wysihat/templates/stylesheets/facebox.css +95 -0
  47. data/generators/wysihat/templates/stylesheets/wysihat.css +76 -0
  48. data/generators/wysihat/wysihat_generator.rb +50 -0
  49. data/lib/wysihat-engine.rb +132 -0
  50. data/vendor/plugins/responds_to_parent/MIT-LICENSE +20 -0
  51. data/vendor/plugins/responds_to_parent/README +42 -0
  52. data/vendor/plugins/responds_to_parent/Rakefile +22 -0
  53. data/vendor/plugins/responds_to_parent/init.rb +2 -0
  54. data/vendor/plugins/responds_to_parent/lib/parent_selector_assertion.rb +144 -0
  55. data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +37 -0
  56. data/vendor/plugins/responds_to_parent/rails/init.rb +2 -0
  57. data/vendor/plugins/responds_to_parent/responds-to-parent.gemspec +15 -0
  58. data/vendor/plugins/responds_to_parent/test/assert_select_parent_test.rb +319 -0
  59. data/vendor/plugins/responds_to_parent/test/responds_to_parent_test.rb +116 -0
  60. data/wysihat-engine.gemspec +101 -0
  61. metadata +133 -0
@@ -0,0 +1,15 @@
1
+ class CreateWysihatFiles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :wysihat_files do |t|
4
+ t.string :file_file_name
5
+ t.string :file_content_type
6
+ t.integer :file_file_size
7
+ t.datetime :file_updated_at
8
+ t.timestamps
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :wysihat_files
14
+ end
15
+ end
@@ -0,0 +1,95 @@
1
+ #facebox .b {
2
+ background:url(../images/b.png);
3
+ }
4
+
5
+ #facebox .tl {
6
+ background:url(../images/tl.png);
7
+ }
8
+
9
+ #facebox .tr {
10
+ background:url(../images/tr.png);
11
+ }
12
+
13
+ #facebox .bl {
14
+ background:url(../images/bl.png);
15
+ }
16
+
17
+ #facebox .br {
18
+ background:url(../images/br.png);
19
+ }
20
+
21
+ #facebox {
22
+ position: absolute;
23
+ top: 0;
24
+ left: 0;
25
+ z-index: 100;
26
+ text-align: left;
27
+ }
28
+
29
+ #facebox .popup {
30
+ position: relative;
31
+ }
32
+
33
+ #facebox table {
34
+ border-collapse: collapse;
35
+ }
36
+
37
+ #facebox td {
38
+ border-bottom: 0;
39
+ padding: 0;
40
+ }
41
+
42
+ #facebox .body {
43
+ padding: 10px;
44
+ background: #fff;
45
+ width: 370px;
46
+ }
47
+
48
+ #facebox .loading {
49
+ text-align: center;
50
+ }
51
+
52
+ #facebox .image {
53
+ text-align: center;
54
+ }
55
+
56
+ #facebox img {
57
+ border: 0;
58
+ margin: 0;
59
+ }
60
+
61
+ #facebox .footer {
62
+ border-top: 1px solid #DDDDDD;
63
+ padding-top: 5px;
64
+ margin-top: 10px;
65
+ text-align: right;
66
+ }
67
+
68
+ #facebox .tl, #facebox .tr, #facebox .bl, #facebox .br {
69
+ height: 10px;
70
+ width: 10px;
71
+ overflow: hidden;
72
+ padding: 0;
73
+ }
74
+
75
+ #facebox_overlay {
76
+ position: fixed;
77
+ top: 0px;
78
+ left: 0px;
79
+ height:100%;
80
+ width:100%;
81
+ }
82
+
83
+ .facebox_hide {
84
+ z-index:-100;
85
+ }
86
+
87
+ .facebox_overlayBG {
88
+ background-color: #000;
89
+ z-index: 99;
90
+ }
91
+
92
+ * html #facebox_overlay { /* ie6 hack */
93
+ position: absolute;
94
+ height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
95
+ }
@@ -0,0 +1,76 @@
1
+ div.editor_toolbar a{
2
+ float:left;
3
+ width:16px;
4
+ height:16px;
5
+ margin:5px;
6
+ background-repeat: no-repeat;
7
+ }
8
+
9
+ div.editor_toolbar span{
10
+ display:none;
11
+ }
12
+
13
+ .editor_toolbar .bold {
14
+ background-image: url('../images/text_bold.png');
15
+ }
16
+
17
+ .editor_toolbar .italic {
18
+ background-image: url('../images/text_italic.png');
19
+ }
20
+
21
+ .editor_toolbar .underline {
22
+ background-image: url('../images/text_underline.png');
23
+ }
24
+
25
+ .editor_toolbar .strikethrough {
26
+ background-image: url('../images/text_strikethrough.png');
27
+ }
28
+
29
+ .editor_toolbar .justifyleft {
30
+ background-image: url('../images/text_align_left.png');
31
+ }
32
+
33
+ .editor_toolbar .justifycenter {
34
+ background-image: url('../images/text_align_center.png');
35
+ }
36
+
37
+ .editor_toolbar .justifyright {
38
+ background-image: url('../images/text_align_right.png');
39
+ }
40
+
41
+ .editor_toolbar .undo {
42
+ background-image: url('../images/arrow_undo.png');
43
+ }
44
+
45
+ .editor_toolbar .redo {
46
+ background-image: url('../images/arrow_redo.png');
47
+ }
48
+
49
+ .editor_toolbar .insertorderedlist {
50
+ background-image: url('../images/text_list_numbers.png');
51
+ }
52
+
53
+ .editor_toolbar .insertunorderedlist {
54
+ background-image: url('../images/text_list_bullets.png');
55
+ }
56
+
57
+ .editor_toolbar .link {
58
+ background-image: url('../images/link.png');
59
+ }
60
+
61
+ .editor_toolbar .image {
62
+ background-image: url('../images/image.png');
63
+ }
64
+
65
+ .editor_toolbar .paste {
66
+ background-image: url('../images/paste_plain.png');
67
+ }
68
+
69
+ .editor_toolbar .html {
70
+ background-image: url('../images/page_code.png');
71
+ }
72
+
73
+ iframe.editor{
74
+ height:400px;
75
+ width:100%;
76
+ }
@@ -0,0 +1,50 @@
1
+ class WysihatGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+
5
+ m.file 'javascripts/wysihat.js', 'public/javascripts/wysihat.js'
6
+ m.file 'javascripts/facebox.js', 'public/javascripts/facebox.js'
7
+
8
+ m.file 'stylesheets/wysihat.css', 'public/stylesheets/wysihat.css'
9
+ m.file 'stylesheets/facebox.css', 'public/stylesheets/facebox.css'
10
+
11
+ m.file 'images/arrow_redo.png', 'public/images/arrow_redo.png'
12
+ m.file 'images/arrow_undo.png', 'public/images/arrow_undo.png'
13
+ m.file 'images/b.png', 'public/images/b.png'
14
+ m.file 'images/bl.png', 'public/images/bl.png'
15
+ m.file 'images/br.png', 'public/images/br.png'
16
+ m.file 'images/closelabel.gif', 'public/images/closelabel.gif'
17
+ m.file 'images/exclamation.png', 'public/images/exclamation.png'
18
+ m.file 'images/film.png', 'public/images/film.png'
19
+ m.file 'images/image.png', 'public/images/image.png'
20
+ m.file 'images/link.png', 'public/images/link.png'
21
+ m.file 'images/loading.gif', 'public/images/loading.gif'
22
+ m.file 'images/page_code.png', 'public/images/page_code.png'
23
+ m.file 'images/page_white_flash.png', 'public/images/page_white_flash.png'
24
+ m.file 'images/paste_plain.png', 'public/images/paste_plain.png'
25
+ m.file 'images/text_align_center.png', 'public/images/text_align_center.png'
26
+ m.file 'images/text_align_left.png', 'public/images/text_align_left.png'
27
+ m.file 'images/text_align_right.png', 'public/images/text_align_right.png'
28
+ m.file 'images/text_bold.png', 'public/images/text_bold.png'
29
+ m.file 'images/text_heading_1.png', 'public/images/text_heading_1.png'
30
+ m.file 'images/text_heading_2.png', 'public/images/text_heading_2.png'
31
+ m.file 'images/text_heading_3.png', 'public/images/text_heading_3.png'
32
+ m.file 'images/text_italic.png', 'public/images/text_italic.png'
33
+ m.file 'images/text_list_bullets.png', 'public/images/text_list_bullets.png'
34
+ m.file 'images/text_list_numbers.png', 'public/images/text_list_numbers.png'
35
+ m.file 'images/text_strikethrough.png', 'public/images/text_strikethrough.png'
36
+ m.file 'images/text_underline.png', 'public/images/text_underline.png'
37
+ m.file 'images/tl.png', 'public/images/tl.png'
38
+ m.file 'images/tr.png', 'public/images/tr.png'
39
+
40
+ m.migration_template 'migrations/create_wysihat_files.rb',
41
+ 'db/migrate',
42
+ :migration_file_name => 'create_wysihat_files'
43
+
44
+ m.route_resources :wysihat_files
45
+
46
+ m.system 'script/plugin install git://github.com/markcatley/responds_to_parent.git'
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,132 @@
1
+ require 'resource_controller'
2
+ require 'paperclip'
3
+
4
+ module ActionView
5
+ module Helpers
6
+ module FormHelper
7
+ def wysihat_editor(object_name, method, options = {})
8
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_wysihat_editor_tag(options)
9
+ end
10
+ end
11
+
12
+ class InstanceTag #:nodoc:
13
+ def to_wysihat_editor_tag(options = {})
14
+
15
+ options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
16
+ add_default_name_and_id(options)
17
+
18
+ if size = options.delete("size")
19
+ options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
20
+ end
21
+
22
+ buttons, helpers = '', ''
23
+
24
+ case options['buttons']
25
+ when nil, :all
26
+ options['buttons'] = [:bold, :italic, :underline, :strikethrough, :justify_left, :justify_center, :justify_right, :insert_ordered_list, :insert_unordered_list, :undo, :redo, :link, :html, :paste, :image]
27
+ end
28
+
29
+ options['buttons'].each do |b|
30
+ case b.to_s.downcase
31
+ when "image"
32
+ buttons << "toolbar.addButton({label : \'Image\', handler: function(editor) { return editor.faceBoxFile(editor); } })\n"
33
+ when "link"
34
+ buttons << "toolbar.addButton({label : \'Link\', handler: function(editor) { return editor.faceboxLink(editor); } })\n"
35
+ when "html"
36
+ buttons << "toolbar.addButton({label : \'HTML\', handler: function(editor) { return editor.faceboxHTML(editor); } })\n"
37
+ when "paste"
38
+ buttons << "toolbar.addButton({label : \'Paste\', handler: function(editor) { return editor.faceboxPaste(editor); } })\n"
39
+ else
40
+ buttons << "toolbar.addButton({label : \'#{b.to_s.split('_').map {|w| w.capitalize}.join}\'});\n"
41
+ end
42
+ end
43
+
44
+ options.delete('buttons');
45
+
46
+ content_tag(
47
+ :script,
48
+ "
49
+ var WysihatHelper = {
50
+ faceBoxFile: function()
51
+ {
52
+ facebox.loading();
53
+ new Effect.Appear($('facebox'), {duration: .3});
54
+
55
+ var fb = facebox;
56
+ var url = '/wysihat_files/?editor=' + this.id
57
+
58
+ new Ajax.Request(url, {
59
+ method : 'get',
60
+ onFailure : function(transport){
61
+ fb.reveal(transport.responseText, null);
62
+ },
63
+ onSuccess : function(transport){
64
+ fb.reveal(transport.responseText, null);
65
+ }
66
+ });
67
+ },
68
+
69
+ faceboxLink: function()
70
+ {
71
+ if (this.linkSelected()) {
72
+ this.unlinkSelection();
73
+ } else {
74
+ facebox.loading();
75
+ new Effect.Appear($('facebox'), {duration: .3});
76
+ iframe = this
77
+ facebox.reveal('<input type=\"text\" id=\"link_field\" style=\"width:100%;\"/>', null);
78
+ Event.observe('link_field', 'change', function(event) {
79
+ iframe.linkSelection($('link_field').value);
80
+ });
81
+ }
82
+ },
83
+
84
+ faceboxHTML: function()
85
+ {
86
+ facebox.loading();
87
+ new Effect.Appear($('facebox'), {duration: .3});
88
+ iframe = this
89
+ facebox.reveal('<textarea id=\"html_editor\" style=\"width:100%; height:400px;\">' + iframe.contentWindow.document.body.innerHTML + '</textarea>', null);
90
+ Event.observe('html_editor', 'change', function(event) {
91
+ iframe.contentWindow.document.body.innerHTML = $('html_editor').value;
92
+ });
93
+ },
94
+
95
+ faceboxPaste: function()
96
+ {
97
+ facebox.loading();
98
+ new Effect.Appear($('facebox'), {duration: .3});
99
+ iframe = this
100
+ facebox.reveal('<textarea id=\"paste_editor\" style=\"width:100%; height:400px;\"></textarea>', null);
101
+ Event.observe('paste_editor', 'change', function(event) {
102
+ iframe.contentWindow.document.body.innerHTML = iframe.contentWindow.document.body.innerHTML + $('paste_editor').value.escapeHTML();
103
+ });
104
+ }
105
+ }
106
+
107
+ WysiHat.Editor.include(WysihatHelper);
108
+
109
+ Event.observe(window, 'load', function() {
110
+ var editor = WysiHat.Editor.attach('#{tag_id}');
111
+ var toolbar = new WysiHat.Toolbar(editor);
112
+ #{buttons}
113
+
114
+ $$('form').first().onsubmit = function(){
115
+ editor.save();
116
+ };
117
+
118
+ });
119
+ ",
120
+ :type => 'text/javascript'
121
+ ) <<
122
+ content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast(object)), options.merge(:class => 'wysihat_editor'))
123
+ end
124
+ end
125
+
126
+ class FormBuilder #:nodoc:
127
+ def wysihat_editor(method, options = {})
128
+ @template.wysihat_editor(@object_name, method, options)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Sean Treadway
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,42 @@
1
+ RespondsToParent
2
+ ================
3
+
4
+ Adds responds_to_parent to your controller to respond to the parent document of your page.
5
+ Make Ajaxy file uploads by posting the form to a hidden iframe, and respond with
6
+ RJS to the parent window.
7
+
8
+ Example
9
+ =======
10
+
11
+ Controller:
12
+
13
+ class Test < ActionController::Base
14
+ def main
15
+ end
16
+
17
+ def form_action
18
+ # Do stuff with params[:uploaded_file]
19
+
20
+ responds_to_parent do
21
+ render :update do |page|
22
+ page << "alert($('stuff').innerHTML)"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ main.rhtml:
29
+
30
+ <html>
31
+ <body>
32
+ <div id="stuff">Here is some stuff</div>
33
+
34
+ <form target="frame" action="form_action">
35
+ <input type="file" name="uploaded_file"/>
36
+ <input type="submit"/>
37
+ </form>
38
+
39
+ <iframe id='frame' name="frame"></iframe>
40
+ </body>
41
+ </html>
42
+
@@ -0,0 +1,22 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the responds_to_parent plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the responds_to_parent plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'RespondsToParent'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
@@ -0,0 +1,2 @@
1
+ ActionController::Base.send :include, RespondsToParent
2
+ require 'parent_selector_assertion'
@@ -0,0 +1,144 @@
1
+ module ActionController
2
+ module Assertions
3
+ module SelectorAssertions
4
+ # :call-seq:
5
+ # assert_select_parent()
6
+ # assert_select_parent() { |script| ... }
7
+ #
8
+ # Selects JavaScript that is generated for the `parent' window.
9
+ #
10
+ # Without a block, #assert_select_parent asserts that the response
11
+ # is generated by responds_to_parent.
12
+ #
13
+ # With a block, #assert_select_parent selects script that is supposed
14
+ # to be evaluated in the parent window and passes it to the block.
15
+ # Typically #assert_select_rjs is used in the block.
16
+ def assert_select_parent(*args, &block)
17
+ wrapper_re_str = Regexp.escape("with(window.parent) { setTimeout(function() { window.eval('") +
18
+ "(.*)" +
19
+ Regexp.escape("'); window.loc && loc.replace('about:blank'); }, 1) }")
20
+ match = @response.body.match(Regexp.new(wrapper_re_str))
21
+
22
+ if match
23
+ escaped_js = match[1]
24
+ unescaped_js = escaped_js.
25
+ gsub(%r!</scr"\+"ipt>!, '</script>').
26
+ gsub(/\\(\'|\")/, '\1').
27
+ gsub(/((?:^|[^\\])(?:\\\\)*)\\n/, "\\1\n"). # replace `n' with odd number of backslash.
28
+ gsub(/\\\\/, '\\')
29
+ @response.body = unescaped_js # assert_select_rjs refers @response.body.
30
+
31
+ if block_given?
32
+ begin
33
+ in_scope, @selected = @selected, unescaped_js
34
+ yield unescaped_js
35
+ ensure
36
+ @selected = in_scope
37
+ end
38
+ end
39
+ unescaped_js
40
+ else
41
+ # doesn't seem a responds_to_parent content.
42
+ flunk args.shift || "No content for the parent window."
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ module ActionController
50
+ module Assertions
51
+ module SelectorAssertions
52
+ # :call-seq:
53
+ # assert_select_parent()
54
+ # assert_select_parent() { |script| ... }
55
+ #
56
+ # Selects JavaScript that is generated for the `parent' window.
57
+ #
58
+ # Without a block, #assert_select_parent asserts that the response
59
+ # is generated by responds_to_parent.
60
+ #
61
+ # With a block, #assert_select_parent selects script that is supposed
62
+ # to be evaluated in the parent window and passes it to the block.
63
+ # Typically #assert_select_rjs is used in the block.
64
+ def assert_select_parent(*args, &block)
65
+ wrapper_re_str = Regexp.escape("with(window.parent) { setTimeout(function() { window.eval('") +
66
+ "(.*)" +
67
+ Regexp.escape("'); window.loc && loc.replace('about:blank'); }, 1) }")
68
+ match = @response.body.match(Regexp.new(wrapper_re_str))
69
+
70
+ if match
71
+ escaped_js = match[1]
72
+ unescaped_js = escaped_js.
73
+ gsub(%r!</scr"\+"ipt>!, '</script>').
74
+ gsub(/\\(\'|\")/, '\1').
75
+ gsub(/((?:^|[^\\])(?:\\\\)*)\\n/, "\\1\n"). # replace `n' with odd number of backslash.
76
+ gsub(/\\\\/, '\\')
77
+ @response.body = unescaped_js # assert_select_rjs refers @response.body.
78
+
79
+ if block_given?
80
+ begin
81
+ in_scope, @selected = @selected, unescaped_js
82
+ yield unescaped_js
83
+ ensure
84
+ @selected = in_scope
85
+ end
86
+ end
87
+ unescaped_js
88
+ else
89
+ # doesn't seem a responds_to_parent content.
90
+ flunk args.shift || "No content for the parent window."
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ module ActionController
98
+ module Assertions
99
+ module SelectorAssertions
100
+ # :call-seq:
101
+ # assert_select_parent()
102
+ # assert_select_parent() { |script| ... }
103
+ #
104
+ # Selects JavaScript that is generated for the `parent' window.
105
+ #
106
+ # Without a block, #assert_select_parent asserts that the response
107
+ # is generated by responds_to_parent.
108
+ #
109
+ # With a block, #assert_select_parent selects script that is supposed
110
+ # to be evaluated in the parent window and passes it to the block.
111
+ # Typically #assert_select_rjs is used in the block.
112
+ def assert_select_parent(*args, &block)
113
+ wrapper_re_str = Regexp.escape("with(window.parent) { setTimeout(function() { window.eval('") +
114
+ "(.*)" +
115
+ Regexp.escape("'); window.loc && loc.replace('about:blank'); }, 1) }")
116
+ match = @response.body.match(Regexp.new(wrapper_re_str))
117
+
118
+ if match
119
+ escaped_js = match[1]
120
+ unescaped_js = escaped_js.
121
+ gsub(%r!</scr"\+"ipt>!, '</script>').
122
+ gsub(/\\(\'|\")/, '\1').
123
+ gsub(/((?:^|[^\\])(?:\\\\)*)\\n/, "\\1\n"). # replace `n' with odd number of backslash.
124
+ gsub(/\\\\/, '\\')
125
+ @response.body = unescaped_js # assert_select_rjs refers @response.body.
126
+
127
+ if block_given?
128
+ begin
129
+ in_scope, @selected = @selected, unescaped_js
130
+ yield unescaped_js
131
+ ensure
132
+ @selected = in_scope
133
+ end
134
+ end
135
+ unescaped_js
136
+ else
137
+ # doesn't seem a responds_to_parent content.
138
+ flunk args.shift || "No content for the parent window."
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
@@ -0,0 +1,37 @@
1
+ # Module containing the methods useful for child IFRAME to parent window communication
2
+ module RespondsToParent
3
+ # Executes the response body as JavaScript in the context of the parent window.
4
+ # Use this method of you are posting a form to a hidden IFRAME or if you would like
5
+ # to use IFRAME base RPC.
6
+ def responds_to_parent(&block)
7
+ yield
8
+
9
+ if performed?
10
+ # Either pull out a redirect or the request body
11
+ script = if location = erase_redirect_results
12
+ "document.location.href = '#{self.class.helpers.escape_javascript location.to_s}'"
13
+ else
14
+ response.body || ''
15
+ end
16
+
17
+ # Clear out the previous render to prevent double render
18
+ erase_results
19
+
20
+ # We're returning HTML instead of JS or XML now
21
+ response.headers['Content-Type'] = 'text/html; charset=UTF-8'
22
+
23
+ # Eval in parent scope and replace document location of this frame
24
+ # so back button doesn't replay action on targeted forms
25
+ # loc = document.location to be set after parent is updated for IE
26
+ # with(window.parent) - pull in variables from parent window
27
+ # setTimeout - scope the execution in the windows parent for safari
28
+ # window.eval - legal eval for Opera
29
+ render :text => "<html><body><script type='text/javascript' charset='utf-8'>
30
+ var loc = document.location;
31
+ with(window.parent) { setTimeout(function() { window.eval('#{self.class.helpers.escape_javascript script}'); window.loc && loc.replace('about:blank'); }, 1) }
32
+ </script></body></html>"
33
+ end
34
+ end
35
+ alias respond_to_parent responds_to_parent
36
+ end
37
+
@@ -0,0 +1,2 @@
1
+ ActionController::Base.send :include, RespondsToParent
2
+ require 'parent_selector_assertion'
@@ -0,0 +1,15 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'responds-to-parent'
3
+ s.version = '1.0.20090923'
4
+ s.homepage = 'http://constancy.rubyforge.org/'
5
+ s.summary = "[Rails] Adds 'responds_to_parent' to your controller to" +
6
+ 'respond to the parent document of your page.'
7
+ 'Make Ajaxy file uploads by posting the form to a hidden' +
8
+ 'iframe, and respond with RJS to the parent window.'
9
+
10
+ s.files = %w( README Rakefile MIT-LICENSE rails/init.rb
11
+ lib/responds_to_parent.rb
12
+ lib/parent_selector_assertion.rb
13
+ test/responds_to_parent_test.rb
14
+ test/assert_select_parent_test.rb)
15
+ end