patch-html_namespacing 0.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ #ifndef HTML_NAMESPACING_H
2
+ #define HTML_NAMESPACING_H
3
+
4
+ #ifdef __cplusplus
5
+ extern "C" {
6
+ #endif
7
+
8
+ typedef struct _HtmlNamespacingAllocationStrategy {
9
+ void *(*malloc)(size_t size);
10
+ void (*free)(void *ptr);
11
+ void *(*realloc)(void *ptr, size_t size);
12
+ } HtmlNamespacingAllocationStrategy;
13
+
14
+ int
15
+ add_namespace_to_html_with_length(
16
+ const char *html,
17
+ size_t html_len,
18
+ const char *ns,
19
+ char **ret,
20
+ size_t *ret_len);
21
+
22
+ int
23
+ add_namespace_to_html_with_length_and_allocation_strategy(
24
+ const char *html,
25
+ size_t html_len,
26
+ const char *ns,
27
+ char **ret,
28
+ size_t *ret_len,
29
+ HtmlNamespacingAllocationStrategy allocation_strategy);
30
+
31
+ #ifdef __cplusplus
32
+ }
33
+ #endif
34
+
35
+ #endif /* HTML_NAMESPACING_H */
@@ -0,0 +1,97 @@
1
+ #include <errno.h>
2
+
3
+ #include "ruby.h"
4
+
5
+ #include "html_namespacing.h"
6
+
7
+ #include <stdio.h>
8
+
9
+ VALUE rb_mHtmlNamespacing = Qnil;
10
+
11
+ static const HtmlNamespacingAllocationStrategy ALLOCATION_STRATEGY = {
12
+ (void *(*)(size_t)) ruby_xmalloc,
13
+ ruby_xfree,
14
+ (void *(*)(void *, size_t)) ruby_xrealloc
15
+ };
16
+
17
+ /*
18
+ * call-seq:
19
+ * HtmlNamespacing.add_namespace_to_html(html, ns) => String
20
+ *
21
+ * Returns new HTML string based on +html+ with the HTML class +ns+ to root
22
+ * elements.
23
+ *
24
+ * If +html+ is nil, returns +nil+.
25
+ *
26
+ * If +ns+ is nil, returns +html+.
27
+ */
28
+
29
+ VALUE
30
+ html_namespacing_add_namespace_to_html(
31
+ VALUE obj,
32
+ VALUE html,
33
+ VALUE ns)
34
+ {
35
+ /*
36
+ * It's almost tempting to manually allocate the RString object to save
37
+ * ourselves the stupid extra copy. (add_namespace_to_html_with_length()
38
+ * implicitly copies the string, and here we are copying it again because
39
+ * Ruby can't convert from a char* to an RString? How lame....)
40
+ *
41
+ * But for now, let's just do the extra copy (implicit in rb_str_new2) and
42
+ * be done with it.
43
+ */
44
+
45
+ const char *html_ptr;
46
+ size_t html_len;
47
+ const char *ns_ptr;
48
+ char *ret_ptr;
49
+ size_t ret_len;
50
+ int rv;
51
+ VALUE ret;
52
+
53
+ if (TYPE(html) == T_NIL) {
54
+ return Qnil;
55
+ }
56
+ Check_Type(html, T_STRING);
57
+
58
+ if (TYPE(ns) == T_NIL) {
59
+ return html;
60
+ }
61
+
62
+ Check_Type(ns, T_STRING);
63
+
64
+ html_ptr = RSTRING_PTR(html);
65
+ html_len = RSTRING_LEN(html);
66
+
67
+ ns_ptr = RSTRING_PTR(ns);
68
+
69
+ rv = add_namespace_to_html_with_length_and_allocation_strategy(
70
+ html_ptr,
71
+ html_len,
72
+ ns_ptr,
73
+ &ret_ptr,
74
+ &ret_len,
75
+ ALLOCATION_STRATEGY);
76
+ if (rv == EINVAL) {
77
+ rb_raise(rb_eArgError, "Badly-formed HTML: %s", html_ptr);
78
+ }
79
+ if (rv != 0) {
80
+ rb_raise(rb_eRuntimeError, "Unknown error in add_namespace_to_html");
81
+ }
82
+
83
+ ret = rb_str_new(ret_ptr, ret_len);
84
+ ruby_xfree(ret_ptr);
85
+
86
+ return ret;
87
+ }
88
+
89
+ /*
90
+ * Holds functions related to HTML namespacing.
91
+ */
92
+ void
93
+ Init_html_namespacing_ext()
94
+ {
95
+ rb_mHtmlNamespacing = rb_define_module("HtmlNamespacing");
96
+ rb_define_module_function(rb_mHtmlNamespacing, "add_namespace_to_html", html_namespacing_add_namespace_to_html, 2);
97
+ }
@@ -0,0 +1,9 @@
1
+ require File.dirname(__FILE__) + '/../ext/html_namespacing/html_namespacing_ext'
2
+
3
+ module HtmlNamespacing
4
+ autoload(:Plugin, File.dirname(__FILE__) + '/html_namespacing/plugin')
5
+
6
+ def self.options
7
+ @options ||= {}
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module HtmlNamespacing
2
+ module Plugin
3
+ autoload(:Rails, File.dirname(__FILE__) + '/plugin/rails')
4
+ autoload(:Sass, File.dirname(__FILE__) + '/plugin/sass')
5
+
6
+ def self.default_relative_path_to_namespace(path)
7
+ path.gsub(/\//, '-')
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ jQuery(function($) {
2
+ function NS_from_node(root_node) {
3
+ var SPLIT_REGEX = /[ \t\n]+/,
4
+ ret = {},
5
+ node_stack = [root_node],
6
+ cur_node,
7
+ class_name_list,
8
+ class_names,
9
+ class_name,
10
+ i,
11
+ j,
12
+ children,
13
+ subnode;
14
+
15
+ while (node_stack.length) {
16
+ cur_node = node_stack.pop();
17
+
18
+ // Populate entry in $.NS
19
+ class_name_list = cur_node.className;
20
+ if (class_name_list) {
21
+ class_names = class_name_list.split(SPLIT_REGEX);
22
+ for (i = 0; i < class_names.length; i++) {
23
+ class_name = class_names[i];
24
+ if (!ret[class_name]) {
25
+ ret[class_name] = [cur_node];
26
+ } else {
27
+ ret[class_name].push(cur_node);
28
+ }
29
+ }
30
+ }
31
+
32
+ // "recurse" to the children, so they are handled in document order
33
+ children = cur_node.childNodes;
34
+ for (var j = children.length - 1; j >= 0; j--) {
35
+ subnode = children[j];
36
+ node_stack.push(subnode);
37
+ }
38
+ }
39
+
40
+ return ret;
41
+ }
42
+
43
+ $.NS = NS_from_node(document.getElementsByTagName('body')[0]);
44
+ });
@@ -0,0 +1 @@
1
+ jQuery(function(b){function a(f){var c=/[ \t\n]+/,m={},o=[f],n,l,p,h,k,g,e,d;while(o.length){n=o.pop();l=n.className;if(l){p=l.split(c);for(k=0;k<p.length;k++){h=p[k];if(!m[h]){m[h]=[n]}else{m[h].push(n)}}}e=n.childNodes;for(var g=e.length-1;g>=0;g--){d=e[g];o.push(d)}}return m}b.NS=a(document.getElementsByTagName("body")[0])});
@@ -0,0 +1,206 @@
1
+ begin
2
+ require 'glob_fu'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'glob_fu'
6
+ end
7
+
8
+ module HtmlNamespacing
9
+ module Plugin
10
+ module Rails
11
+ def self.install(options = {})
12
+ @options = options
13
+ install_rails_2_3(options)
14
+ end
15
+
16
+ # Called by ActionView
17
+ def self.path_to_namespace(relative_path)
18
+ if func = @options[:path_to_namespace_callback]
19
+ func.call(relative_path)
20
+ else
21
+ HtmlNamespacing::Plugin.default_relative_path_to_namespace(relative_path)
22
+ end
23
+ end
24
+
25
+ def self.handle_exception(e, template, view)
26
+ if func = @options[:handle_exception_callback]
27
+ func.call(e, template, view)
28
+ else
29
+ raise(e)
30
+ end
31
+ end
32
+
33
+ def self.javascript_root
34
+ @options[:javascript_root] || RAILS_ROOT + '/app/javascripts/views'
35
+ end
36
+
37
+ def self.stylesheet_root
38
+ @options[:stylesheet_root] || RAILS_ROOT + '/app/stylesheets/views'
39
+ end
40
+
41
+ def self.javascript_optional_suffix
42
+ @options[:javascript_optional_suffix]
43
+ end
44
+
45
+ def self.stylesheet_optional_suffix
46
+ @options[:stylesheet_optional_suffix]
47
+ end
48
+
49
+ def self.template_formats
50
+ @formats ||= Set.new(@options[:template_formats] || ['html'])
51
+ end
52
+
53
+ def self.skip_template?(path)
54
+ specifier = @options[:skip_templates]
55
+ if specifier.respond_to?(:call)
56
+ specifier.call(path)
57
+ elsif specifier.respond_to?(:include?)
58
+ specifier.include?(path)
59
+ else
60
+ specifier === path
61
+ end
62
+ end
63
+
64
+ module Helpers
65
+ #
66
+ # Return the javascript for the rendered partials, wrapped in
67
+ # a script tag.
68
+ #
69
+ def html_namespacing_javascript_tag(framework, options = {})
70
+ js = html_namespacing_javascript(framework, options)
71
+ unless js.blank?
72
+ "<script type=\"text/javascript\"><!--//--><![CDATA[//><!--\n#{js}//--><!]]></script>"
73
+ end
74
+ end
75
+
76
+ #
77
+ # Return the javascript for the rendered partials.
78
+ #
79
+ def html_namespacing_javascript(framework, options = {})
80
+ root = Pathname.new(Rails.javascript_root)
81
+ optional_suffix = Rails.javascript_optional_suffix
82
+ files = html_namespacing_files('js',
83
+ :format => options[:format],
84
+ :optional_suffix => optional_suffix,
85
+ :root => root.to_s)
86
+ unless files.empty?
87
+ r = [HtmlNamespaceJs[framework][:top]] << "\n"
88
+ files.each do |path|
89
+ relative_path = Pathname.new(path).relative_path_from(root)
90
+ r << html_namespacing_inline_js(framework, relative_path.to_s, path) << "\n"
91
+ end
92
+ r.join
93
+ end
94
+ end
95
+
96
+ #
97
+ # Return the CSS for the rendered partials, wrapped in a style
98
+ # tag.
99
+ #
100
+ def html_namespacing_style_tag(options = {}, style_tag_attributes = {})
101
+ css = html_namespacing_styles(options)
102
+ unless css.blank?
103
+ attribute_string = style_tag_attributes.map{|k,v| " #{k}=\"#{v}\""}.join
104
+ "<style type=\"text/css\"#{attribute_string}>#{css}</style>"
105
+ end
106
+ end
107
+
108
+ #
109
+ # Return the CSS for the rendered partials.
110
+ #
111
+ def html_namespacing_styles(options = {})
112
+ root = Pathname.new(Rails.stylesheet_root)
113
+ optional_suffix = Rails.stylesheet_optional_suffix
114
+ files = html_namespacing_files('sass',
115
+ :format => options[:format],
116
+ :optional_suffix => optional_suffix,
117
+ :root => root.to_s)
118
+ unless files.empty?
119
+ r = []
120
+ files.each do |path|
121
+ relative_path = Pathname.new(path).relative_path_from(root)
122
+ r << html_namespacing_inline_css(relative_path.to_s, path)
123
+ end
124
+ r.join
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ HtmlNamespaceJs = {
131
+ :jquery => {
132
+ :top => File.open(File.join(File.dirname(__FILE__), 'dom_scan_jquery.js.compressed')) { |f| f.read },
133
+ :each => 'jQuery(function($){var NS=%s,$NS=function(){return $($.NS[NS]||[])};%s});'
134
+ }
135
+ }
136
+
137
+ def html_namespacing_files(suffix, options={})
138
+ format = options[:format]
139
+ (Array === format ? format : [format]).inject([]) do |r, f|
140
+ r.concat(GlobFu.find(
141
+ html_namespacing_rendered_paths,
142
+ :suffix => suffix,
143
+ :extra_suffix => f && f.to_s,
144
+ :optional_suffix => options[:optional_suffix],
145
+ :root => options[:root]
146
+ ))
147
+ end
148
+ end
149
+
150
+ def html_namespacing_inline_js(framework, relative_path, absolute_path)
151
+ namespace = ::HtmlNamespacing::Plugin::Rails.path_to_namespace(relative_path.sub(/\..*$/, ''))
152
+ content = File.open(absolute_path) { |f| f.read }
153
+
154
+ HtmlNamespaceJs[framework][:each] % [namespace.to_json, content]
155
+ end
156
+
157
+ def html_namespacing_inline_css(relative_path, absolute_path)
158
+ HtmlNamespacing.options[:styles_for].call(relative_path, absolute_path)
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def self.install_rails_2_3(options = {})
165
+ if options[:javascript]
166
+ ::ActionView::Base.class_eval do
167
+ attr_writer(:html_namespacing_rendered_paths)
168
+ def html_namespacing_rendered_paths
169
+ @html_namespacing_rendered_paths ||= []
170
+ end
171
+ end
172
+ end
173
+
174
+ ::ActionView::Template.class_eval do
175
+ def render_with_html_namespacing(view, local_assigns = {})
176
+ html = render_without_html_namespacing(view, local_assigns)
177
+
178
+ return html if HtmlNamespacing::Plugin::Rails.skip_template?(path)
179
+
180
+ view.html_namespacing_rendered_paths << path_without_format_and_extension if view.respond_to?(:html_namespacing_rendered_paths)
181
+
182
+ if HtmlNamespacing::Plugin::Rails.template_formats.include?(format)
183
+ add_namespace_to_html(html, view)
184
+ else
185
+ html
186
+ end
187
+ end
188
+ alias_method_chain :render, :html_namespacing
189
+
190
+ private
191
+
192
+ def add_namespace_to_html(html, view)
193
+ HtmlNamespacing::add_namespace_to_html(html, html_namespace)
194
+ rescue ArgumentError => e
195
+ HtmlNamespacing::Plugin::Rails.handle_exception(e, self, view)
196
+ html # unless handle_exception() raised something
197
+ end
198
+
199
+ def html_namespace
200
+ HtmlNamespacing::Plugin::Rails.path_to_namespace(path_without_format_and_extension)
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,93 @@
1
+ module HtmlNamespacing
2
+ module Plugin
3
+ module Sass
4
+ class << self
5
+ def install(options = {})
6
+ options[:prefix] ||= 'views'
7
+ options[:callback] ||= lambda { |p| HtmlNamespacing::Plugin.default_relative_path_to_namespace(p) }
8
+ options[:compress] ||= false
9
+ @options = options
10
+ Sass_2_2.install(options)
11
+ HtmlNamespacing.options[:styles_for] = method(:styles_for)
12
+ end
13
+
14
+ attr_reader :options
15
+
16
+ def sass_options
17
+ ::Sass::Plugin.options
18
+ end
19
+
20
+ def styles_for(relative_path, absolute_path)
21
+ # Assume the file exists. (If it was found by GlobFu, it should.)
22
+ css_path = File.join(sass_options[:css_location], self.options[:prefix], relative_path.sub(/\.[^\.\/]*\z/, '.css'))
23
+ File.read(css_path)
24
+ end
25
+ end
26
+
27
+ module Sass_2_2
28
+ def self.install(options = {})
29
+ self.allow_updating_partials
30
+ self.add_super_rule_to_tree_node(options)
31
+ self.try_to_enable_memoization
32
+ end
33
+
34
+ private
35
+
36
+ def self.allow_updating_partials
37
+ ::Sass::Plugin.module_eval do
38
+ private
39
+
40
+ # Though a bit over-zealous, who *cares* if we render partial
41
+ # Sass files?
42
+ def forbid_update?(*args)
43
+ false
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.add_super_rule_to_tree_node(namespacing_options)
49
+ ::Sass::Tree::RuleNode.class_eval do
50
+ def to_s_with_namespacing(tabs, super_rules = nil)
51
+ super_rules ||= namespacing_rules
52
+ to_s_without_namespacing(tabs, super_rules)
53
+ end
54
+ alias_method_chain(:to_s, :namespacing)
55
+
56
+ private
57
+
58
+ define_method(:namespacing_prefix) do
59
+ namespacing_options[:prefix]
60
+ end
61
+ define_method(:namespacing_callback) do
62
+ namespacing_options[:callback]
63
+ end
64
+
65
+ def namespacing_regex
66
+ /^#{Regexp.quote(Sass.sass_options[:css_location])}\/#{Regexp.quote(namespacing_prefix)}\/(.*)\.css$/
67
+ end
68
+
69
+ def namespace
70
+ @namespace ||= if options[:css_filename] =~ namespacing_regex
71
+ namespacing_callback.call($1.split('.')[0])
72
+ end
73
+ @namespace
74
+ end
75
+
76
+ def namespacing_rules
77
+ namespace && [ ".#{namespace}" ]
78
+ end
79
+ end
80
+ end
81
+
82
+ def self.try_to_enable_memoization
83
+ ::Sass::Tree::RuleNode.class_eval do
84
+ extend ActiveSupport::Memoizable
85
+ memoize :namespacing_rules
86
+ end
87
+ rescue NameError
88
+ # ActiveSupport isn't loaded
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end