guilded 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +3 -0
  2. data/History.txt +111 -0
  3. data/README.rdoc +219 -0
  4. data/Rakefile +56 -0
  5. data/VERSION +1 -0
  6. data/guilded.gemspec +90 -0
  7. data/lib/guilded.rb +79 -0
  8. data/lib/guilded/browser_detector.rb +217 -0
  9. data/lib/guilded/component_def.rb +24 -0
  10. data/lib/guilded/exceptions.rb +29 -0
  11. data/lib/guilded/guilder.rb +357 -0
  12. data/lib/guilded/rails.rb +4 -0
  13. data/lib/guilded/rails/active_record/human_attribute_hint.rb +41 -0
  14. data/lib/guilded/rails/active_record/human_attribute_override.rb +41 -0
  15. data/lib/guilded/rails/helpers.rb +123 -0
  16. data/lib/guilded/rails/inactive_record/human_attribute_hint.rb +26 -0
  17. data/lib/guilded/rails/view_helpers.rb +147 -0
  18. data/rails_generators/guilded_assets/guilded_assets_generator.rb +23 -0
  19. data/rails_generators/guilded_assets/templates/guilded.js +12 -0
  20. data/rails_generators/guilded_assets/templates/guilded.min.js +1 -0
  21. data/rails_generators/guilded_assets/templates/jquery-1.2.6.js +3549 -0
  22. data/rails_generators/guilded_assets/templates/jquery-1.2.6.min.js +32 -0
  23. data/rails_generators/guilded_assets/templates/jquery-1.3.2.js +4376 -0
  24. data/rails_generators/guilded_assets/templates/jquery-1.3.2.min.js +19 -0
  25. data/rails_generators/guilded_assets/templates/jquery-url.js +199 -0
  26. data/rails_generators/guilded_assets/templates/jquery-url.min.js +9 -0
  27. data/rails_generators/guilded_assets/templates/mootools-1.2.3.js +4036 -0
  28. data/rails_generators/guilded_assets/templates/mootools-1.2.3.min.js +356 -0
  29. data/rails_generators/guilded_assets/templates/reset-min.css +7 -0
  30. data/rails_generators/guilded_config/guilded_config_generator.rb +11 -0
  31. data/rails_generators/guilded_config/templates/load_guilded_settings.rb +13 -0
  32. data/script/console +10 -0
  33. data/script/destroy +14 -0
  34. data/script/generate +14 -0
  35. data/tasks/rails.rake +8 -0
  36. data/tasks/rspec.rake +21 -0
  37. data/test/guilded/browser_detector_test.rb +1248 -0
  38. data/test/guilded/component_def_test.rb +26 -0
  39. data/test/guilded/guilder_test.rb +79 -0
  40. data/test/guilded/rails/helpers_test.rb +1450 -0
  41. data/test/guilded/rails/view_helpers_test.rb +97 -0
  42. data/test/guilded_test.rb +7 -0
  43. data/test/test_helper.rb +47 -0
  44. metadata +112 -0
@@ -0,0 +1,79 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'guilded/browser_detector'
5
+ require 'guilded/component_def'
6
+ require 'guilded/guilder'
7
+ require 'guilded/exceptions'
8
+ require 'guilded/rails/helpers'
9
+ require 'guilded/rails/view_helpers'
10
+ require 'guilded/rails/active_record/human_attribute_hint'
11
+ require 'guilded/rails/active_record/human_attribute_override'
12
+ require 'guilded/rails/inactive_record/human_attribute_hint'
13
+
14
+ # Guilded is a framework for creating reusable UI components for web applications. Guilded provides the facilities to
15
+ # easily add components and all of their required assets to a page.
16
+ #
17
+ # A Guilded component is a set of XHTML, CSS and JavaScript that combine to create a rich UI. The Guilded component should be
18
+ # authored using progressive enhancement to enable users without CSS or JavaScript enabled to utilize the essential functionality
19
+ # of the component. The easiest way to accomplish this requirement is to write your behavior code as a jQuery plugin. Doing
20
+ # so will naturally guide your behavior code to be applied through progrssive enhancement and also allow your behavior code
21
+ # to be used without Guilded.
22
+ #
23
+ # To create a Guilded component at least four things must occur: writing a view helper, writing a JavaScript initialization
24
+ # function, writing a default stylesheet and writing either a rake task or generator to place these assets into a project's
25
+ # public directory. It is also probable that a jQuery plugin with the behavior code will need to be authored. If this is the
26
+ # case, then the JavaScript initialization function will most likely just call the jQuery plugin on the DOM element(s) that should
27
+ # have the behavior applied.
28
+ #
29
+ # The view helper should output the HTML necessary for the component. It must also add the component to a collection so that
30
+ # Guilded can collect its assets and add them to the page. This is accomplished through a call to the Guilder.add() method.
31
+ # The Guilder is a singleton, so you must access it like: Guilded::Guilder.instance.add( ... ). The Guilder.add() method adds
32
+ # element to a collection with it's options. Guilded automatically looks for CSS assets that obey the naming convention
33
+ # {component_name}/default.css. Guilded also automatically looks for a JavaScript asset named: guilded.{component_name}.js.
34
+ # This asset should at least include the initComponentName( options ). The Guilded.add() method can also add additional CSS and
35
+ # JavaScript assets necessary for a component. An example of an additional JavaScript asset is the jQuery plugin authored to
36
+ # contain the behavior of a component. The view helper must be called with an :id option as it is used to differentiate different
37
+ # instances of the same component on a single page from each other. It will also be used as the DOM element's id. A Guilded view
38
+ # helper should have a g_ appended to the beginning of it to help differentiate from normal view helepr sin a project.
39
+ #
40
+ # The JavaScript initialization function should be named init{ComponentName}( options ). The options hash from the view helper
41
+ # will automatically be passed to this function. The function should handle a before and after init callback to enable the user
42
+ # to set up anything necessary for the component and perform additional tasks. This function must also perform any other code
43
+ # necessary to set up the behavior for a compnent. An example fo this would be adding behavior to events or simply calling
44
+ # a jQuery plugin on element(s) of the component. Remember that any JavaScript relating to Guilded should be placed in the 'g'
45
+ # namespace that Guilded creates on every page. An example init function for a g_load_alerter would be:
46
+ #
47
+ # g.initLoadAlerter = function( options )
48
+ # {
49
+ # if( g.beforeInitLoadAlerter )
50
+ # g.beforeInitLoadAlerter( options );
51
+ #
52
+ # // Initialization code here...
53
+ #
54
+ # if( g.afterInitLoadAlerter )
55
+ # g.afterInitLoadAlerter( options );
56
+ # };
57
+ #
58
+ # For a user to implement these callback functions, they should create a JavaScript file with the functions and call it on
59
+ # any page they need it on utilizing Guilded's JavaScript include helper g_javascript_include_tag. This helper will add the
60
+ # JavaScript files after the jQuery and Guilded framework files, but within the component files in the order they were added.
61
+ # This will make the 'g' namespace available for use. Obviously this file must be included prior to calling the component's helper,
62
+ # or the callback functions will not be defined for use.
63
+ #
64
+ # The default stylesheet for a Guilded component should be the minimum amount of CSS for the component to function. When no :skin
65
+ # option is passed to the view helper when a component is added, Guilded will look for the default stylesheet. The assets
66
+ # used within the stylesheet (images) should be placed in a folder named default that is in the same directory as the default.css
67
+ # file. All asssets used in the stylesheet should use an absolute reference from the public directory: /stylesheets/guilded/{component_name}/default/{asset_name}.
68
+ #
69
+ # In order to create another skin for a component, simply create a {skin_name}.css file and a {skin_name} directory with the style
70
+ # assets in it. Be sure to reference the style assets with absolute paths from teh public directory so that the skin will work in
71
+ # all cases. Then call the component's view helper and use the :skin option:
72
+ #
73
+ # <%= g_load_alerter :skin => 'blueish', :id => 'load_alerter' %>
74
+ #
75
+ module Guilded
76
+ VERSION = '0.3.0'
77
+ end
78
+
79
+ ActionView::Base.send( :include, Guilded::Rails::ViewHelpers ) if defined?( ActionView )
@@ -0,0 +1,217 @@
1
+ module Guilded
2
+
3
+ # The BrowserDetector provides the ability to determine browser information from the user
4
+ # agent string.
5
+ #
6
+ class BrowserDetector
7
+
8
+ attr_reader :ua
9
+
10
+ def initialize( user_agent )
11
+ @ua = user_agent.downcase
12
+ @version_regex = /(\d*)\.(\d*)\.*(\d*)\.*(\d*)/
13
+ end
14
+
15
+ # Returns true if the browser matches the options ent in, otherwise returns false.
16
+ #
17
+ # === Request
18
+ # * +request+ - The request object.
19
+ #
20
+ # === Options
21
+ # * +:name+ - The name of the browser. For example 'ie' or :ie.
22
+ # * +:version+ - The version of the browser. For example '3.0.5'.
23
+ # * +:major_version+ - The major version of the browser. For example '3' or 3.
24
+ # * +:minor_version+ - The minor version of the browser. For example '0' or 0.
25
+ #
26
+ def browser_is?( options={} )
27
+ name = options[:name]
28
+ version = options[:version]
29
+ major_version = options[:major_version]
30
+ minor_version = options[:minor_version]
31
+ build_version = options[:build_version]
32
+ revision_version = options[:revision_version]
33
+
34
+ name ||= self.browser_name
35
+ version ||= self.browser_version
36
+ major_version ||= self.browser_version_major
37
+ minor_version ||= self.browser_version_minor
38
+ build_version ||= self.browser_version_build
39
+ revision_version ||= self.browser_version_revision
40
+
41
+ name = name.to_s.strip
42
+ version = version.to_s.strip
43
+ major_version = major_version.to_s.strip
44
+ minor_version = minor_version.to_s.strip
45
+ build_version = build_version.to_s.strip
46
+ revision_version = revision_version.to_s.strip
47
+
48
+ self.browser_name == name && self.browser_version == version && self.browser_version_major == major_version &&
49
+ self.browser_version_minor == minor_version #&& self.browser_version_build == build_version &&
50
+ #self.browser_version_revision == revision_version
51
+ end
52
+
53
+ # Returns the name of the browser that is making this request. For example 'ie'.
54
+ #
55
+ # === Request
56
+ # * +request+ - The request object.
57
+ #
58
+ def browser_name
59
+ begin
60
+ @browser_name ||= begin
61
+ ua = @ua
62
+ if ua.nil?
63
+ 'unknown'
64
+ else
65
+ if ua.index( 'msie' ) && !ua.index( 'opera' ) && !ua.index( 'webtv' )
66
+ if ua.index( 'windows ce' )
67
+ 'ie' + '_ce' #+ ua[ua.index( 'msie' ) + 5].chr
68
+ else
69
+ 'ie' # + ua[ua.index( 'msie' ) + 5].chr
70
+ end
71
+ elsif ua.include?( 'opera' )
72
+ 'opera'
73
+ elsif ua.include?( 'konqueror' )
74
+ 'konqueror'
75
+ elsif ua.include?( 'applewebkit/' )
76
+ 'safari'
77
+ elsif ua.include?( 'chrome' )
78
+ 'chrome'
79
+ #elsif ua.include?( 'mozilla/' )
80
+ # 'firefox'
81
+ elsif ua.include?( 'firefox' )
82
+ 'firefox'
83
+ #elsif ua.include?( 'gecko/' )
84
+ # 'firefox'
85
+ elsif ua.include?( 'netscape' )
86
+ 'netscape'
87
+ else
88
+ 'unknown'
89
+ end
90
+ end
91
+ end
92
+ rescue
93
+ 'unknown'
94
+ end
95
+ end
96
+
97
+ # Returns the version of the browser that is making this request. For example '7'.
98
+ #
99
+ # === Request
100
+ # * +request+ - The request object.
101
+ #
102
+ def browser_version
103
+ @browser_version ||= begin
104
+ self.send( "resolve_version_for_#{self.browser_name}".to_sym )
105
+ end
106
+ end
107
+
108
+ def browser_version_major
109
+ match = @version_regex.match( browser_version )
110
+ return match[1].to_i.to_s unless match.nil? || match.size < 2
111
+ '0'
112
+ end
113
+
114
+ def browser_version_minor
115
+ match = @version_regex.match( browser_version )
116
+ return match[2].to_i.to_s unless match.nil? || match.size < 3
117
+ '0'
118
+ end
119
+
120
+ def browser_version_build
121
+ match = @version_regex.match( browser_version )
122
+ return match[3].to_i.to_s unless match.nil? || match.size < 4 || match[3].empty? || match[3].nil?
123
+ '0'
124
+ end
125
+
126
+ def browser_version_revision
127
+ match = @version_regex.match( browser_version )
128
+ return match[4].to_i.to_s unless match.nil? || match.size < 5 || match[4].empty? || match[4].nil?
129
+ '0'
130
+ end
131
+
132
+ # Returns the browser name concatenated with the browser version. for example, 'ie7'.
133
+ #
134
+ # === Request
135
+ # * +request+ - The request object.
136
+ #
137
+ def browser_full_name
138
+ self.send( "browser_full_name_for_#{self.browser_name}".to_sym )
139
+ end
140
+
141
+ # Returns the browser name concatenated with the browser version. for example, 'ie7'.
142
+ #
143
+ # === Request
144
+ # * +request+ - The request object.
145
+ #
146
+ def browser_id
147
+ browser_name + browser_version.gsub( /\./, '' )
148
+ end
149
+
150
+ # Returns true if the current browser type can handle PNGs.
151
+ #
152
+ def can_use_png?
153
+ return browser_version.to_i >= 7 if browser_name== 'ie'
154
+ true
155
+ end
156
+
157
+ # A list of all the browser types that Guilded recognizes.
158
+ #
159
+ def self.all_browsers
160
+ %w( chrome firefox ie55 ie60 ie70 ie80 konqueror netscape opera safari )
161
+ end
162
+
163
+ # A list of all the mobile browser types that Guilded recognizes.
164
+ #
165
+ def self.all_mobile_browsers
166
+ %w( ie_ce4 iphone )
167
+ end
168
+
169
+ def self.user_agents
170
+ {
171
+ :ie55 => 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.1)',
172
+ :ie60 => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
173
+ :ie70 => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
174
+ :ie80 => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
175
+ :firefox2 => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17',
176
+ :firefox3 => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.0.11) Gecko/2009060214 Firefox/3.0.11',
177
+ :firefox35 => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3',
178
+ :firefox35win => 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3 (.NET CLR 3.5.30729)',
179
+ :opera10 => 'Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.2.15 Version/10.00'
180
+ }
181
+ end
182
+
183
+ protected
184
+
185
+ def resolve_version_for_ie
186
+ match = /.*msie (.*); windows nt 5.1/.match( @ua )
187
+ return match.nil? ? '0' : match[1]
188
+ end
189
+
190
+ def resolve_version_for_firefox
191
+ match = /.*\((.*); u;.*firefox\/(.*) \(.net.*/.match( @ua )
192
+ if match.nil?
193
+ match = /.*\((.*); u;.*firefox\/(.*)/.match( @ua )
194
+ return match.nil? ? '0' : match[2]
195
+ end
196
+ return match.nil? ? '0' : match[2]
197
+ end
198
+
199
+ def resolve_version_for_opera
200
+ match = /.*\((.*); intel.*version\/(.*)/.match( @ua )
201
+ return match.nil? ? '0' : match[2]
202
+ end
203
+
204
+ def browser_full_name_for_ie
205
+ "Internet Explorer #{browser_version}"
206
+ end
207
+
208
+ def browser_full_name_for_firefox
209
+ "Firefox #{browser_version}"
210
+ end
211
+
212
+ def browser_full_name_for_opera
213
+ "Opera #{browser_version}"
214
+ end
215
+
216
+ end
217
+ end
@@ -0,0 +1,24 @@
1
+ module Guilded
2
+ class ComponentDef
3
+
4
+ attr_reader :kind, :options, :libs, :styles
5
+ attr_accessor :additional_js
6
+
7
+ def initialize( kind, options={}, libs=[], styles=[], additional_js='' )
8
+ @kind = kind
9
+ @options = options
10
+ @libs = libs
11
+ @styles = styles
12
+ @additional_js = additional_js
13
+ end
14
+
15
+ def exclude_css?
16
+ options.include?( :exclude_css ) && ( options[:exclude_css] == 'true' || options[:exclude_css] == true )
17
+ end
18
+
19
+ def exclude_js?
20
+ options.include?( :exclude_js ) && ( options[:exclude_js] == 'true' || options[:exclude_js] == true )
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,29 @@
1
+ module Guilded
2
+ module Exceptions
3
+
4
+ class GuildedException < RuntimeError
5
+ def initialize( msg="" ) #:nodoc:
6
+ @msg = msg
7
+ end
8
+ end
9
+
10
+ class IdMissing < GuildedException
11
+ def initialize #:nodoc:
12
+ @msg = ":id for element must be present in the options hash"
13
+ end
14
+ end
15
+
16
+ class DuplicateElementId < GuildedException
17
+ def initialize( id='' ) #:nodoc:
18
+ @msg = ":id #{id} for element is already in use on current page"
19
+ end
20
+ end
21
+
22
+ class MissingConfiguration < GuildedException
23
+ def initialize #:nodoc:
24
+ @msg = "There is no GUILDED_CONFIG instance. Please load Guilded configuration with your apps initialization."
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,357 @@
1
+ require 'digest'
2
+ require 'singleton'
3
+ require 'guilded/exceptions'
4
+
5
+ module Guilded
6
+
7
+ # Guilder is the worker for the entire Guilded framework. It collects all of the necessary components for a page
8
+ # through its add() method. When the g_apply_behavior() method is called at the end of a page, the Guilder writes
9
+ # HTML to include all of the necessary asset files (caching them in production). It also writes s JavaScript initialization
10
+ # function and fires the initialization function on page load.
11
+ #
12
+ # This initialization function calls the initialization function for each Guilded component that was added to the current
13
+ # page. For example, if a Guilded component named 'g_load_alerter' was added to a page, the Guilder would include this line
14
+ # in the initialization function it writes: g.initLoadAlerter( /* passing options hash here */ ); The g before the function
15
+ # is a JavaScript namespace that Guilded automatically creates to facilitate avoiding name collisions with other JavaScript
16
+ # libraries and code.
17
+ #
18
+ # Th options hash that is passed to the init functions for each Guilded component is simply the options hash from the
19
+ # component's view helper. The Guilder calls .to_json() on the options hash. Thus, if there are pairs in the options hash
20
+ # that need not go to the JavaScript init method they should be removed within the view helper.
21
+ #
22
+ class Guilder
23
+ include Singleton
24
+
25
+ GUILDED_NS = "guilded."
26
+
27
+ attr_reader :initialized_at, :jquery_js, :mootools_js
28
+
29
+ def initialize #:nodoc:
30
+ if defined?( GUILDED_CONFIG )
31
+ @config = GUILDED_CONFIG
32
+ else
33
+ raise Guilded::Exceptions::MissingConfiguration
34
+ end
35
+ configure_guilded
36
+ @initialized_at = Time.now
37
+ @g_elements = Hash.new
38
+ @g_data_elements = Hash.new
39
+ @combined_js_srcs = Array.new
40
+ @combined_css_srcs = Array.new
41
+ @assets_combined = false
42
+ # Make sure that the css reset file is first so that other files can override the reset,
43
+ # unless the user specified no reset to be included.
44
+ init_sources
45
+ end
46
+
47
+ # Adds an element with its options to the @g_elements hash to be used later.
48
+ #
49
+ def add( element, options={}, libs=[], styles=[] )
50
+ raise Guilded::Exceptions::IdMissing.new unless options.has_key?( :id )
51
+ raise Guilded::Exceptions::DuplicateElementId.new( options[:id] ) if @g_elements.has_key?( options[:id] )
52
+ @need_mootools = true if options[:mootools]
53
+ @g_elements[ options[:id].to_sym ] = Guilded::ComponentDef.new( element, options, libs, styles )
54
+ end
55
+
56
+ # Adds a data structure to be passed to the Guilded JavaScript environment for use on the client
57
+ # side. The data is passed using the ruby to_json method on the data structure provided.
58
+ #
59
+ # === Parameters
60
+ # * +name+ - The desired name of the variable on the client side.
61
+ # * +data+ - The data to pass to the Guilded JavaScript environment.
62
+ #
63
+ def add_data( name, data )
64
+ @g_data_elements.merge!( name.to_sym => data )
65
+ end
66
+
67
+ # Adds JavaScript sources to the libs collection by resolving them to the normal or min version
68
+ # based on the current running environment, development, production, etc.
69
+ #
70
+ def add_js_sources( *sources )
71
+ resolve_js_libs( *sources )
72
+ end
73
+
74
+ def count #:nodoc:
75
+ @g_elements.size
76
+ end
77
+
78
+ # The number of Guilded components to be renderred.
79
+ #
80
+ def component_count
81
+ count
82
+ end
83
+
84
+ # The current number of CSS assets necessary for the Guilded component set.
85
+ #
86
+ def style_count
87
+ @combined_css_srcs.size
88
+ end
89
+
90
+ # The current number of JavaScript assets necessary for the Guilded component set.
91
+ #
92
+ def script_count
93
+ @combined_js_srcs.size
94
+ end
95
+
96
+ # Returns true if the component type is included, otherwise false.
97
+ #
98
+ def include_component?( type )
99
+ @g_elements.has_key?( type.to_sym )
100
+ end
101
+
102
+ # The collection of JavaScript assets for the current Guilded component set.
103
+ #
104
+ def combined_js_srcs
105
+ #generate_asset_lists unless @assets_combined
106
+ @combined_js_srcs
107
+ end
108
+
109
+ # The collection of CSS assets for the current Guilded component set.
110
+ #
111
+ def combined_css_srcs
112
+ #generate_asset_lists unless @assets_combined
113
+ @combined_css_srcs
114
+ end
115
+
116
+ # Clears out all but the reset CSS and the base JavaScripts
117
+ #
118
+ def reset!
119
+ @combined_css_srcs.clear
120
+ @combined_js_srcs.clear
121
+ @g_elements.clear
122
+ @assets_combined = false
123
+ init_sources
124
+ @default_css_count = @combined_css_srcs.size
125
+ @default_js_count = @combined_js_srcs.size
126
+ end
127
+
128
+ def inject_css( *sources )
129
+ @combined_css_srcs.insert( @default_css_count, *sources )
130
+ end
131
+
132
+ def inject_js( *sources )
133
+ @combined_js_srcs.insert( @default_js_count, *sources )
134
+ end
135
+
136
+ # Generates the markup required to include all the assets necessary for the Guilded compoents in
137
+ # @g_elements collection. Use this if you are not interested in caching asset files.
138
+ #
139
+ def apply #:nodoc:
140
+ to_init = ""
141
+ generate_asset_lists unless @assets_combined
142
+ @combined_css_srcs.each { |css| to_init << "<link href=\"/stylesheets/#{css}\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />" }
143
+ @combined_js_srcs.each { |js| to_init << "<script type=\"text/javascript\" src=\"/javascripts/#{js}\"></script>" }
144
+ to_init << generate_javascript_init
145
+ reset!
146
+ end
147
+
148
+ # Writes an initialization method that calls each Guilded components initialization method. This
149
+ # method will exceute on document load finish.
150
+ #
151
+ def generate_javascript_init #:nodoc:
152
+ code = "<script type=\"text/javascript\">"
153
+ code << "var initGuildedElements = function(){"
154
+ @g_data_elements.each do |name, data|
155
+ code << "g.#{name} = #{data.to_json};"
156
+ end
157
+ @g_elements.each_value do |guilded_def|
158
+ code << "g.#{guilded_def.kind.to_s.camelize( :lower )}Init(#{guilded_def.options.to_json});" unless guilded_def.exclude_js?
159
+ end
160
+ code << "jQuery('body').trigger('guildedInitialized');};jQuery('document').ready(initGuildedElements);</script>"
161
+ end
162
+
163
+ # Generates a name to use when caching the current set of Guilded component JavaScript assets. Sorts and concatenates
164
+ # the name of each JavaScript asset in @combined_js_srcs. Then hashes this string to generate a reproducible, unique
165
+ # and shorter string.
166
+ #
167
+ def js_cache_name
168
+ generate_js_cache_name( @combined_js_srcs )
169
+ end
170
+
171
+ # Generates a name to use when caching the current set of Guilded component CSS assets. Sorts and concatenates
172
+ # the name of each JavaScript asset in @combined_js_srcs. Then hashes this string to generate a reproducible, unique
173
+ # and shorter string.
174
+ #
175
+ def css_cache_name
176
+ generate_css_cache_name( @combined_css_srcs )
177
+ end
178
+
179
+ def generate_js_cache_name( sources ) #:nodoc:
180
+ generate_asset_lists unless @assets_combined
181
+ #return"#{controller.class.to_s.underscore}_#{controller.action_name}" if development?
182
+ sorted_srcs = sources.sort
183
+ stripped_srcs = sorted_srcs.map { |src| src.gsub( /.js/, '' ).gsub( /\//, '_') }
184
+ joined = stripped_srcs.join( "+" )
185
+ "#{Digest::MD5.hexdigest( joined )}"
186
+ end
187
+
188
+ def generate_css_cache_name( sources ) #:nodoc:
189
+ generate_asset_lists unless @assets_combined
190
+ #return "#{controller.class.to_s.underscore}_#{controller.action_name}" if development?
191
+ sorted_srcs = sources.sort
192
+ stripped_srcs = sorted_srcs.map { |src| src.gsub( /.css/, '' ).gsub( /\//, '_') }
193
+ joined = stripped_srcs.join( "+" )
194
+ "#{Digest::MD5.hexdigest( joined )}"
195
+ end
196
+
197
+ protected
198
+
199
+ def configure_guilded #:nodoc:
200
+ @js_path = @config[:js_path]
201
+ @js_folder = @config[:js_folder]
202
+ @jquery_js = @config[:jquery_js]
203
+ @mootools_js = @config[:mootools_js]
204
+ @jquery_folder = @config[:jquery_folder] || 'jquery/'
205
+ @mootools_folder = @config[:mootools_folder] || 'mootools/'
206
+ @guilded_js = 'guilded.min.js'
207
+ @url_js = 'jquery-url.min.js'
208
+ @css_path = @config[:css_path]
209
+ @css_folder = @config[:css_folder]
210
+ @reset_css = @config[:reset_css]
211
+ #@do_reset_css = @config[:do_reset_css]
212
+ @env = @config[:environment]
213
+ @env ||= :production
214
+ @js_path.freeze
215
+ @css_path.freeze
216
+ @js_folder.freeze
217
+ @guilded_js.freeze
218
+ @url_js.freeze
219
+ @jquery_js.freeze
220
+ @jquery_folder.freeze
221
+ @mootols_js.freeze
222
+ @mootools_folder.freeze
223
+ @guilded_js.freeze
224
+ @css_folder.freeze
225
+ @reset_css.freeze
226
+ #@do_reset_css.freeze
227
+ @env.freeze
228
+ end
229
+
230
+ # Adds the Guilded reset CSS file and the guilded.js and jQuery files to the respective sources
231
+ # collections.
232
+ #
233
+ def init_sources #:nodoc:
234
+ @combined_css_srcs << "#{@reset_css}" unless @reset_css.nil? || @reset_css.empty?
235
+ resolve_js_libs( "#{@jquery_js}", "#{@jquery_folder}#{@url_js}", "#{@js_folder}#{@guilded_js}" )
236
+ end
237
+
238
+ # Combines all JavaScript and CSS files into lists to include based on what Guilded components are on
239
+ # the current page.
240
+ #
241
+ def generate_asset_lists #:nodoc:
242
+ @assets_combined = true
243
+ @g_elements.each_value do |defi|
244
+ #TODO get stylesheet (skin) stuff using rails caching
245
+ combine_css_sources( defi.kind, defi.options[:skin], defi.styles ) unless defi.exclude_css?
246
+
247
+ # Combine all JavaScript sources so that the caching option can be used on
248
+ # the javascript_incldue_tag helper.
249
+ combine_js_sources( defi.kind, defi.libs ) unless defi.exclude_js?
250
+ end
251
+ end
252
+
253
+ # Helper method that takes the libs and component specific js files and puts them
254
+ # into one array so that the javascript_include_tag can correctly cache them. Automatically
255
+ # ignores files that have already been inlcuded.
256
+ #
257
+ # *parameters*
258
+ # combined_src (required) An array of the combined sorces for the page being renderred.
259
+ # component (required) The name of a guilded component.
260
+ # libs An array of JavaScript libraries that this component depends on. More than likely
261
+ # a jQuery plugin, etc.
262
+ #
263
+ def combine_js_sources( component, libs=[] ) #:nodoc:
264
+ libs << @mootools_js if @need_mootools
265
+ resolve_js_libs( *libs )
266
+
267
+ comp_src = add_guilded_js_path( component )
268
+ @combined_js_srcs.push( comp_src ) unless @combined_js_srcs.include?( comp_src )
269
+ end
270
+
271
+ # Helper method that adds the aditional JavaScript library icludes to the include set.
272
+ #
273
+ # If running development mode, it will try to remove any .pack, .min, or.compressed
274
+ # parts fo the name to try and get the debug version of the library. If it cannot
275
+ # find the debug version of the file, it will just remain what was initially provded.
276
+ #
277
+ def resolve_js_libs( *libs ) #:nodoc:
278
+ if development?
279
+ # Try to use an unpacked or unminimized version
280
+ libs.each do |lib|
281
+ debug_lib = lib.gsub( /.pack/, '' ).gsub( /.min/, '' ).gsub( /.compressed/, '' )
282
+ path = "#{RAILS_ROOT}/public/javascripts/#{debug_lib}"
283
+ if File.exist?( path )
284
+ @combined_js_srcs.push( debug_lib ) unless @combined_js_srcs.include?( debug_lib )
285
+ else
286
+ @combined_js_srcs.push( lib ) unless @combined_js_srcs.include?( lib )
287
+ end
288
+ end
289
+ else
290
+ libs.each { |lib| @combined_js_srcs.push( lib ) unless @combined_js_srcs.include?( lib ) }
291
+ end
292
+ end
293
+
294
+ # Helper method that takes an array of js sources and adds the correct guilded
295
+ # path to them. Returns an array with the new path resolved sources.
296
+ #
297
+ def map_guilded_js_paths( *sources ) #:nodoc:
298
+ sources.map { |source| add_guilded_js_path( source ) }
299
+ end
300
+
301
+ # Adds the guilded JS path to the the source name passed in. When not in development mode,
302
+ # it looks for a .pack.js, .min.jsm .compressed.js and chooses one of these over the
303
+ # development version.
304
+ #
305
+ def add_guilded_js_path( source ) #:nodoc:
306
+ part = "#{@js_folder}#{GUILDED_NS}#{source.to_s}"
307
+ ext = 'js'
308
+
309
+ return "#{part}.#{ext}" unless production?
310
+
311
+ possibles = [ "#{@js_path}#{part}.min.#{ext}", "#{@js_path}#{part}.pack.#{ext}", "#{@js_path}#{part}.compressed.#{ext}",
312
+ "#{@js_path}#{part}.#{ext}" ]
313
+ parts = [ "#{part}.min.#{ext}", "{part}.pack.#{ext}", "#{part}.compressed.#{ext}", "#{part}.#{ext}" ]
314
+
315
+ possibles.each_with_index do |full_path, i|
316
+ return parts[i] if File.exists?( full_path )
317
+ end
318
+
319
+ "" # Should never reach here
320
+ end
321
+
322
+ def combine_css_sources( component, skin, styles=[] ) #:nodoc:
323
+ # Get all of this components defined external styles
324
+ styles.each do |style|
325
+ @combined_css_srcs.push( style ) unless @combined_css_srcs.include?( style )
326
+ end
327
+
328
+ #Get the default or guilded skin styles for this component
329
+ comp_src = add_guilded_css_path( component, skin )
330
+ @combined_css_srcs.push( comp_src ) unless @combined_css_srcs.include?( comp_src ) || comp_src.empty?
331
+ user_src = add_guilded_css_path( component, "user" )
332
+ @combined_css_srcs.push( user_src ) unless @combined_css_srcs.include?( user_src ) || user_src.empty?
333
+ skin_user_src = add_guilded_css_path( component, "#{skin || 'default'}_user" )
334
+ @combined_css_srcs.push( skin_user_src ) unless @combined_css_srcs.include?( skin_user_src ) || skin_user_src.empty?
335
+ end
336
+
337
+ def add_guilded_css_path( source, skin ) #:nodoc:
338
+ skin = 'default' if skin.nil? || skin.empty?
339
+ part = "#{@css_folder}#{source.to_s}/#{skin}.css"
340
+ path = "#{@css_path}#{part}"
341
+ File.exists?( path ) ? part : ''
342
+ end
343
+
344
+ def development? #:nodoc:
345
+ @env.to_sym == :development
346
+ end
347
+
348
+ def production? #:nodoc:
349
+ @env.to_sym == :production
350
+ end
351
+
352
+ def test? #:nodoc:
353
+ @env.to_sym == :test
354
+ end
355
+
356
+ end
357
+ end