The helper module - methods for use within views.
Default option values should all come from the configuration YAML data, but the user may have elected to delete entries or omit the entire file.
Include CSS and JavaScript related to the YUI tree if it is used by the current view (established by a “YuiTree::ClassMethods#uses_yui_tree” call made within the Controller). Invoke this helper within the HEAD section of an XHTML view using, for example, this line of code:
<%= include_yui_tree_if_used -%>
Note that a trailing newline is output so you can call the helper with the “-%>” closing ERB tag (as shown in the previous paragraph) to avoid inserting a single blank line into your output in the event that the plugin is not used by the current view.
If you like to keep indentation in your rendered HTML files, you may want any lines output by this plugin to be indented too. Pass an optional string to the method to cause all output from this call to be prefixed accordingly.
# File lib/yui_tree/yui_tree.rb, line 430 def include_yui_tree_if_used( line_prefix = nil ) yui_tree_init( line_prefix ) if using_yui_tree? end
Returns ‘true’ if configured to use the YUI tree control for the view related to the current request. See the “uses_yui_tree” class method for more information.
# File lib/yui_tree/yui_tree.rb, line 438 def using_yui_tree? ! @uses_yui_tree.nil? end
Build a YUI tree at the point where the method is called - e.g. place within the body of an XHTML document using “<%= #yui_tree(…) %>”. By default the tree has no option buttons next to entries and is designed to allow the selection of any single item from within it.
An options hash must be passed. It may contain any of the options specified as available in a call to “YuiTree::ClassMethods#uses_yui_tree” and must at least contain values for the following keys unless they have been given values in the YAML configuration file (uncommon) or the view’s corresponding controller’s call to “YuiTree::ClassMethods#uses_yui_tree” (more common):
:xhr_url_method
:target_form_field_id
:select_leaf_only
:root_model
or :root_collection
(see
below)
:root_title_method
(perhaps, if using
:root_model
)
:root_collection
- if your model does not
match the characteristics described for option ":root_model
"
in the "YuiTree::ClassMethods#uses_yui_tree"
method documentation, then you must provide an array of objects defining
the roots of the tree. Construct array entries by calling "YuiTree.make_node_object".
The resulting collection is later turned into a JavaScript-safe array by a
wider call to "to_json" on a JavaScript options hash which gets created by
the YUI tree instantiation helper code; you don't need to worry about HTML
or JavaScript escaping of any values yourself.
The following option values can be specified technically in any of the locations described by the documentation for the “YuiTree::ClassMethods#uses_yui_tree” method, but it is very uncommon to use them anywhere other than a call here, to “#yui_tree”. Each of the items is optional and modifies tree behaviour in some way. Some of the options work together, so use of one may mean you must also use another. Any such relationships are described below:
:include_blank
A string if you want that string to be included at the top of the roots of
the tree as an item that indicates nothing/none/blank - an ID of zero is
associated with it. This option works whether you use the plugin’s
generation of the root dataset via “:root_model
” or if you
generate your own dataset and provide it through
“:root_collection
”. Of course, you can always add your own
blank entry/entries in the latter case, but using the
“:include_blank
” might be more convenient and keep your code
clean.
The reason you pass a string rather than, say, a boolean is so that you can pass any relevant message, including one looked up from an internationalisation locale file (assuming Rails >= 2.3.x).
Note that this string is used if a nodes is deselected so that no nodes are
highlighted and the “:target_name_field_id
” option has been
set up - the string is written as innerHTML of the name field.
:exclude
An ID as an integer, or an object with a method ‘id’ which returns its ID as an integer, or an array of either of these types (they can be mixed in the same array). Specifies items which will not be included in the tree even if the Controller returns them at any point. This works at the node addition level in JS, so it is *not a security feature*. If you don’t want something to be seen by a user, don’t let the Controller send it out in the first place. The exclusion feature is useful if using, say, a tree to assign a parent to an existing item within a hierarchy - you don’t want to let the user try and assign the item itself as a parent, so exclude it.
If you exclude a non-leaf item, it will be shown in the tree but will not be selectable. This way, children can still be selected. At present the code is “sort of” clever enough to know if no children are going to be present due to exclusions, in that it will try to fetch child nodes but when they all get excluded the Yahoo tree view itself will stop trying further and change the node indicator on the parent to being a branch, rather than a toggle control. It does mean one technically unnecessary AJAX request and associated UI update delay though, but it’s a much simpler mechanism than trying to work such things out up-front.
:expand
As “:exclude
”, but specifies nodes which will be automatically
expanded whenever they are added to the tree. Node that if you add a
non-root node to the exclusions list, you must also add any if its parents
(i.e. its complete set of ancestors) back to the root if you want that
whole branch to be expanded automatically. Otherwise the branch starting
from the child will only automatically expand if the user expands the
parent node which contains it.
:highlight
As “:exclude
” but only takes a single item not an array;
specifies an item to be highlighted when the tree is created. Once
highlighted initially, the item is forgotten and will not be
auto-highlighted again. If a non-root item then it will only be highlighted
when the branch containing it is opened; in general you will probably want
to use the “:expand
” option in cojunction with
“:highlight
” for non-root nodes.
:multiple
A boolean (defaults to ‘false
’) which says that multiple items
may be selected in the tree. See below for more.
:form_leaf_nodes_only
If ‘true
’, only the IDs of leaf nodes will be written
into the form field identified by the mandatory
“:target_form_field_id
” option. Defaults to
‘false
’ so that any selected (highlighted) node is included,
leaf or otherwise.
:data_for_xhr_call
An optional string which if included will be added to the parameters used
when the XHR call to retrieve more node items is made. If using the
built-in “YuiTree::ClassMethods#yui_tree_handled_xhr_request?”
method then this is of no use, but if you write a custom handler then the
data string provides a crude but effective way of passing in external data.
Usually, the data is used as some kind of filter, restricting the range of
nodes which the XHR handler method would otherwise have returned. Note that
the string will be run through the ‘j’ helper to make it JS-safe, so some
characters may not be treated quite how you expect - avoid literal single
quotes, double quotes and backslashes in particular. The string will appear
in the params hash under the key “:data
”. An empty string
given here is treated the same way as if the option had been completely
omitted.
The options hash *must not* contain any of the following keys since this would interfere with values set for those same options elsewhere and provoke undefined behaviour (one part of your view would disagree with another part of your view). If present, these keys will be deleted.
:body_class
Any other YUI tree options not listed above are irrelevant here.
Note that you must *never mix* the use of options
“:root_model
” and “:root_collection
” between this
call and any other places where options may be specified; if you do,
results will be undefined.
If ‘:multiple
’ is ‘true
’ the tree allows multiple
check boxes to be selected. Selecting a child does not cause its parents to
change state by default - the user is free to choose any collection of
nodes without constraint - but that can be changed with options listed
below.
Option key “:highlight
” now begins to work as
“:expand
” or “:exclude
” in that it can take a
single item or an array of items to be highlighted. If you use
“:expand
” to auto-expand highlighted nodes, remember that you
need to specify all ancestors of any non-root node you want expanded.
The form field specified in “:target_form_field_id
” has its
value populated using a comma-separated list of IDs of selected items. The
container specified in “:target_name_field_id
”, if any, will
have its value populated using a space-separated list of names of selected
items. You can customise this list of names with the following extra
options only relevant to multiple selection trees using
“:target_name_field_id
”:
:name_field_separator
Text to use as a separator between items in the name field. Defaults to a single space. Must be JS single quoted string safe. May be HTML (e.g. “<br />”).
:name_include_parents
If you want to include the names of all parent nodes whenever a node’s label is used for the name field so that each text entry reflects the whole of the branch of the tree used to reach the node in question, then set this option to a string which is used as a separator between each of the parent items. Must be JS single quoted string safe. May be HTML or even an empty string to directly concatenate parent names. By default names of parents are not included - only the label of the individual node is added to the name field.
:name_leaf_nodes_only
If ‘true
’, use only labels of leaf nodes for name field text;
don’t include others. Useful in conjunction with the
“:name_include_parents
” option, since when the latter is in
use, individual entries in the name field will already indicate the labels
for the complete branch leading to a node so including names of non-leaf
items would only lead to duplication. By default names of all nodes are
included.
Once multiple items are selectable, it becomes desirable to be able to control the YUI tree’s highlight propagation features - that is, when a node is selected, should its parent and/or child nodes be automatically selected as well? The following options control this behaviour. The settings apply to every node in the tree.
:propagate_up
If ‘true
’, selecting any node causes all parent nodes on the
same branch to be selected, if any. By default this option is disabled.
:propagate_down
If ‘true
’, selecting any node causes all child nodes in the
tree below this node, if any, to be selected. By default this option is
disabled.
# File lib/yui_tree/yui_tree.rb, line 637 def yui_tree( options ) # Reject options which are explicitly not allowed here. options.delete( :body_class ) # Merge default from-YAML options, options given in the "uses_yui_tree" # call and mandatory passed-in options for this method. We store the view # options in the main options store to override previously set defaults. defaults = YuiTree.default_options.merge( @yui_tree_options || {} ) options = defaults.merge( options ) # Extract items of interest for which default values exist. xhr_timeout = options.delete( :xhr_timeout_ms ) || YUI_TREE_DEFAULT_TIMEOUT body_class = options.delete( :body_class ) || YUI_TREE_DEFAULT_BODY_CLASS div_class = options.delete( :div_class ) || YUI_TREE_DEFAULT_DIV_CLASS div_id = options.delete( :div_id ) || YUI_TREE_DEFAULT_DIV_ID select_leaf_only = options.delete( :select_leaf_only ) || YUI_TREE_DEFAULT_LEAF_ONLY # Extract other options, raising exceptions if anything mandatory has # been omitted. xhr_url_method = options.delete( :xhr_url_method ) || raise( YUI_MISSING_OPTS_ERROR % :xhr_url_method ) target_form_field_id = options.delete( :target_form_field_id ) || raise( YUI_MISSING_OPTS_ERROR % :target_form_field_id ) target_name_field_id = options.delete( :target_name_field_id ) root_model = options.delete( :root_model ) root_title_method = options.delete( :root_title_method ) || YuiTree::YUI_TREE_DEFAULT_TITLE_METHOD root_collection = options.delete( :root_collection ) include_blank = options.delete( :include_blank ) exclude = options.delete( :exclude ) expand = options.delete( :expand ) highlight = options.delete( :highlight ) form_leaf_nodes_only = options.delete( :form_leaf_nodes_only ) || false data_for_xhr_call = options.delete( :data_for_xhr_call ) multiple = options.delete( :multiple ) || false propagate_up = options.delete( :propagate_up ) || false propagate_down = options.delete( :propagate_down ) || false name_field_separator = options.delete( :name_field_separator ) || ' ' name_include_parents = options.delete( :name_include_parents ) name_leaf_nodes_only = options.delete( :name_leaf_nodes_only ) || false xhr_url = send( xhr_url_method ) # Allow minor API misuse... multiple = true if ( multiple == :true ) multiple = false if ( multiple == :false ) # Build a root collection if working with the "root_model" option. if ( root_model.nil? && root_collection.nil? ) raise ( YUI_MISSING_OPTS_ERROR % 'root_model" or ":root_collection' ) elsif root_collection.nil? root_model = root_model.to_s.constantize root_instances = root_model.roots() if ( root_model.respond_to?( :apply_default_sort_order ) ) root_model.apply_default_sort_order( root_instances ) end root_collection = root_instances.map() do | root_item | YuiTree::make_node_object( root_item.id, root_item.send( root_title_method ), root_item.leaf? ) end end # Add in a blank entry if necessary. unless ( include_blank.nil? ) blank_item = YuiTree::make_node_object( '0', include_blank, true ) root_collection.unshift( blank_item ) end # Build the DIV for the tree then initialise the object for the lengthy # script which manages the tree. Compile and return the HTML and JS data. result = content_tag( :div, '', { :class => "#{ div_class } ygtv-checkbox", :id => div_id } ) + "\n" options_hash = { # Mandatory :divID => div_id, :multiple => !! multiple, :rootCollection => root_collection, :xhrURL => xhr_url, :xhrTimeout => xhr_timeout, :exclude => coerce_to_array( exclude ), :expand => coerce_to_array( expand ), :highlight => coerce_to_array( highlight ), :formFieldID => target_form_field_id, # Optional :bodyClass => body_class, :dataForXHRCall => data_for_xhr_call, :selectLeafOnly => !! select_leaf_only, :propagateUp => !! propagate_up, :propagateDown => !! propagate_down, :nameFieldID => target_name_field_id, :nameLeafNodesOnly => !! name_leaf_nodes_only, :nameFieldSeparator => name_field_separator, :nameFieldBlank => include_blank, :nameIncludeParents => name_include_parents, :formLeafNodesOnly => form_leaf_nodes_only }.delete_if { | k, v | v.nil? } # The use of 'to_json' ensures that strange JS characters get properly # escaped, among other things. js = " var options = #{ options_hash.to_json };\n" << " new uk_org_pond_yui_tree_support( options );" return ( result << javascript_tag( js ) ).html_safe() end