guilded 1.0.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 (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