patch-html_namespacing 0.2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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