logical_tabs 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'http://rubygems.org'
2
+
3
+
4
+ group :test do
5
+ gem 'rails', '~> 3.0.4'
6
+ gem 'sqlite3-ruby', :require => 'sqlite3'
7
+ gem "rspec", ">= 2.5.0"
8
+ gem "rspec-rails", ">= 2.5.0"
9
+ gem 'ruby-debug'
10
+ gem 'webrat'
11
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,105 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.4)
6
+ actionpack (= 3.0.4)
7
+ mail (~> 2.2.15)
8
+ actionpack (3.0.4)
9
+ activemodel (= 3.0.4)
10
+ activesupport (= 3.0.4)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.13)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.4)
19
+ activesupport (= 3.0.4)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4)
22
+ activerecord (3.0.4)
23
+ activemodel (= 3.0.4)
24
+ activesupport (= 3.0.4)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.4)
28
+ activemodel (= 3.0.4)
29
+ activesupport (= 3.0.4)
30
+ activesupport (3.0.4)
31
+ arel (2.0.8)
32
+ builder (2.1.2)
33
+ columnize (0.3.2)
34
+ diff-lcs (1.1.2)
35
+ erubis (2.6.6)
36
+ abstract (>= 1.0.0)
37
+ i18n (0.5.0)
38
+ linecache (0.43)
39
+ mail (2.2.15)
40
+ activesupport (>= 2.3.6)
41
+ i18n (>= 0.4.0)
42
+ mime-types (~> 1.16)
43
+ treetop (~> 1.4.8)
44
+ mime-types (1.16)
45
+ nokogiri (1.4.4)
46
+ polyglot (0.3.1)
47
+ rack (1.2.1)
48
+ rack-mount (0.6.13)
49
+ rack (>= 1.0.0)
50
+ rack-test (0.5.7)
51
+ rack (>= 1.0)
52
+ rails (3.0.4)
53
+ actionmailer (= 3.0.4)
54
+ actionpack (= 3.0.4)
55
+ activerecord (= 3.0.4)
56
+ activeresource (= 3.0.4)
57
+ activesupport (= 3.0.4)
58
+ bundler (~> 1.0)
59
+ railties (= 3.0.4)
60
+ railties (3.0.4)
61
+ actionpack (= 3.0.4)
62
+ activesupport (= 3.0.4)
63
+ rake (>= 0.8.7)
64
+ thor (~> 0.14.4)
65
+ rake (0.8.7)
66
+ rspec (2.5.0)
67
+ rspec-core (~> 2.5.0)
68
+ rspec-expectations (~> 2.5.0)
69
+ rspec-mocks (~> 2.5.0)
70
+ rspec-core (2.5.1)
71
+ rspec-expectations (2.5.0)
72
+ diff-lcs (~> 1.1.2)
73
+ rspec-mocks (2.5.0)
74
+ rspec-rails (2.5.0)
75
+ actionpack (~> 3.0)
76
+ activesupport (~> 3.0)
77
+ railties (~> 3.0)
78
+ rspec (~> 2.5.0)
79
+ ruby-debug (0.10.4)
80
+ columnize (>= 0.1)
81
+ ruby-debug-base (~> 0.10.4.0)
82
+ ruby-debug-base (0.10.4)
83
+ linecache (>= 0.3)
84
+ sqlite3 (1.3.3)
85
+ sqlite3-ruby (1.3.3)
86
+ sqlite3 (>= 1.3.3)
87
+ thor (0.14.6)
88
+ treetop (1.4.9)
89
+ polyglot (>= 0.3.1)
90
+ tzinfo (0.3.24)
91
+ webrat (0.7.3)
92
+ nokogiri (>= 1.2.0)
93
+ rack (>= 1.0)
94
+ rack-test (>= 0.5.3)
95
+
96
+ PLATFORMS
97
+ ruby
98
+
99
+ DEPENDENCIES
100
+ rails (~> 3.0.4)
101
+ rspec (>= 2.5.0)
102
+ rspec-rails (>= 2.5.0)
103
+ ruby-debug
104
+ sqlite3-ruby
105
+ webrat
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010-2011 Evan Dorn and Logical Reality Design
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,115 @@
1
+ == LogicalTabs
2
+
3
+ LogicalTabs is a Rails gem for generating a tabbed panel interface. It has a couple of advantages over existing solutions (like the tabs item in JQuery UI, or the old Rails plugin railstabbedpanel):
4
+ * Tabs are persistent across page loads. Reload a page and the tab you had selected is still selected.
5
+ * Rails helper to write the HTML for you and keep the IDs of all the elements involved straight. Less HTML for you to write and manage.
6
+ * Output designed to print well (with stacked panels) and display comprehensibly if CSS and JS are not available.
7
+ * If you create multiple tabbed panels on one page, the helper will automatically increment a suffix on the element's ID to keep them distinct.
8
+ * Unobtrusive javascript on simple markup.
9
+ * Compatible with both Prototype and jQuery.
10
+ * HTML helpers compatible with both ERB and HAML, resistant to future changes in ActionView::Base and HAML.
11
+
12
+ Note: jQuery compatibility is a bit of a weekend hack. In particular, it was written primarily for Prototype, and the jQuery version of the javasscript is a quick hack of the prototype code to make it work in jQuery. It does work, but is not a proper jQuery plugin and doesn't talk to jQueryUI at all. I do plan to clean this up for future releases, but am happy to accept contributions from anyone else who is interested.
13
+
14
+ In an upcoming version we will release it with a third Javascript that uses NinjaScript syntax. For
15
+ more information on NinjaScript, see https://github.com/LRDesign/NinjaScript.
16
+
17
+ == Compatibility
18
+
19
+ logical_tabs >= 0.8.0 is built for Rails 3.0.x. Versions below 0.8 were a plugin (not a gem) for Rails 2.x. If you want to use LogicalTabs with Rails 2.x, see the "rails2_plugin" branch of this project on github:
20
+ https://github.com/LRDesign/logical_tabs/tree/rails2_plugin
21
+
22
+ == Installing
23
+
24
+ Add to your Gemfile:
25
+ gem 'logical_tabs'
26
+
27
+ Install the basic stylesheets and javascripts:
28
+ rails generate logical_tabs install
29
+
30
+ The above command installs CSS stylesheet into public/stylesheets and a prototype version of the
31
+ javascript into public/javascripts. Pass '--css=sass' to instead copy a SASS version to public/stylesheets/sass. Pass '--js=jquery' to copy the jquery javascript instead of the prototype.
32
+ Pass --css
33
+
34
+ Include the css in your application layout:
35
+ <%= stylesheet_include_tag 'logical_tabs', :media => :all %>
36
+
37
+ Note that the :media => :all is important, because the LT stylesheet contains directives for print
38
+ and handhelds as well as screen. The default LT stylesheets will eliminate the tabs and display
39
+ all of the panels in order with headers so that the full content is visible during print.
40
+
41
+ Include the js in your application layout:
42
+ For Prototype: <%= javascript_include_tag 'logical_tabs_prototype.js' %>
43
+ For jQuery: <%= javascript_include_tag 'logical_tabs_jquery.js' %>
44
+
45
+ Note that both of the above javascripts include dependencies; logical_tabs_prototype.js includes a copy of Lalit Patel's CookieJar (http://www.lalit.org/lab/jsoncookies ) and logical_tabs_jquery.js includes a copy of Jon Combe's Jookie. If you already have one of those libraries installed in your application, you'll want to strip the duplicate out of this plugin's js file.
46
+
47
+ == Example Usage
48
+
49
+ Create new tabbed panels by calling the 'tabbed_panel' helper and feeding blocks of content to the 'add_tab' method:
50
+
51
+ <%= tabbed_panel do |tp| %>
52
+ <% tp.add_tab("Tab One") do %>
53
+ Content for Tab One
54
+ <% end %>
55
+ <% tp.add_tab("Tab Two") do %>
56
+ Content for Tab Two
57
+ <% end %>
58
+ <% tp.add_tab("Tab Three") do %>
59
+ <p>Content for Tab Three</p>
60
+ <% end %>
61
+ <% end %>
62
+
63
+ You can override the ID or text used for a tab:
64
+ <% tp.add_tab("Tab Four", :base_id => 'my_tab', :text => "Tab Text") do %>
65
+ Content for Tab four.
66
+ <% end %>
67
+
68
+ You can also specify pane content rather than pass a block:
69
+ <% tp.add_tab("Tab Four", :content => "Content for tab four") %>
70
+
71
+ In each case, the helper will generate a DIV that contains a UL of the tabs (each tab in an LI) and a set of DIVs containing each of the content panes.
72
+
73
+ See more examples (including both ERB and HAML formatting) in the logical_tabs_demo example project: http://github.com/LRDesign/logical_tabs_demo
74
+
75
+ == Print compatibility
76
+
77
+ LogicalTabs includes an <h3> in each pane with the same text as that pane's <li> tab. This header has class '.printonly', and the default stylesheet eliminates it for screen and projection media. The default stylesheet also removes the <ul class='tabs'> from the page for print media.
78
+
79
+ The effect is that, when printed, the content of each pane appears, stacked in source order, with an associated header explaining the content. This makes for a much happier experience for the printout user.
80
+
81
+ This can also be leveraged with an @media = 'handheld' stylesheet to give stacked panels on mobile devices that don't support the full screen simulation common on modern smartphones.
82
+
83
+ == License
84
+
85
+ Convection is licensed under the MIT License. Please see the LICENSE file
86
+ for more details.
87
+
88
+ == Credits
89
+ Copyright Evan Dorn and Logical Reality Design, 2010-2011
90
+
91
+ Design and Development:
92
+ Logical Reality Design, Altadena, California
93
+ Evan Dorn, Lead Developer
94
+
95
+ == Future Plans
96
+
97
+ NinjaScript compatibility coming soon.
98
+
99
+ == Version History
100
+
101
+ 0.8.0 (March 1, 2011)
102
+ * Released Rails 3-compatible version as a gem.
103
+ * Added improved formatting for printout
104
+
105
+ 0.6.2 (June 8, 2010)
106
+ * Removed some unnecessary console debugging calls from the jQuery script and updated this README.
107
+
108
+ 0.6.1 (May 11, 2010)
109
+ * Fixed a serious bug with the jQuery script.
110
+
111
+ 0.6 (April 29, 2010)
112
+ * Added jQuery-compatible javascript.
113
+
114
+ 0.5 (April 22, 2010)
115
+ * Initial release, contains only Prototype-compatible javascript.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+
3
+ desc 'Default: Run all specs.'
4
+ task :default => :all_specs
5
+
6
+ desc "Run all specs"
7
+ task :all_specs do
8
+ Dir['spec/*/Rakefile'].each do |rakefile|
9
+ directory_name = File.dirname(rakefile)
10
+ puts "=========== Running specs in #{directory_name}"
11
+ sh <<-CMD
12
+ cd #{directory_name}
13
+ bundle exec rake
14
+ CMD
15
+ end
16
+ end
17
+
18
+ namespace :update do
19
+ desc "regenerate the CSS from SASS source"
20
+ task :css do
21
+ sh 'sass lib/generators/logical_tabs/install/templates/stylesheets/sass/logical_tabs.sass lib/generators/logical_tabs/install/templates/stylesheets/logical_tabs.css'
22
+ end
23
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.0
data/doc/README ADDED
@@ -0,0 +1,2 @@
1
+ == This is a Really Cool Ruby Thing
2
+ === ( That deserves better documentation than this. )
@@ -0,0 +1,75 @@
1
+ module LogicalTabs
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :css,
6
+ :type => :string,
7
+ :desc => "Type of CSS to install, (css, sass, or none), default css",
8
+ :default => "css"
9
+
10
+ class_option :js,
11
+ :type => :string,
12
+ :desc => "Type of JS to install, (prototype, jquery, or none), default prototype",
13
+ :default => 'prototype'
14
+
15
+
16
+ desc <<DESC
17
+ Description:
18
+ Copy LogicalTabs files to your application.
19
+ DESC
20
+
21
+ def self.source_root
22
+ @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
23
+ end
24
+
25
+ def check_options
26
+ quit_with_options_error unless %w(css sass none).include?(options[:css])
27
+ quit_with_options_error unless %w(prototype jquery none).include?(options[:js])
28
+ end
29
+
30
+ def install_javascript
31
+ case options[:js]
32
+ when 'prototype': copy_file 'javascripts/logical_tabs_prototype.js', 'public/javascripts/logical_tabs_prototype.js'
33
+ when 'jquery': copy_file 'javascripts/logical_tabs_prototype.js', 'public/javascripts/logical_tabs_prototype.js'
34
+ end
35
+ end
36
+
37
+ def install_stylesheet
38
+ case options[:css]
39
+ when 'css': copy_file 'stylesheets/logical_tabs.css', 'public/stylesheets/logical_tabs.css'
40
+ when 'sass': copy_file 'stylesheets/sass/logical_tabs.sass', 'public/stylesheets/sass/logical_tabs.sass'
41
+ end
42
+ end
43
+
44
+ def add_javascript
45
+ file = File.join("public", "javascripts", "application.js")
46
+ end
47
+
48
+ def reminder
49
+ say <<NOTICE
50
+
51
+ LogicalTabs installed! Remember to add the stylesheet and javascript link tags.
52
+
53
+ <%= stylesheet_link_tag 'logical_tabs.css' %>
54
+ <%= javascript_include_tag 'logical_tabs_prototype.js' %>
55
+ -- OR --
56
+ <%= javascript_include_tag 'logical_tabs_jquery.js' %>
57
+ NOTICE
58
+ end
59
+
60
+
61
+ private
62
+ def quit_with_options_error
63
+ say <<END_OF_ERROR
64
+
65
+ Invalid option specified. If options are specified:
66
+ --css must be one of [ css, sass, none ]
67
+ --js must be one of [ prototype, jquery, none ]
68
+
69
+ END_OF_ERROR
70
+ exit(1)
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,688 @@
1
+ /**
2
+ * Javascript code to support switching, memorizing, and preselecting
3
+ * tabs in a tabbed_panel structure for the plugin logical_tabs.
4
+ *
5
+ * NOTE: Includes a full copy of Jookie by Jon Combe, see below.
6
+ *
7
+ * Author : Evan Dorn
8
+ * Website: http://lrdesign.com/tools
9
+ * License: MIT License, see included LICENSE file.
10
+ * Version: 1.0
11
+ * Updated: April 29, 2010
12
+ *
13
+ */
14
+
15
+ // Set up the click observer on the tabs, and
16
+ $(document).ready( function(){
17
+ $('.tabbed_panel .tab a').click(function(event){ event.preventDefault(); handle_tab_click($(this))});
18
+ $.Jookie.Initialise("tab_memory", 60*24*365);
19
+ $('.tabbed_panel').each(function(){ pre_select_tab($(this))});
20
+ });
21
+
22
+ // Given a tabbed_panel div element, checks to see if there's a pre-stored
23
+ // tab association. If there is, select that tab.
24
+ function pre_select_tab(tabbed_panel) {
25
+ tab_memory = get_tab_memory();
26
+ tab_id = tab_memory[memory_key_from_tabbed_panel(tabbed_panel.attr('id'))]
27
+ if(tab_id != null) {
28
+ select_tab($("#"+tab_id));
29
+ }
30
+ }
31
+
32
+ // Select a tab that's been clicked on and store that preference in a cookie.
33
+ function handle_tab_click(element) {
34
+ tab = element.parents("li.tab");
35
+ select_tab(tab);
36
+ store_tab_preference(tab.attr('id'));
37
+ }
38
+
39
+
40
+
41
+ // Handle the display changes necessary for selecting a new tab.
42
+ // switches on the clicked tab, finds the matching pane, and switches it
43
+ // on. Then iterates the siblings of both and switches them all off
44
+ function select_tab(tab) {
45
+ pane_id = tab.attr('id').replace(/_tab$/,"_pane");
46
+ pane = $("#"+pane_id);
47
+ tab.removeClass('tab_unselected').addClass('tab_selected');
48
+ tab.siblings().removeClass('tab_selected').addClass('tab_unselected');
49
+ pane.removeClass('pane_unselected').addClass('pane_selected');
50
+ pane.siblings().removeClass('pane_selected').addClass('pane_unselected');
51
+ }
52
+
53
+
54
+ // extract the containing tabbed panel's ID from the tab's ID
55
+ function get_tabbed_panel_id(tab_id) {
56
+ return tab_id.substring(0, tab_id.indexOf("_tp_"));
57
+ }
58
+
59
+ // Generate the key associated with this page's path and this tabbed_panel,
60
+ // starting from the id of an individual tab.
61
+ function memory_key_from_tab(tab_id) {
62
+ return (location.pathname + "--" + get_tabbed_panel_id(tab_id)).replace(/^\//,'')
63
+ }
64
+
65
+ // Generate the key associated with this page's path and this tabbed_panel,
66
+ // starting from the id of the tabbed panel.
67
+ function memory_key_from_tabbed_panel(tabbed_panel_id) {
68
+ return (location.pathname + "--" + tabbed_panel_id).replace(/^\//,'')
69
+ }
70
+
71
+ // Store which tab was selected for this URL and tabbed panel combination
72
+ function store_tab_preference(tab_id) {
73
+ tab_memory = get_tab_memory();
74
+ if (tab_memory == null) {
75
+ tab_memory = new Object();
76
+ }
77
+ tab_memory[memory_key_from_tab(tab_id)] = tab_id;
78
+ $.Jookie.Set('tab_memory', 'tab_history', tab_memory);
79
+ }
80
+
81
+ function get_tab_memory() {
82
+ tab_memory = $.Jookie.Get('tab_memory','tab_history');
83
+ if(typeof(tab_memory) == 'undefined') {
84
+ tab_memory = {};
85
+ }
86
+ return tab_memory;
87
+ }
88
+
89
+ /*
90
+ License:
91
+ Jookie 1.0 jQuery Plugin
92
+
93
+ Copyright (c) 2008 Jon Combe (http://joncom.be)
94
+
95
+ Permission is hereby granted, free of charge, to any person
96
+ obtaining a copy of this software and associated documentation
97
+ files (the "Software"), to deal in the Software without
98
+ restriction, including without limitation the rights to use,
99
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
100
+ copies of the Software, and to permit persons to whom the
101
+ Software is furnished to do so, subject to the following
102
+ conditions:
103
+
104
+ The above copyright notice and this permission notice shall be
105
+ included in all copies or substantial portions of the Software.
106
+
107
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
108
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
109
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
110
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
111
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
112
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
113
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
114
+ OTHER DEALINGS IN THE SOFTWARE.
115
+ */
116
+
117
+ (function($) {
118
+
119
+ $.Jookie = {
120
+ Data: {},
121
+ Debug: function(a) { Debug(a) },
122
+ Delete: function(a) { Delete(a) }, // delete cookie
123
+ Get: function(a,b) { return Get(a,b) }, // get a single value from a cookie
124
+ Initialise: function(a,b) { Initialise(a,b) },
125
+ Set: function(a,b,c) { Set(a,b,c) }, // set a single value to a cookie
126
+ Unset: function(a,b) { Unset(a,b) } // remove a single value from a cookie
127
+ }
128
+
129
+ // PUBLIC: show debugging information
130
+ function Debug(sName) {
131
+ var lsRegExp = /\+/g;
132
+ var sJSON = unescape(String(Extract(sName)).replace(lsRegExp, " "));
133
+ alert("Name: " + sName +
134
+ "\nLifespan: " + $.Jookie.Data[sName].iLifespan +
135
+ " minutes\nCookie Existed Prior to Init: " + $.Jookie.Data[sName].bMadeEarlier + "\n\n" +
136
+ sJSON);
137
+ }
138
+
139
+ // PUBLIC: delete a cookie
140
+ function Delete(sName) {
141
+ delete $.Jookie.Data[sName];
142
+ document.cookie = (sName + "=; expires=" + (new Date(1990, 6, 3)).toGMTString() + "; path=/");
143
+ }
144
+
145
+ // PRIVATE: extract the contents of a cookie
146
+ function Extract(sName) {
147
+ var vValue = null;
148
+ var aContents = document.cookie.split(';');
149
+ sName += "=";
150
+
151
+ // loop through cookie strings
152
+ for (var iIndex in aContents) {
153
+ var sString = aContents[iIndex];
154
+ while (sString.charAt(0) == " ") {
155
+ sString = sString.substring(1, sString.length);
156
+ }
157
+ if (sString.indexOf(sName) == 0) {
158
+ vValue = sString.substring(sName.length, sString.length);
159
+ break;
160
+ }
161
+ }
162
+
163
+ // return extracted value
164
+ return vValue;
165
+ }
166
+
167
+ // PUBLIC: retrieve a cookie's value
168
+ function Get(sName, sVariableName) {
169
+ return $.Jookie.Data[sName].oValues[sVariableName];
170
+ }
171
+
172
+ // PUBLIC: Initialise the plugin
173
+ function Initialise(sName, iLifespanInMinutes) {
174
+ if (typeof $.Jookie.Data[sName] == "undefined") {
175
+ var oRetrievedValues = {};
176
+ var bCookieExists = false;
177
+
178
+ // extract cookie value
179
+ var vCookieValue = Extract(sName);
180
+ if (vCookieValue !== null) {
181
+ oRetrievedValues = JSON.parse( unescape(String(vCookieValue).replace(/\+/g, " ")) );
182
+ bCookieExists = true;
183
+ }
184
+
185
+ // add cookie details to object
186
+ $.Jookie.Data[sName] = { iLifespan : iLifespanInMinutes,
187
+ bMadeEarlier : bCookieExists,
188
+ oValues : oRetrievedValues };
189
+ Save(sName);
190
+ }
191
+ }
192
+
193
+ // PRIVATE: write cookie to user's browser
194
+ function Save(sName) {
195
+ var sExpires = "";
196
+ if ($.Jookie.Data[sName].iLifespan > 0) {
197
+ var dtDate = new Date();
198
+ dtDate.setMinutes(dtDate.getMinutes() + $.Jookie.Data[sName].iLifespan);
199
+ sExpires = ("; expires=" + dtDate.toGMTString());
200
+ }
201
+ document.cookie = (sName + "=" +
202
+ escape(JSON.stringify($.Jookie.Data[sName].oValues)) +
203
+ sExpires + "; path=/");
204
+ }
205
+
206
+ // PUBLIC: set and save a cookie's value
207
+ function Set(sName, sVariableName, vValue) {
208
+ $.Jookie.Data[sName].oValues[sVariableName] = vValue;
209
+ Save(sName);
210
+ }
211
+
212
+ // PUBLIC: delete a single variable from a cookie
213
+ function Unset(sName, sVariableName) {
214
+ delete $.Jookie.Data[sName].oValues[sVariableName];
215
+ Save(sName);
216
+ }
217
+
218
+ })(jQuery);
219
+
220
+ /*
221
+ http://www.JSON.org/json2.js
222
+ 2008-05-25
223
+
224
+ Public Domain.
225
+
226
+ NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
227
+
228
+ See http://www.JSON.org/js.html
229
+
230
+ This file creates a global JSON object containing two methods: stringify
231
+ and parse.
232
+
233
+ JSON.stringify(value, replacer, space)
234
+ value any JavaScript value, usually an object or array.
235
+
236
+ replacer an optional parameter that determines how object
237
+ values are stringified for objects without a toJSON
238
+ method. It can be a function or an array.
239
+
240
+ space an optional parameter that specifies the indentation
241
+ of nested structures. If it is omitted, the text will
242
+ be packed without extra whitespace. If it is a number,
243
+ it will specify the number of spaces to indent at each
244
+ level. If it is a string (such as '\t' or '&nbsp;'),
245
+ it contains the characters used to indent at each level.
246
+
247
+ This method produces a JSON text from a JavaScript value.
248
+
249
+ When an object value is found, if the object contains a toJSON
250
+ method, its toJSON method will be called and the result will be
251
+ stringified. A toJSON method does not serialize: it returns the
252
+ value represented by the name/value pair that should be serialized,
253
+ or undefined if nothing should be serialized. The toJSON method
254
+ will be passed the key associated with the value, and this will be
255
+ bound to the object holding the key.
256
+
257
+ For example, this would serialize Dates as ISO strings.
258
+
259
+ Date.prototype.toJSON = function (key) {
260
+ function f(n) {
261
+ // Format integers to have at least two digits.
262
+ return n < 10 ? '0' + n : n;
263
+ }
264
+
265
+ return this.getUTCFullYear() + '-' +
266
+ f(this.getUTCMonth() + 1) + '-' +
267
+ f(this.getUTCDate()) + 'T' +
268
+ f(this.getUTCHours()) + ':' +
269
+ f(this.getUTCMinutes()) + ':' +
270
+ f(this.getUTCSeconds()) + 'Z';
271
+ };
272
+
273
+ You can provide an optional replacer method. It will be passed the
274
+ key and value of each member, with this bound to the containing
275
+ object. The value that is returned from your method will be
276
+ serialized. If your method returns undefined, then the member will
277
+ be excluded from the serialization.
278
+
279
+ If the replacer parameter is an array, then it will be used to
280
+ select the members to be serialized. It filters the results such
281
+ that only members with keys listed in the replacer array are
282
+ stringified.
283
+
284
+ Values that do not have JSON representations, such as undefined or
285
+ functions, will not be serialized. Such values in objects will be
286
+ dropped; in arrays they will be replaced with null. You can use
287
+ a replacer function to replace those with JSON values.
288
+ JSON.stringify(undefined) returns undefined.
289
+
290
+ The optional space parameter produces a stringification of the
291
+ value that is filled with line breaks and indentation to make it
292
+ easier to read.
293
+
294
+ If the space parameter is a non-empty string, then that string will
295
+ be used for indentation. If the space parameter is a number, then
296
+ the indentation will be that many spaces.
297
+
298
+ Example:
299
+
300
+ text = JSON.stringify(['e', {pluribus: 'unum'}]);
301
+ // text is '["e",{"pluribus":"unum"}]'
302
+
303
+
304
+ text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
305
+ // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
306
+
307
+ text = JSON.stringify([new Date()], function (key, value) {
308
+ return this[key] instanceof Date ?
309
+ 'Date(' + this[key] + ')' : value;
310
+ });
311
+ // text is '["Date(---current time---)"]'
312
+
313
+
314
+ JSON.parse(text, reviver)
315
+ This method parses a JSON text to produce an object or array.
316
+ It can throw a SyntaxError exception.
317
+
318
+ The optional reviver parameter is a function that can filter and
319
+ transform the results. It receives each of the keys and values,
320
+ and its return value is used instead of the original value.
321
+ If it returns what it received, then the structure is not modified.
322
+ If it returns undefined then the member is deleted.
323
+
324
+ Example:
325
+
326
+ // Parse the text. Values that look like ISO date strings will
327
+ // be converted to Date objects.
328
+
329
+ myData = JSON.parse(text, function (key, value) {
330
+ var a;
331
+ if (typeof value === 'string') {
332
+ a =
333
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
334
+ if (a) {
335
+ return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
336
+ +a[5], +a[6]));
337
+ }
338
+ }
339
+ return value;
340
+ });
341
+
342
+ myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
343
+ var d;
344
+ if (typeof value === 'string' &&
345
+ value.slice(0, 5) === 'Date(' &&
346
+ value.slice(-1) === ')') {
347
+ d = new Date(value.slice(5, -1));
348
+ if (d) {
349
+ return d;
350
+ }
351
+ }
352
+ return value;
353
+ });
354
+
355
+
356
+ This is a reference implementation. You are free to copy, modify, or
357
+ redistribute.
358
+
359
+ This code should be minified before deployment.
360
+ See http://javascript.crockford.com/jsmin.html
361
+
362
+ USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
363
+ NOT CONTROL.
364
+ */
365
+
366
+ /*jslint evil: true */
367
+
368
+ /*global JSON */
369
+
370
+ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
371
+ charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
372
+ getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
373
+ parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
374
+ test, toJSON, toString
375
+ */
376
+
377
+ if (!this.JSON) {
378
+
379
+ // Create a JSON object only if one does not already exist. We create the
380
+ // object in a closure to avoid creating global variables.
381
+
382
+ JSON = function () {
383
+
384
+ function f(n) {
385
+ // Format integers to have at least two digits.
386
+ return n < 10 ? '0' + n : n;
387
+ }
388
+
389
+ Date.prototype.toJSON = function (key) {
390
+
391
+ return this.getUTCFullYear() + '-' +
392
+ f(this.getUTCMonth() + 1) + '-' +
393
+ f(this.getUTCDate()) + 'T' +
394
+ f(this.getUTCHours()) + ':' +
395
+ f(this.getUTCMinutes()) + ':' +
396
+ f(this.getUTCSeconds()) + 'Z';
397
+ };
398
+
399
+ var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
400
+ escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
401
+ gap,
402
+ indent,
403
+ meta = { // table of character substitutions
404
+ '\b': '\\b',
405
+ '\t': '\\t',
406
+ '\n': '\\n',
407
+ '\f': '\\f',
408
+ '\r': '\\r',
409
+ '"' : '\\"',
410
+ '\\': '\\\\'
411
+ },
412
+ rep;
413
+
414
+
415
+ function quote(string) {
416
+
417
+ // If the string contains no control characters, no quote characters, and no
418
+ // backslash characters, then we can safely slap some quotes around it.
419
+ // Otherwise we must also replace the offending characters with safe escape
420
+ // sequences.
421
+
422
+ escapeable.lastIndex = 0;
423
+ return escapeable.test(string) ?
424
+ '"' + string.replace(escapeable, function (a) {
425
+ var c = meta[a];
426
+ if (typeof c === 'string') {
427
+ return c;
428
+ }
429
+ return '\\u' + ('0000' +
430
+ (+(a.charCodeAt(0))).toString(16)).slice(-4);
431
+ }) + '"' :
432
+ '"' + string + '"';
433
+ }
434
+
435
+
436
+ function str(key, holder) {
437
+
438
+ // Produce a string from holder[key].
439
+
440
+ var i, // The loop counter.
441
+ k, // The member key.
442
+ v, // The member value.
443
+ length,
444
+ mind = gap,
445
+ partial,
446
+ value = holder[key];
447
+
448
+ // If the value has a toJSON method, call it to obtain a replacement value.
449
+
450
+ if (value && typeof value === 'object' &&
451
+ typeof value.toJSON === 'function') {
452
+ value = value.toJSON(key);
453
+ }
454
+
455
+ // If we were called with a replacer function, then call the replacer to
456
+ // obtain a replacement value.
457
+
458
+ if (typeof rep === 'function') {
459
+ value = rep.call(holder, key, value);
460
+ }
461
+
462
+ // What happens next depends on the value's type.
463
+
464
+ switch (typeof value) {
465
+ case 'string':
466
+ return quote(value);
467
+
468
+ case 'number':
469
+
470
+ // JSON numbers must be finite. Encode non-finite numbers as null.
471
+
472
+ return isFinite(value) ? String(value) : 'null';
473
+
474
+ case 'boolean':
475
+ case 'null':
476
+
477
+ // If the value is a boolean or null, convert it to a string. Note:
478
+ // typeof null does not produce 'null'. The case is included here in
479
+ // the remote chance that this gets fixed someday.
480
+
481
+ return String(value);
482
+
483
+ // If the type is 'object', we might be dealing with an object or an array or
484
+ // null.
485
+
486
+ case 'object':
487
+
488
+ // Due to a specification blunder in ECMAScript, typeof null is 'object',
489
+ // so watch out for that case.
490
+
491
+ if (!value) {
492
+ return 'null';
493
+ }
494
+
495
+ // Make an array to hold the partial results of stringifying this object value.
496
+
497
+ gap += indent;
498
+ partial = [];
499
+
500
+ // If the object has a dontEnum length property, we'll treat it as an array.
501
+
502
+ if (typeof value.length === 'number' &&
503
+ !(value.propertyIsEnumerable('length'))) {
504
+
505
+ // The object is an array. Stringify every element. Use null as a placeholder
506
+ // for non-JSON values.
507
+
508
+ length = value.length;
509
+ for (i = 0; i < length; i += 1) {
510
+ partial[i] = str(i, value) || 'null';
511
+ }
512
+
513
+ // Join all of the elements together, separated with commas, and wrap them in
514
+ // brackets.
515
+
516
+ v = partial.length === 0 ? '[]' :
517
+ gap ? '[\n' + gap +
518
+ partial.join(',\n' + gap) + '\n' +
519
+ mind + ']' :
520
+ '[' + partial.join(',') + ']';
521
+ gap = mind;
522
+ return v;
523
+ }
524
+
525
+ // If the replacer is an array, use it to select the members to be stringified.
526
+
527
+ if (rep && typeof rep === 'object') {
528
+ length = rep.length;
529
+ for (i = 0; i < length; i += 1) {
530
+ k = rep[i];
531
+ if (typeof k === 'string') {
532
+ v = str(k, value, rep);
533
+ if (v) {
534
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
535
+ }
536
+ }
537
+ }
538
+ } else {
539
+
540
+ // Otherwise, iterate through all of the keys in the object.
541
+
542
+ for (k in value) {
543
+ if (Object.hasOwnProperty.call(value, k)) {
544
+ v = str(k, value, rep);
545
+ if (v) {
546
+ partial.push(quote(k) + (gap ? ': ' : ':') + v);
547
+ }
548
+ }
549
+ }
550
+ }
551
+
552
+ // Join all of the member texts together, separated with commas,
553
+ // and wrap them in braces.
554
+
555
+ v = partial.length === 0 ? '{}' :
556
+ gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
557
+ mind + '}' : '{' + partial.join(',') + '}';
558
+ gap = mind;
559
+ return v;
560
+ }
561
+ }
562
+
563
+ // Return the JSON object containing the stringify and parse methods.
564
+
565
+ return {
566
+ stringify: function (value, replacer, space) {
567
+
568
+ // The stringify method takes a value and an optional replacer, and an optional
569
+ // space parameter, and returns a JSON text. The replacer can be a function
570
+ // that can replace values, or an array of strings that will select the keys.
571
+ // A default replacer method can be provided. Use of the space parameter can
572
+ // produce text that is more easily readable.
573
+
574
+ var i;
575
+ gap = '';
576
+ indent = '';
577
+
578
+ // If the space parameter is a number, make an indent string containing that
579
+ // many spaces.
580
+
581
+ if (typeof space === 'number') {
582
+ for (i = 0; i < space; i += 1) {
583
+ indent += ' ';
584
+ }
585
+
586
+ // If the space parameter is a string, it will be used as the indent string.
587
+
588
+ } else if (typeof space === 'string') {
589
+ indent = space;
590
+ }
591
+
592
+ // If there is a replacer, it must be a function or an array.
593
+ // Otherwise, throw an error.
594
+
595
+ rep = replacer;
596
+ if (replacer && typeof replacer !== 'function' &&
597
+ (typeof replacer !== 'object' ||
598
+ typeof replacer.length !== 'number')) {
599
+ throw new Error('JSON.stringify');
600
+ }
601
+
602
+ // Make a fake root object containing our value under the key of ''.
603
+ // Return the result of stringifying the value.
604
+
605
+ return str('', {'': value});
606
+ },
607
+
608
+
609
+ parse: function (text, reviver) {
610
+
611
+ // The parse method takes a text and an optional reviver function, and returns
612
+ // a JavaScript value if the text is a valid JSON text.
613
+
614
+ var j;
615
+
616
+ function walk(holder, key) {
617
+
618
+ // The walk method is used to recursively walk the resulting structure so
619
+ // that modifications can be made.
620
+
621
+ var k, v, value = holder[key];
622
+ if (value && typeof value === 'object') {
623
+ for (k in value) {
624
+ if (Object.hasOwnProperty.call(value, k)) {
625
+ v = walk(value, k);
626
+ if (v !== undefined) {
627
+ value[k] = v;
628
+ } else {
629
+ delete value[k];
630
+ }
631
+ }
632
+ }
633
+ }
634
+ return reviver.call(holder, key, value);
635
+ }
636
+
637
+
638
+ // Parsing happens in four stages. In the first stage, we replace certain
639
+ // Unicode characters with escape sequences. JavaScript handles many characters
640
+ // incorrectly, either silently deleting them, or treating them as line endings.
641
+
642
+ cx.lastIndex = 0;
643
+ if (cx.test(text)) {
644
+ text = text.replace(cx, function (a) {
645
+ return '\\u' + ('0000' +
646
+ (+(a.charCodeAt(0))).toString(16)).slice(-4);
647
+ });
648
+ }
649
+
650
+ // In the second stage, we run the text against regular expressions that look
651
+ // for non-JSON patterns. We are especially concerned with '()' and 'new'
652
+ // because they can cause invocation, and '=' because it can cause mutation.
653
+ // But just to be safe, we want to reject all unexpected forms.
654
+
655
+ // We split the second stage into 4 regexp operations in order to work around
656
+ // crippling inefficiencies in IE's and Safari's regexp engines. First we
657
+ // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
658
+ // replace all simple value tokens with ']' characters. Third, we delete all
659
+ // open brackets that follow a colon or comma or that begin the text. Finally,
660
+ // we look to see that the remaining characters are only whitespace or ']' or
661
+ // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
662
+
663
+ if (/^[\],:{}\s]*$/.
664
+ test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
665
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
666
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
667
+
668
+ // In the third stage we use the eval function to compile the text into a
669
+ // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
670
+ // in JavaScript: it can begin a block or an object literal. We wrap the text
671
+ // in parens to eliminate the ambiguity.
672
+
673
+ j = eval('(' + text + ')');
674
+
675
+ // In the optional fourth stage, we recursively walk the new structure, passing
676
+ // each name/value pair to a reviver function for possible transformation.
677
+
678
+ return typeof reviver === 'function' ?
679
+ walk({'': j}, '') : j;
680
+ }
681
+
682
+ // If the text is not JSON parseable, then a SyntaxError is thrown.
683
+
684
+ throw new SyntaxError('JSON.parse');
685
+ }
686
+ };
687
+ }();
688
+ }