Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial checkin

  • Loading branch information...
commit b4fb1e24414d7a45d5a1b2e4a49a2aac16ca9966 0 parents
Wynn Netherland authored
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 [name of plugin creator]
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
64 README
@@ -0,0 +1,64 @@
+=UnobtrusiveSortHelper
+
+
+This is a plugin version of Stuart Rackham's most excellent SortHelper helper. The link_to_remote ajax calls have been removed in favor of unobtrusive javascript using lowpro.js.
+
+=== Features
+
+- Consecutive clicks toggle the column's sort order.
+- Sort state is maintained by a session hash entry.
+- Icon image identifies sort column and state.
+- Typically used in conjunction with will_paginate plugin
+
+=== Usage
+
+====Controller:
+
+ def list
+ sort_init 'last_name'
+ sort_update
+ @items = Contact.find_all nil, sort_clause
+ end
+
+====Controller (using will_paginate)
+
+ def list
+ sort_init 'last_name'
+ sort_update
+
+ options = {:page => params[:page], :include => :addresses, :order => sort_clause
+
+ @contacts = Contact.paginate(options)
+ end
+
+====Layout (app/views/layouts/application.html.erb):
+
+ <%= stylesheet_link_tag "ujs_sort_helper"%>
+styles for nifty sort arrow images
+
+ <%= javascript_include_tag :defaults%>
+as long as prototype.js is included before lowpro and ujs_sort_helper
+
+ <%= javascript_include_tag "lowpro"%>
+you SHOULD already have this ;-)
+
+ <%= javascript_include_tag "usj_sort_helper"%>
+this is where ujs comes in
+====View (table header in index.rhtml):
+
+ <thead>
+ <tr>
+ <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
+ <%= sort_header_tag('last_name', :caption => 'Name') %>
+ <%= sort_header_tag('phone') %>
+ <%= sort_header_tag('address', :width => 200) %>
+ </tr>
+ </thead>
+
+- The ascending and descending sort icon images are sort_asc.png and sort_desc.png and reside in the application's images directory.
+- Introduces instance variables: @sort_name, @sort_default.
+- Introduces params :sort_key and :sort_order.
+
+
+
+Copyright (c) 2005 Stuart Rackham, (c) 2008 Wynn Netherland released under the MIT license
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the unobtrusive_sort_helper plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the unobtrusive_sort_helper plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'UnobtrusiveSortHelper'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
11 init.rb
@@ -0,0 +1,11 @@
+require 'unobtrusive_sort_helper'
+
+ActionView::Base.send(:include, UnobtrusiveSortHelper)
+ActionController::Base.send(:include, UnobtrusiveSortHelper)
+
+# install files
+['/public/javascripts', '/public/stylesheets', '/public/images'].each{|dir|
+ source = File.join(directory,dir)
+ dest = RAILS_ROOT + dir
+ FileUtils.cp_r(Dir.glob(source+'/*.*'), dest)
+} unless File.exists?(RAILS_ROOT + '/public/javascripts/ujs_sort_helper.js')
157 lib/unobtrusive_sort_helper.rb
@@ -0,0 +1,157 @@
+# Helpers to sort tables using clickable column headers.
+#
+# Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
+# Modified by: Wynn Netherland <wynn@squeejee.com>, February 2008 to use UJS
+# License: This source code is released under the MIT license.
+#
+# - Consecutive clicks toggle the column's sort order.
+# - Sort state is maintained by a session hash entry.
+# - Icon image identifies sort column and state.
+# - Typically used in conjunction with the Pagination module.
+#
+# Example code snippets:
+#
+# Controller:
+#
+# helper :sort
+# include SortHelper
+#
+# def list
+# sort_init 'last_name'
+# sort_update
+# @items = Contact.find_all nil, sort_clause
+# end
+#
+# Controller (using Pagination module):
+#
+# helper :sort
+# include SortHelper
+#
+# def list
+# sort_init 'last_name'
+# sort_update
+# @contact_pages, @items = paginate :contacts,
+# :order_by => sort_clause,
+# :per_page => 10
+# end
+#
+# View (table header in list.rhtml):
+#
+# <thead>
+# <tr>
+# <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
+# <%= sort_header_tag('last_name', :caption => 'Name') %>
+# <%= sort_header_tag('phone') %>
+# <%= sort_header_tag('address', :width => 200) %>
+# </tr>
+# </thead>
+#
+# - The ascending and descending sort icon images are sort_asc.png and
+# sort_desc.png and reside in the application's images directory.
+# - Introduces instance variables: @sort_name, @sort_default.
+# - Introduces params :sort_key and :sort_order.
+#
+module UnobtrusiveSortHelper
+
+ # Initializes the default sort column (default_key) and sort order
+ # (default_order).
+ #
+ # - default_key is a column attribute name.
+ # - default_order is 'asc' or 'desc'.
+ # - name is the name of the session hash entry that stores the sort state,
+ # defaults to '<controller_name>_sort'.
+ #
+ def sort_init(default_key, default_order='asc', name=nil)
+ @sort_name = name || params[:controller] + params[:action] + '_sort'
+ @sort_default = {:key => default_key, :order => default_order}
+ end
+
+ # Updates the sort state. Call this in the controller prior to calling
+ # sort_clause.
+ #
+ def sort_update()
+ if params[:sort_key]
+ sort = {:key => params[:sort_key], :order => params[:sort_order]}
+ elsif session[@sort_name]
+ sort = session[@sort_name] # Previous sort.
+ else
+ sort = @sort_default
+ end
+ session[@sort_name] = sort
+ end
+
+ # Returns an SQL sort clause corresponding to the current sort state.
+ # Use this to sort the controller's table items collection.
+ #
+ def sort_clause()
+ session[@sort_name][:key] + ' ' + session[@sort_name][:order]
+ end
+
+ # Returns a link which sorts by the named column.
+ #
+ # - column is the name of an attribute in the sorted record collection.
+ # - The optional caption explicitly specifies the displayed link text.
+ # - A sort icon image is positioned to the right of the sort link.
+ #
+ def sort_link(column, caption=nil, initial_order="asc")
+
+ key, order = session[@sort_name][:key], session[@sort_name][:order]
+ if key == column
+ if order.downcase == 'asc'
+ icon = 'sort_asc.png'
+ order = 'desc'
+ else
+ icon = 'sort_desc.png'
+ order = 'asc'
+ end
+ else
+ icon = nil
+ order = initial_order # JPM - Updated original helper to allow for a dynamic initial sort order
+ end
+ caption = titleize(Inflector::humanize(column)) unless caption
+
+ url = { :sort_key => column, :sort_order => order, :filter => params[:filter]}
+ url.merge!({:q => params[:q]}) unless params[:q].nil?
+
+ link_to(caption, url, :class => "sort_link #{order if key == column}")
+ end
+
+ # Returns a table header <th> tag with a sort link for the named column
+ # attribute.
+ #
+ # Options:
+ # :caption The displayed link name (defaults to titleized column name).
+ # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
+ #
+ # Other options hash entries generate additional table header tag attributes.
+ #
+ # Example:
+ #
+ # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
+ #
+ # Renders:
+ #
+ # <th title="Sort by contact ID" width="40">
+ # <a href="/contact/list?sort_order=desc&amp;sort_key=id">Id</a>
+ # </th>
+ #
+ def sort_header_tag(column, options = {})
+ options[:initial_order].nil? ? initial_order = "asc" : initial_order = options[:initial_order]
+ caption = options.delete(:caption) || titleize(Inflector::humanize(column))
+ #options[:title] = caption unless options[:title]
+ content_tag('th', sort_link(column, caption, initial_order), options.except(:initial_order))
+ end
+
+ private
+
+ # Return n non-breaking spaces.
+ def nbsp(n)
+ '&nbsp;' * n
+ end
+
+ # Return capitalized title.
+ def titleize(title)
+ title.split.map {|w| w.capitalize }.join(' ')
+ end
+
+end
BIN  public/images/sort_asc.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN  public/images/sort_desc.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
320 public/javascripts/lowpro.js
@@ -0,0 +1,320 @@
+LowPro = {};
+LowPro.Version = '0.5';
+LowPro.CompatibleWithPrototype = '1.6';
+
+if (Prototype.Version.indexOf(LowPro.CompatibleWithPrototype) != 0 && console && console.warn)
+ console.warn("This version of Low Pro is tested with Prototype " + LowPro.CompatibleWithPrototype +
+ " it may not work as expected with this version (" + Prototype.Version + ")");
+
+if (!Element.addMethods)
+ Element.addMethods = function(o) { Object.extend(Element.Methods, o) };
+
+// Simple utility methods for working with the DOM
+DOM = {};
+
+// DOMBuilder for prototype
+DOM.Builder = {
+ tagFunc : function(tag) {
+ return function() {
+ var attrs, children;
+ if (arguments.length>0) {
+ if (arguments[0].nodeName ||
+ typeof arguments[0] == "string")
+ children = arguments;
+ else {
+ attrs = arguments[0];
+ children = Array.prototype.slice.call(arguments, 1);
+ };
+ }
+ return DOM.Builder.create(tag, attrs, children);
+ };
+ },
+ create : function(tag, attrs, children) {
+ attrs = attrs || {}; children = children || []; tag = tag.toLowerCase();
+ var el = new Element(tag, attrs);
+
+ for (var i=0; i<children.length; i++) {
+ if (typeof children[i] == 'string')
+ children[i] = document.createTextNode(children[i]);
+ el.appendChild(children[i]);
+ }
+ return $(el);
+ }
+};
+
+// Automatically create node builders as $tagName.
+(function() {
+ var els = ("p|div|span|strong|em|img|table|tr|td|th|thead|tbody|tfoot|pre|code|" +
+ "h1|h2|h3|h4|h5|h6|ul|ol|li|form|input|textarea|legend|fieldset|" +
+ "select|option|blockquote|cite|br|hr|dd|dl|dt|address|a|button|abbr|acronym|" +
+ "script|link|style|bdo|ins|del|object|param|col|colgroup|optgroup|caption|" +
+ "label|dfn|kbd|samp|var").split("|");
+ var el, i=0;
+ while (el = els[i++])
+ window['$' + el] = DOM.Builder.tagFunc(el);
+})();
+
+DOM.Builder.fromHTML = function(html) {
+ var root;
+ if (!(root = arguments.callee._root))
+ root = arguments.callee._root = document.createElement('div');
+ root.innerHTML = html;
+ return root.childNodes[0];
+};
+
+
+
+// Wraps the 1.6 contentloaded event for backwards compatibility
+//
+// Usage:
+//
+// Event.onReady(callbackFunction);
+Object.extend(Event, {
+ onReady : function(f) {
+ if (document.body) f();
+ else document.observe('dom:loaded', f);
+ }
+});
+
+// Based on event:Selectors by Justin Palmer
+// http://encytemedia.com/event-selectors/
+//
+// Usage:
+//
+// Event.addBehavior({
+// "selector:event" : function(event) { /* event handler. this refers to the element. */ },
+// "selector" : function() { /* runs function on dom ready. this refers to the element. */ }
+// ...
+// });
+//
+// Multiple calls will add to exisiting rules. Event.addBehavior.reassignAfterAjax and
+// Event.addBehavior.autoTrigger can be adjusted to needs.
+Event.addBehavior = function(rules) {
+ var ab = this.addBehavior;
+ Object.extend(ab.rules, rules);
+
+ if (!ab.responderApplied) {
+ Ajax.Responders.register({
+ onComplete : function() {
+ if (Event.addBehavior.reassignAfterAjax)
+ setTimeout(function() { ab.reload() }, 10);
+ }
+ });
+ ab.responderApplied = true;
+ }
+
+ if (ab.autoTrigger) {
+ this.onReady(ab.load.bind(ab, rules));
+ }
+
+};
+
+Object.extend(Event.addBehavior, {
+ rules : {}, cache : [],
+ reassignAfterAjax : false,
+ autoTrigger : true,
+
+ load : function(rules) {
+ for (var selector in rules) {
+ var observer = rules[selector];
+ var sels = selector.split(',');
+ sels.each(function(sel) {
+ var parts = sel.split(/:(?=[a-z]+$)/), css = parts[0], event = parts[1];
+ $$(css).each(function(element) {
+ if (event) {
+ observer = Event.addBehavior._wrapObserver(observer);
+ $(element).observe(event, observer);
+ Event.addBehavior.cache.push([element, event, observer]);
+ } else {
+ if (!element.$$assigned || !element.$$assigned.include(observer)) {
+ if (observer.attach) observer.attach(element);
+
+ else observer.call($(element));
+ element.$$assigned = element.$$assigned || [];
+ element.$$assigned.push(observer);
+ }
+ }
+ });
+ });
+ }
+ },
+
+ unload : function() {
+ this.cache.each(function(c) {
+ Event.stopObserving.apply(Event, c);
+ });
+ this.cache = [];
+ },
+
+ reload: function() {
+ var ab = Event.addBehavior;
+ ab.unload();
+ ab.load(ab.rules);
+ },
+
+ _wrapObserver: function(observer) {
+ return function(event) {
+ if (observer.call(this, event) === false) event.stop();
+ }
+ }
+
+});
+
+Event.observe(window, 'unload', Event.addBehavior.unload.bind(Event.addBehavior));
+
+// A silly Prototype style shortcut for the reckless
+$$$ = Event.addBehavior.bind(Event);
+
+// Behaviors can be bound to elements to provide an object orientated way of controlling elements
+// and their behavior. Use Behavior.create() to make a new behavior class then use attach() to
+// glue it to an element. Each element then gets it's own instance of the behavior and any
+// methods called onxxx are bound to the relevent event.
+//
+// Usage:
+//
+// var MyBehavior = Behavior.create({
+// onmouseover : function() { this.element.addClassName('bong') }
+// });
+//
+// Event.addBehavior({ 'a.rollover' : MyBehavior });
+//
+// If you need to pass additional values to initialize use:
+//
+// Event.addBehavior({ 'a.rollover' : MyBehavior(10, { thing : 15 }) })
+//
+// You can also use the attach() method. If you specify extra arguments to attach they get passed to initialize.
+//
+// MyBehavior.attach(el, values, to, init);
+//
+// Finally, the rawest method is using the new constructor normally:
+// var draggable = new Draggable(element, init, vals);
+//
+// Each behaviour has a collection of all its instances in Behavior.instances
+//
+var Behavior = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ var behavior = function() {
+ var behavior = arguments.callee;
+ if (!this.initialize) {
+ var args = $A(arguments);
+
+ return function() {
+ var initArgs = [this].concat(args);
+ behavior.attach.apply(behavior, initArgs);
+ };
+ } else {
+ var args = (arguments.length == 2 && arguments[1] instanceof Array) ?
+ arguments[1] : Array.prototype.slice.call(arguments, 1);
+
+ this.element = $(arguments[0]);
+ this.initialize.apply(this, args);
+ behavior._bindEvents(this);
+ behavior.instances.push(this);
+ }
+ };
+
+ Object.extend(behavior, Class.Methods);
+ Object.extend(behavior, Behavior.Methods);
+ behavior.superclass = parent;
+ behavior.subclasses = [];
+ behavior.instances = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ behavior.prototype = new subclass;
+ parent.subclasses.push(behavior);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ behavior.addMethods(properties[i]);
+
+ if (!behavior.prototype.initialize)
+ behavior.prototype.initialize = Prototype.emptyFunction;
+
+ behavior.prototype.constructor = behavior;
+
+ return behavior;
+ },
+ Methods : {
+ attach : function(element) {
+ return new this(element, Array.prototype.slice.call(arguments, 1));
+ },
+ _bindEvents : function(bound) {
+ for (var member in bound)
+ if (member.match(/^on(.+)/) && typeof bound[member] == 'function')
+ bound.element.observe(RegExp.$1, Event.addBehavior._wrapObserver(bound[member].bindAsEventListener(bound)));
+ }
+ }
+};
+
+Remote = Behavior.create({
+ initialize: function(options) {
+ if (this.element.nodeName == 'FORM') new Remote.Form(this.element, options);
+ else new Remote.Link(this.element, options);
+ }
+});
+
+Remote.Base = {
+ initialize : function(options) {
+ this.options = Object.extend({
+ evaluateScripts : true
+ }, options || {});
+ },
+ _makeRequest : function(options) {
+ if (options.update) new Ajax.Updater(options.update, options.url, options);
+ else new Ajax.Request(options.url, options);
+ return false;
+ }
+}
+
+Remote.Link = Behavior.create(Remote.Base, {
+ onclick : function() {
+ var options = Object.extend({ url : this.element.href, method : 'get' }, this.options);
+ return this._makeRequest(options);
+ }
+});
+
+
+Remote.Form = Behavior.create(Remote.Base, {
+ onclick : function(e) {
+ var sourceElement = e.element();
+
+ if (['input', 'button'].include(sourceElement.nodeName.toLowerCase()) &&
+ sourceElement.type == 'submit')
+ this._submitButton = sourceElement;
+ },
+ onsubmit : function() {
+ var options = Object.extend({
+ url : this.element.action,
+ method : this.element.method || 'get',
+ parameters : this.element.serialize({ submit: this._submitButton.name })
+ }, this.options);
+ this._submitButton = null;
+ return this._makeRequest(options);
+ }
+});
+
+Observed = Behavior.create({
+ initialize : function(callback, options) {
+ this.callback = callback.bind(this);
+ this.options = options || {};
+ this.observer = (this.element.nodeName == 'FORM') ? this._observeForm() : this._observeField();
+ },
+ stop: function() {
+ this.observer.stop();
+ },
+ _observeForm: function() {
+ return (this.options.frequency) ? new Form.Observer(this.element, this.options.frequency, this.callback) :
+ new Form.EventObserver(this.element, this.callback);
+ },
+ _observeField: function() {
+ return (this.options.frequency) ? new Form.Element.Observer(this.element, this.options.frequency, this.callback) :
+ new Form.Element.EventObserver(this.element, this.callback);
+ }
+});
+
13 public/javascripts/ujs_sort_helper.js
@@ -0,0 +1,13 @@
+Event.addBehavior({
+ 'a.sort_link:click': function(e) {
+ new Ajax.Updater('content', this.href, {asynchronous:true, evalScripts:true, method:'get'});
+ return false;
+ },
+ 'div.pagination a:click': function(e) {
+ new Ajax.Updater('content', this.href, {asynchronous:true, evalScripts:true, method:'get'});
+ return false;
+ }
+});
+
+Event.addBehavior.reassignAfterAjax = true;
+
9 public/stylesheets/ujs_sort_helper.css
@@ -0,0 +1,9 @@
+th a.asc {
+ background: url(/images/sort_desc.png) center right no-repeat;
+ padding-right: 10px;
+}
+
+th a.desc {
+ background: url(/images/sort_asc.png) center right no-repeat;
+ padding-right: 10px;
+}
420 rdoc/classes/UnobtrusiveSortHelper.html
@@ -0,0 +1,420 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Module: UnobtrusiveSortHelper</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
+ <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
+ <script type="text/javascript">
+ // <![CDATA[
+
+ function popupCode( url ) {
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
+ }
+
+ function toggleCode( id ) {
+ if ( document.getElementById )
+ elem = document.getElementById( id );
+ else if ( document.all )
+ elem = eval( "document.all." + id );
+ else
+ return false;
+
+ elemStyle = elem.style;
+
+ if ( elemStyle.display != "block" ) {
+ elemStyle.display = "block"
+ } else {
+ elemStyle.display = "none"
+ }
+
+ return true;
+ }
+
+ // Make codeblocks hidden by default
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
+
+ // ]]>
+ </script>
+
+</head>
+<body>
+
+
+
+ <div id="classHeader">
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong>Module</strong></td>
+ <td class="class-name-in-header">UnobtrusiveSortHelper</td>
+ </tr>
+ <tr class="top-aligned-row">
+ <td><strong>In:</strong></td>
+ <td>
+ <a href="../files/lib/unobtrusive_sort_helper_rb.html">
+ lib/unobtrusive_sort_helper.rb
+ </a>
+ <br />
+ </td>
+ </tr>
+
+ </table>
+ </div>
+ <!-- banner header -->
+
+ <div id="bodyContent">
+
+
+
+ <div id="contextContent">
+
+ <div id="description">
+ <p>
+Helpers to sort tables using clickable column headers.
+</p>
+<p>
+Author: Stuart Rackham &lt;srackham@methods.co.nz&gt;, March 2005. Modified
+by: Wynn Netherland &lt;wynn@squeejee.com&gt;, February 2008 to use UJS
+License: This source code is released under the MIT license.
+</p>
+<ul>
+<li>Consecutive clicks toggle the column&#8216;s sort order.
+
+</li>
+<li>Sort state is maintained by a session hash entry.
+
+</li>
+<li>Icon image identifies sort column and state.
+
+</li>
+<li>Typically used in conjunction with the Pagination module.
+
+</li>
+</ul>
+<p>
+Example code snippets:
+</p>
+<p>
+Controller:
+</p>
+<pre>
+ helper :sort
+ include SortHelper
+
+ def list
+ sort_init 'last_name'
+ sort_update
+ @items = Contact.find_all nil, sort_clause
+ end
+</pre>
+<p>
+Controller (using Pagination module):
+</p>
+<pre>
+ helper :sort
+ include SortHelper
+
+ def list
+ sort_init 'last_name'
+ sort_update
+ @contact_pages, @items = paginate :contacts,
+ :order_by =&gt; sort_clause,
+ :per_page =&gt; 10
+ end
+</pre>
+<p>
+View (table header in list.rhtml):
+</p>
+<pre>
+ &lt;thead&gt;
+ &lt;tr&gt;
+ &lt;%= sort_header_tag('id', :title =&gt; 'Sort by contact ID') %&gt;
+ &lt;%= sort_header_tag('last_name', :caption =&gt; 'Name') %&gt;
+ &lt;%= sort_header_tag('phone') %&gt;
+ &lt;%= sort_header_tag('address', :width =&gt; 200) %&gt;
+ &lt;/tr&gt;
+ &lt;/thead&gt;
+</pre>
+<ul>
+<li>The ascending and descending sort icon images are sort_asc.png and
+sort_desc.png and reside in the application&#8216;s images directory.
+
+</li>
+<li>Introduces instance variables: @sort_name, @sort_default.
+
+</li>
+<li>Introduces params :sort_key and :sort_order.
+
+</li>
+</ul>
+
+ </div>
+
+
+ </div>
+
+ <div id="method-list">
+ <h3 class="section-bar">Methods</h3>
+
+ <div class="name-list">
+ <a href="#M000003">sort_clause</a>&nbsp;&nbsp;
+ <a href="#M000005">sort_header_tag</a>&nbsp;&nbsp;
+ <a href="#M000001">sort_init</a>&nbsp;&nbsp;
+ <a href="#M000004">sort_link</a>&nbsp;&nbsp;
+ <a href="#M000002">sort_update</a>&nbsp;&nbsp;
+ </div>
+ </div>
+
+ </div>
+
+
+ <!-- if includes -->
+
+ <div id="section">
+
+
+
+
+
+
+
+
+ <!-- if method_list -->
+ <div id="methods">
+ <h3 class="section-bar">Public Instance methods</h3>
+
+ <div id="method-M000003" class="method-detail">
+ <a name="M000003"></a>
+
+ <div class="method-heading">
+ <a href="#M000003" class="method-signature">
+ <span class="method-name">sort_clause</span><span class="method-args">()</span>
+ </a>
+ </div>
+
+ <div class="method-description">
+ <p>
+Returns an SQL sort clause corresponding to the current sort state. Use
+this to sort the controller&#8216;s table items collection.
+</p>
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('M000003-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="M000003-source">
+<pre>
+ <span class="ruby-comment cmt"># File lib/unobtrusive_sort_helper.rb, line 86</span>
+86: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">sort_clause</span>()
+87: <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>][<span class="ruby-identifier">:key</span>] <span class="ruby-operator">+</span> <span class="ruby-value str">' '</span> <span class="ruby-operator">+</span> <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>][<span class="ruby-identifier">:order</span>]
+88: <span class="ruby-keyword kw">end</span>
+</pre>
+ </div>
+ </div>
+ </div>
+
+ <div id="method-M000005" class="method-detail">
+ <a name="M000005"></a>
+
+ <div class="method-heading">
+ <a href="#M000005" class="method-signature">
+ <span class="method-name">sort_header_tag</span><span class="method-args">(column, options = {})</span>
+ </a>
+ </div>
+
+ <div class="method-description">
+ <p>
+Returns a table header &lt;th&gt; tag with a sort link for the named column
+attribute.
+</p>
+<p>
+Options:
+</p>
+<pre>
+ :caption The displayed link name (defaults to titleized column name).
+ :title The tag's 'title' attribute (defaults to 'Sort by :caption').
+</pre>
+<p>
+Other options hash entries generate additional table header tag attributes.
+</p>
+<p>
+Example:
+</p>
+<pre>
+ &lt;%= sort_header_tag('id', :title =&gt; 'Sort by contact ID', :width =&gt; 40) %&gt;
+</pre>
+<p>
+Renders:
+</p>
+<pre>
+ &lt;th title=&quot;Sort by contact ID&quot; width=&quot;40&quot;&gt;
+ &lt;a href=&quot;/contact/list?sort_order=desc&amp;amp;sort_key=id&quot;&gt;Id&lt;/a&gt;
+ &amp;nbsp;&amp;nbsp;&lt;img alt=&quot;Sort_asc&quot; src=&quot;/images/sort_asc.png&quot; /&gt;
+ &lt;/th&gt;
+</pre>
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('M000005-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="M000005-source">
+<pre>
+ <span class="ruby-comment cmt"># File lib/unobtrusive_sort_helper.rb, line 145</span>
+145: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">sort_header_tag</span>(<span class="ruby-identifier">column</span>, <span class="ruby-identifier">options</span> = {})
+146: <span class="ruby-identifier">options</span>[<span class="ruby-identifier">:initial_order</span>].<span class="ruby-identifier">nil?</span> <span class="ruby-value">? </span><span class="ruby-identifier">initial_order</span> = <span class="ruby-value str">&quot;asc&quot;</span> <span class="ruby-operator">:</span> <span class="ruby-identifier">initial_order</span> = <span class="ruby-identifier">options</span>[<span class="ruby-identifier">:initial_order</span>]
+147: <span class="ruby-identifier">caption</span> = <span class="ruby-identifier">options</span>.<span class="ruby-identifier">delete</span>(<span class="ruby-identifier">:caption</span>) <span class="ruby-operator">||</span> <span class="ruby-identifier">titleize</span>(<span class="ruby-constant">Inflector</span><span class="ruby-operator">::</span><span class="ruby-identifier">humanize</span>(<span class="ruby-identifier">column</span>))
+148: <span class="ruby-comment cmt">#options[:title] = caption unless options[:title]</span>
+149: <span class="ruby-identifier">content_tag</span>(<span class="ruby-value str">'th'</span>, <span class="ruby-identifier">sort_link</span>(<span class="ruby-identifier">column</span>, <span class="ruby-identifier">caption</span>, <span class="ruby-identifier">initial_order</span>), <span class="ruby-identifier">options</span>.<span class="ruby-identifier">except</span>(<span class="ruby-identifier">:initial_order</span>))
+150: <span class="ruby-keyword kw">end</span>
+</pre>
+ </div>
+ </div>
+ </div>
+
+ <div id="method-M000001" class="method-detail">
+ <a name="M000001"></a>
+
+ <div class="method-heading">
+ <a href="#M000001" class="method-signature">
+ <span class="method-name">sort_init</span><span class="method-args">(default_key, default_order='asc', name=nil)</span>
+ </a>
+ </div>
+
+ <div class="method-description">
+ <p>
+Initializes the default sort column (default_key) and sort order
+(default_order).
+</p>
+<ul>
+<li>default_key is a column attribute name.
+
+</li>
+<li>default_order is &#8216;asc&#8217; or &#8216;desc&#8217;.
+
+</li>
+<li>name is the name of the session hash entry that stores the sort state,
+defaults to &#8217;&lt;controller_name&gt;_sort&#8217;.
+
+</li>
+</ul>
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('M000001-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="M000001-source">
+<pre>
+ <span class="ruby-comment cmt"># File lib/unobtrusive_sort_helper.rb, line 64</span>
+64: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">sort_init</span>(<span class="ruby-identifier">default_key</span>, <span class="ruby-identifier">default_order</span>=<span class="ruby-value str">'asc'</span>, <span class="ruby-identifier">name</span>=<span class="ruby-keyword kw">nil</span>)
+65: <span class="ruby-ivar">@sort_name</span> = <span class="ruby-identifier">name</span> <span class="ruby-operator">||</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:controller</span>] <span class="ruby-operator">+</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:action</span>] <span class="ruby-operator">+</span> <span class="ruby-value str">'_sort'</span>
+66: <span class="ruby-ivar">@sort_default</span> = {<span class="ruby-identifier">:key</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">default_key</span>, <span class="ruby-identifier">:order</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">default_order</span>}
+67: <span class="ruby-keyword kw">end</span>
+</pre>
+ </div>
+ </div>
+ </div>
+
+ <div id="method-M000004" class="method-detail">
+ <a name="M000004"></a>
+
+ <div class="method-heading">
+ <a href="#M000004" class="method-signature">
+ <span class="method-name">sort_link</span><span class="method-args">(column, caption=nil, initial_order=&quot;asc&quot;)</span>
+ </a>
+ </div>
+
+ <div class="method-description">
+ <p>
+Returns a link which sorts by the named column.
+</p>
+<ul>
+<li>column is the name of an attribute in the sorted record collection.
+
+</li>
+<li>The optional caption explicitly specifies the displayed link text.
+
+</li>
+<li>A sort icon image is positioned to the right of the sort link.
+
+</li>
+</ul>
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('M000004-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="M000004-source">
+<pre>
+ <span class="ruby-comment cmt"># File lib/unobtrusive_sort_helper.rb, line 96</span>
+ 96: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">sort_link</span>(<span class="ruby-identifier">column</span>, <span class="ruby-identifier">caption</span>=<span class="ruby-keyword kw">nil</span>, <span class="ruby-identifier">initial_order</span>=<span class="ruby-value str">&quot;asc&quot;</span>)
+ 97:
+ 98: <span class="ruby-identifier">key</span>, <span class="ruby-identifier">order</span> = <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>][<span class="ruby-identifier">:key</span>], <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>][<span class="ruby-identifier">:order</span>]
+ 99: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">key</span> <span class="ruby-operator">==</span> <span class="ruby-identifier">column</span>
+100: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">order</span>.<span class="ruby-identifier">downcase</span> <span class="ruby-operator">==</span> <span class="ruby-value str">'asc'</span>
+101: <span class="ruby-identifier">icon</span> = <span class="ruby-value str">'sort_asc.png'</span>
+102: <span class="ruby-identifier">order</span> = <span class="ruby-value str">'desc'</span>
+103: <span class="ruby-keyword kw">else</span>
+104: <span class="ruby-identifier">icon</span> = <span class="ruby-value str">'sort_desc.png'</span>
+105: <span class="ruby-identifier">order</span> = <span class="ruby-value str">'asc'</span>
+106: <span class="ruby-keyword kw">end</span>
+107: <span class="ruby-keyword kw">else</span>
+108: <span class="ruby-identifier">icon</span> = <span class="ruby-keyword kw">nil</span>
+109: <span class="ruby-identifier">order</span> = <span class="ruby-identifier">initial_order</span> <span class="ruby-comment cmt"># JPM - Updated original helper to allow for a dynamic initial sort order</span>
+110: <span class="ruby-keyword kw">end</span>
+111: <span class="ruby-identifier">caption</span> = <span class="ruby-identifier">titleize</span>(<span class="ruby-constant">Inflector</span><span class="ruby-operator">::</span><span class="ruby-identifier">humanize</span>(<span class="ruby-identifier">column</span>)) <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">caption</span>
+112:
+113: <span class="ruby-identifier">url</span> = { <span class="ruby-identifier">:sort_key</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">column</span>, <span class="ruby-identifier">:sort_order</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">order</span>, <span class="ruby-identifier">:filter</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:filter</span>]}
+114: <span class="ruby-identifier">url</span>.<span class="ruby-identifier">merge!</span>({<span class="ruby-identifier">:q</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:q</span>]}) <span class="ruby-keyword kw">unless</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:q</span>].<span class="ruby-identifier">nil?</span>
+115:
+116: <span class="ruby-comment cmt"># link_to_remote(caption,</span>
+117: <span class="ruby-comment cmt"># {:update =&gt; &quot;content&quot;, :url =&gt; url, :method =&gt; :get},</span>
+118: <span class="ruby-comment cmt"># {:href =&gt; url_for(url), :class =&gt; &quot;sort_link&quot;}) +</span>
+119: <span class="ruby-comment cmt"># (icon ? nbsp(2) + image_tag(icon) : '')</span>
+120:
+121: <span class="ruby-comment cmt"># new ujs version:</span>
+122: <span class="ruby-identifier">link_to</span>(<span class="ruby-identifier">caption</span>, <span class="ruby-identifier">url</span>, <span class="ruby-identifier">:class</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-node">&quot;sort_link #{order if key == column}&quot;</span>)
+123: <span class="ruby-keyword kw">end</span>
+</pre>
+ </div>
+ </div>
+ </div>
+
+ <div id="method-M000002" class="method-detail">
+ <a name="M000002"></a>
+
+ <div class="method-heading">
+ <a href="#M000002" class="method-signature">
+ <span class="method-name">sort_update</span><span class="method-args">()</span>
+ </a>
+ </div>
+
+ <div class="method-description">
+ <p>
+Updates the sort state. Call this in the controller prior to calling <a
+href="UnobtrusiveSortHelper.html#M000003">sort_clause</a>.
+</p>
+ <p><a class="source-toggle" href="#"
+ onclick="toggleCode('M000002-source');return false;">[Source]</a></p>
+ <div class="method-source-code" id="M000002-source">
+<pre>
+ <span class="ruby-comment cmt"># File lib/unobtrusive_sort_helper.rb, line 72</span>
+72: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">sort_update</span>()
+73: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:sort_key</span>]
+74: <span class="ruby-identifier">sort</span> = {<span class="ruby-identifier">:key</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:sort_key</span>], <span class="ruby-identifier">:order</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">params</span>[<span class="ruby-identifier">:sort_order</span>]}
+75: <span class="ruby-keyword kw">elsif</span> <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>]
+76: <span class="ruby-identifier">sort</span> = <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>] <span class="ruby-comment cmt"># Previous sort.</span>
+77: <span class="ruby-keyword kw">else</span>
+78: <span class="ruby-identifier">sort</span> = <span class="ruby-ivar">@sort_default</span>
+79: <span class="ruby-keyword kw">end</span>
+80: <span class="ruby-identifier">session</span>[<span class="ruby-ivar">@sort_name</span>] = <span class="ruby-identifier">sort</span>
+81: <span class="ruby-keyword kw">end</span>
+</pre>
+ </div>
+ </div>
+ </div>
+
+
+ </div>
+
+
+ </div>
+
+
+<div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+</div>
+
+</body>
+</html>
1  rdoc/created.rid
@@ -0,0 +1 @@
+Wed, 27 Feb 2008 13:55:04 -0600
197 rdoc/files/README.html
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>File: README</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
+ <link rel="stylesheet" href=".././rdoc-style.css" type="text/css" media="screen" />
+ <script type="text/javascript">
+ // <![CDATA[
+
+ function popupCode( url ) {
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
+ }
+
+ function toggleCode( id ) {
+ if ( document.getElementById )
+ elem = document.getElementById( id );
+ else if ( document.all )
+ elem = eval( "document.all." + id );
+ else
+ return false;
+
+ elemStyle = elem.style;
+
+ if ( elemStyle.display != "block" ) {
+ elemStyle.display = "block"
+ } else {
+ elemStyle.display = "none"
+ }
+
+ return true;
+ }
+
+ // Make codeblocks hidden by default
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
+
+ // ]]>
+ </script>
+
+</head>
+<body>
+
+
+
+ <div id="fileHeader">
+ <h1>README</h1>
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong>Path:</strong></td>
+ <td>README
+ </td>
+ </tr>
+ <tr class="top-aligned-row">
+ <td><strong>Last Update:</strong></td>
+ <td>Wed Feb 27 13:55:00 -0600 2008</td>
+ </tr>
+ </table>
+ </div>
+ <!-- banner header -->
+
+ <div id="bodyContent">
+
+
+
+ <div id="contextContent">
+
+ <div id="description">
+ <h1><a href="../classes/UnobtrusiveSortHelper.html">UnobtrusiveSortHelper</a></h1>
+<p>
+This is a plugin version of Stuart Rackham&#8216;s most excellent
+SortHelper helper. The link_to_remote ajax calls have been removed in favor
+of unobtrusive javascript using lowpro.js.
+</p>
+<h3>Features</h3>
+<ul>
+<li>Consecutive clicks toggle the column&#8216;s sort order.
+
+</li>
+<li>Sort state is maintained by a session hash entry.
+
+</li>
+<li>Icon image identifies sort column and state.
+
+</li>
+<li>Typically used in conjunction with the Pagination module.
+
+</li>
+</ul>
+<h3>Usage</h3>
+<h4>Controller:</h4>
+<pre>
+ def list
+ sort_init 'last_name'
+ sort_update
+ @items = Contact.find_all nil, sort_clause
+ end
+</pre>
+<h4>Controller (using will_paginate)</h4>
+<pre>
+ def list
+ sort_init 'last_name'
+ sort_update
+
+ options = {:page =&gt; params[:page], :include =&gt; :addresses, :order =&gt; sort_clause
+
+ @contacts = Contact.paginate(options)
+ end
+</pre>
+<h4>Layout (app/views/layouts/application.html.erb):</h4>
+<pre>
+ &lt;%= stylesheet_include_tag &quot;ujs_sort_helper&quot;%&gt;
+</pre>
+<p>
+styles for nifty sort arrow images
+</p>
+<pre>
+ &lt;%= javascript_include_tag :defaults%&gt;
+</pre>
+<p>
+as long as prototype.js is included before lowpro and ujs_sort_helper
+</p>
+<pre>
+ &lt;%= javascript_include_tag &quot;lowpro&quot;%&gt;
+</pre>
+<p>
+you SHOULD already have this ;-)
+</p>
+<pre>
+ &lt;%= javascript_include_tag &quot;usj_sort_helper&quot;%&gt;
+</pre>
+<p>
+this is where ujs comes in
+</p>
+<h4>View (table header in index.rhtml):</h4>
+<pre>
+ &lt;thead&gt;
+ &lt;tr&gt;
+ &lt;%= sort_header_tag('id', :title =&gt; 'Sort by contact ID') %&gt;
+ &lt;%= sort_header_tag('last_name', :caption =&gt; 'Name') %&gt;
+ &lt;%= sort_header_tag('phone') %&gt;
+ &lt;%= sort_header_tag('address', :width =&gt; 200) %&gt;
+ &lt;/tr&gt;
+ &lt;/thead&gt;
+</pre>
+<ul>
+<li>The ascending and descending sort icon images are sort_asc.png and
+sort_desc.png and reside in the application&#8216;s images directory.
+
+</li>
+<li>Introduces instance variables: @sort_name, @sort_default.
+
+</li>
+<li>Introduces params :sort_key and :sort_order.
+
+</li>
+</ul>
+<p>
+Copyright (c) 2005 Stuart Rackham, (c) 2008 Wynn Netherland released under
+the MIT license
+</p>
+
+ </div>
+
+
+ </div>
+
+
+ </div>
+
+
+ <!-- if includes -->
+
+ <div id="section">
+
+
+
+
+
+
+
+
+ <!-- if method_list -->
+
+
+ </div>
+
+
+<div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+</div>
+
+</body>
+</html>
182 rdoc/files/lib/unobtrusive_sort_helper_rb.html
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>File: unobtrusive_sort_helper.rb</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <meta http-equiv="Content-Script-Type" content="text/javascript" />
+ <link rel="stylesheet" href="../.././rdoc-style.css" type="text/css" media="screen" />
+ <script type="text/javascript">
+ // <![CDATA[
+
+ function popupCode( url ) {
+ window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400")
+ }
+
+ function toggleCode( id ) {
+ if ( document.getElementById )
+ elem = document.getElementById( id );
+ else if ( document.all )
+ elem = eval( "document.all." + id );
+ else
+ return false;
+
+ elemStyle = elem.style;
+
+ if ( elemStyle.display != "block" ) {
+ elemStyle.display = "block"
+ } else {
+ elemStyle.display = "none"
+ }
+
+ return true;
+ }
+
+ // Make codeblocks hidden by default
+ document.writeln( "<style type=\"text/css\">div.method-source-code { display: none }</style>" )
+
+ // ]]>
+ </script>
+
+</head>
+<body>
+
+
+
+ <div id="fileHeader">
+ <h1>unobtrusive_sort_helper.rb</h1>
+ <table class="header-table">
+ <tr class="top-aligned-row">
+ <td><strong>Path:</strong></td>
+ <td>lib/unobtrusive_sort_helper.rb
+ </td>
+ </tr>
+ <tr class="top-aligned-row">
+ <td><strong>Last Update:</strong></td>
+ <td>Wed Feb 27 13:24:15 -0600 2008</td>
+ </tr>
+ </table>
+ </div>
+ <!-- banner header -->
+
+ <div id="bodyContent">
+
+
+
+ <div id="contextContent">
+
+ <div id="description">
+ <p>
+Helpers to sort tables using clickable column headers.
+</p>
+<p>
+Author: Stuart Rackham &lt;srackham@methods.co.nz&gt;, March 2005. Modified
+by: Wynn Netherland &lt;wynn@squeejee.com&gt;, February 2008 to use UJS
+License: This source code is released under the MIT license.
+</p>
+<ul>
+<li>Consecutive clicks toggle the column&#8216;s sort order.
+
+</li>
+<li>Sort state is maintained by a session hash entry.
+
+</li>
+<li>Icon image identifies sort column and state.
+
+</li>
+<li>Typically used in conjunction with the Pagination module.
+
+</li>
+</ul>
+<p>
+Example code snippets:
+</p>
+<p>
+Controller:
+</p>
+<pre>
+ helper :sort
+ include SortHelper
+
+ def list
+ sort_init 'last_name'
+ sort_update
+ @items = Contact.find_all nil, sort_clause
+ end
+</pre>
+<p>
+Controller (using Pagination module):
+</p>
+<pre>
+ helper :sort
+ include SortHelper
+
+ def list
+ sort_init 'last_name'
+ sort_update
+ @contact_pages, @items = paginate :contacts,
+ :order_by =&gt; sort_clause,
+ :per_page =&gt; 10
+ end
+</pre>
+<p>
+View (table header in list.rhtml):
+</p>
+<pre>
+ &lt;thead&gt;
+ &lt;tr&gt;
+ &lt;%= sort_header_tag('id', :title =&gt; 'Sort by contact ID') %&gt;
+ &lt;%= sort_header_tag('last_name', :caption =&gt; 'Name') %&gt;
+ &lt;%= sort_header_tag('phone') %&gt;
+ &lt;%= sort_header_tag('address', :width =&gt; 200) %&gt;
+ &lt;/tr&gt;
+ &lt;/thead&gt;
+</pre>
+<ul>
+<li>The ascending and descending sort icon images are sort_asc.png and
+sort_desc.png and reside in the application&#8216;s images directory.
+
+</li>
+<li>Introduces instance variables: @sort_name, @sort_default.
+
+</li>
+<li>Introduces params :sort_key and :sort_order.
+
+</li>
+</ul>
+
+ </div>
+
+
+ </div>
+
+
+ </div>
+
+
+ <!-- if includes -->
+
+ <div id="section">
+
+
+
+
+
+
+
+
+ <!-- if method_list -->
+
+
+ </div>
+
+
+<div id="validator-badges">
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
+</div>
+
+</body>
+</html>
27 rdoc/fr_class_index.html
@@ -0,0 +1,27 @@
+
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<!--
+
+ Classes
+
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Classes</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
+ <base target="docwin" />
+</head>
+<body>
+<div id="index">
+ <h1 class="section-bar">Classes</h1>
+ <div id="index-entries">
+ <a href="classes/UnobtrusiveSortHelper.html">UnobtrusiveSortHelper</a><br />
+ </div>
+</div>
+</body>
+</html>
28 rdoc/fr_file_index.html
@@ -0,0 +1,28 @@
+
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<!--
+
+ Files
+
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Files</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
+ <base target="docwin" />
+</head>
+<body>
+<div id="index">
+ <h1 class="section-bar">Files</h1>
+ <div id="index-entries">
+ <a href="files/README.html">README</a><br />
+ <a href="files/lib/unobtrusive_sort_helper_rb.html">lib/unobtrusive_sort_helper.rb</a><br />
+ </div>
+</div>
+</body>
+</html>
31 rdoc/fr_method_index.html
@@ -0,0 +1,31 @@
+
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<!--
+
+ Methods
+
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>Methods</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
+ <base target="docwin" />
+</head>
+<body>
+<div id="index">
+ <h1 class="section-bar">Methods</h1>
+ <div id="index-entries">
+ <a href="classes/UnobtrusiveSortHelper.html#M000003">sort_clause (UnobtrusiveSortHelper)</a><br />
+ <a href="classes/UnobtrusiveSortHelper.html#M000005">sort_header_tag (UnobtrusiveSortHelper)</a><br />
+ <a href="classes/UnobtrusiveSortHelper.html#M000001">sort_init (UnobtrusiveSortHelper)</a><br />
+ <a href="classes/UnobtrusiveSortHelper.html#M000004">sort_link (UnobtrusiveSortHelper)</a><br />
+ <a href="classes/UnobtrusiveSortHelper.html#M000002">sort_update (UnobtrusiveSortHelper)</a><br />
+ </div>
+</div>
+</body>
+</html>
24 rdoc/index.html
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
+
+<!--
+
+ UnobtrusiveSortHelper
+
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>UnobtrusiveSortHelper</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+</head>
+<frameset rows="20%, 80%">
+ <frameset cols="25%,35%,45%">
+ <frame src="fr_file_index.html" title="Files" name="Files" />
+ <frame src="fr_class_index.html" name="Classes" />
+ <frame src="fr_method_index.html" name="Methods" />
+ </frameset>
+ <frame src="files/README.html" name="docwin" />
+</frameset>
+</html>
208 rdoc/rdoc-style.css
@@ -0,0 +1,208 @@
+
+body {
+ font-family: Verdana,Arial,Helvetica,sans-serif;
+ font-size: 90%;
+ margin: 0;
+ margin-left: 40px;
+ padding: 0;
+ background: white;
+}
+
+h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
+h1 { font-size: 150%; }
+h2,h3,h4 { margin-top: 1em; }
+
+a { background: #eef; color: #039; text-decoration: none; }
+a:hover { background: #039; color: #eef; }
+
+/* Override the base stylesheet's Anchor inside a table cell */
+td > a {
+ background: transparent;
+ color: #039;
+ text-decoration: none;
+}
+
+/* and inside a section title */
+.section-title > a {
+ background: transparent;
+ color: #eee;
+ text-decoration: none;
+}
+
+/* === Structural elements =================================== */
+
+div#index {
+ margin: 0;
+ margin-left: -40px;
+ padding: 0;
+ font-size: 90%;
+}
+
+
+div#index a {
+ margin-left: 0.7em;
+}
+
+div#index .section-bar {
+ margin-left: 0px;
+ padding-left: 0.7em;
+ background: #ccc;
+ font-size: small;
+}
+
+
+div#classHeader, div#fileHeader {
+ width: auto;
+ color: white;
+ padding: 0.5em 1.5em 0.5em 1.5em;
+ margin: 0;
+ margin-left: -40px;
+ border-bottom: 3px solid #006;
+}
+
+div#classHeader a, div#fileHeader a {
+ background: inherit;
+ color: white;
+}
+
+div#classHeader td, div#fileHeader td {
+ background: inherit;
+ color: white;
+}
+
+
+div#fileHeader {
+ background: #057;
+}
+
+div#classHeader {
+ background: #048;
+}
+
+
+.class-name-in-header {
+ font-size: 180%;
+ font-weight: bold;
+}
+
+
+div#bodyContent {
+ padding: 0 1.5em 0 1.5em;
+}
+
+div#description {
+ padding: 0.5em 1.5em;
+ background: #efefef;
+ border: 1px dotted #999;
+}
+
+div#description h1,h2,h3,h4,h5,h6 {
+ color: #125;;
+ background: transparent;
+}
+
+div#validator-badges {
+ text-align: center;
+}
+div#validator-badges img { border: 0; }
+
+div#copyright {
+ color: #333;
+ background: #efefef;
+ font: 0.75em sans-serif;
+ margin-top: 5em;
+ margin-bottom: 0;
+ padding: 0.5em 2em;
+}
+
+
+/* === Classes =================================== */
+
+table.header-table {
+ color: white;
+ font-size: small;
+}
+
+.type-note {
+ font-size: small;
+ color: #DEDEDE;
+}
+
+.xxsection-bar {
+ background: #eee;
+ color: #333;
+ padding: 3px;
+}
+
+.section-bar {
+ color: #333;
+ border-bottom: 1px solid #999;
+ margin-left: -20px;
+}
+
+
+.section-title {
+ background: #79a;
+ color: #eee;
+ padding: 3px;
+ margin-top: 2em;
+ margin-left: -30px;
+ border: 1px solid #999;
+}
+
+.top-aligned-row { vertical-align: top }
+.bottom-aligned-row { vertical-align: bottom }
+
+/* --- Context section classes ----------------------- */
+
+.context-row { }
+.context-item-name { font-family: monospace; font-weight: bold; color: black; }
+.context-item-value { font-size: small; color: #448; }
+.context-item-desc { color: #333; padding-left: 2em; }
+
+/* --- Method classes -------------------------- */
+.method-detail {
+ background: #efefef;
+ padding: 0;
+ margin-top: 0.5em;
+ margin-bottom: 1em;
+ border: 1px dotted #ccc;
+}
+.method-heading {
+ color: black;
+ background: #ccc;
+ border-bottom: 1px solid #666;
+ padding: 0.2em 0.5em 0 0.5em;
+}
+.method-signature { color: black; background: inherit; }
+.method-name { font-weight: bold; }
+.method-args { font-style: italic; }
+.method-description { padding: 0 0.5em 0 0.5em; }
+
+/* --- Source code sections -------------------- */
+
+a.source-toggle { font-size: 90%; }
+div.method-source-code {
+ background: #262626;
+ color: #ffdead;
+ margin: 1em;
+ padding: 0.5em;
+ border: 1px dashed #999;
+ overflow: hidden;
+}
+
+div.method-source-code pre { color: #ffdead; overflow: hidden; }
+
+/* --- Ruby keyword styles --------------------- */
+
+.standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
+
+.ruby-constant { color: #7fffd4; background: transparent; }
+.ruby-keyword { color: #00ffff; background: transparent; }
+.ruby-ivar { color: #eedd82; background: transparent; }
+.ruby-operator { color: #00ffee; background: transparent; }
+.ruby-identifier { color: #ffdead; background: transparent; }
+.ruby-node { color: #ffa07a; background: transparent; }
+.ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
+.ruby-regexp { color: #ffa07a; background: transparent; }
+.ruby-value { color: #7fffd4; background: transparent; }
4 tasks/unobtrusive_sort_helper_tasks.rake
@@ -0,0 +1,4 @@
+# desc "Explaining what the task does"
+# task :unobtrusive_sort_helper do
+# # Task goes here
+# end
8 test/unobtrusive_sort_helper_test.rb
@@ -0,0 +1,8 @@
+require 'test/unit'
+
+class UnobtrusiveSortHelperTest < Test::Unit::TestCase
+ # Replace this with your real tests.
+ def test_this_plugin
+ true
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.