From 692233ef766ed54e321e2b99fcd8d0b345e1762b Mon Sep 17 00:00:00 2001
From: David Regla
Date: Sun, 18 Dec 2011 14:38:38 -0600
Subject: [PATCH 01/26] fixed variable name
---
model/list/local/local.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/model/list/local/local.js b/model/list/local/local.js
index 568a63cb..eb9bfb0f 100644
--- a/model/list/local/local.js
+++ b/model/list/local/local.js
@@ -26,7 +26,7 @@ $.Model.List("jQuery.Model.List.Local",
// go through and listen to instance updating
var ids = [], days = this.days;
this.each(function(i, inst){
- window.localStorage[inst.identity()] = instance.attrs();
+ window.localStorage[inst.identity()] = inst.attrs();
ids.push(inst.identity());
});
window.localStorage[name] = {
From 8a7ffcb6354d8230de9a3e38aaf3462499766406 Mon Sep 17 00:00:00 2001
From: David Regla
Date: Mon, 30 Jan 2012 15:28:16 -0600
Subject: [PATCH 02/26] merge
---
dom/route/route.js | 471 ---------------------------------------------
1 file changed, 471 deletions(-)
delete mode 100644 dom/route/route.js
diff --git a/dom/route/route.js b/dom/route/route.js
deleted file mode 100644
index 4159adec..00000000
--- a/dom/route/route.js
+++ /dev/null
@@ -1,471 +0,0 @@
-steal('jquery/lang/observe', 'jquery/event/hashchange', 'jquery/lang/string/deparam',
-function( $ ) {
-
- // Helper methods used for matching routes.
- var
- // RegEx used to match route variables of the type ':name'.
- // Any word character or a period is matched.
- matcher = /\:([\w\.]+)/g,
- // Regular expression for identifying &key=value lists.
- paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
- // Converts a JS Object into a list of parameters that can be
- // inserted into an html element tag.
- makeProps = function( props ) {
- var html = [],
- name, val;
- each(props, function(name, val){
- if ( name === 'className' ) {
- name = 'class'
- }
- val && html.push(escapeHTML(name), "=\"", escapeHTML(val), "\" ");
- })
- return html.join("")
- },
- // Escapes ' and " for safe insertion into html tag parameters.
- escapeHTML = function( content ) {
- return content.replace(/"/g, '"').replace(/'/g, "'");
- },
- // Checks if a route matches the data provided. If any route variable
- // is not present in the data the route does not match. If all route
- // variables are present in the data the number of matches is returned
- // to allow discerning between general and more specific routes.
- matchesData = function(route, data) {
- var count = 0;
- for ( var i = 0; i < route.names.length; i++ ) {
- if (!data.hasOwnProperty(route.names[i]) ) {
- return -1;
- }
- count++;
- }
- return count;
- },
- //
- onready = true,
- location = window.location,
- encode = encodeURIComponent,
- decode = decodeURIComponent,
- each = $.each,
- extend = $.extend;
-
- /**
- * @class jQuery.route
- * @inherits jQuery.Observe
- * @plugin jquery/dom/route
- * @parent dom
- * @tag 3.2
- *
- * jQuery.route helps manage browser history (and
- * client state) by
- * synchronizing the window.location.hash with
- * an [jQuery.Observe].
- *
- * ## Background Information
- *
- * To support the browser's back button and bookmarking
- * in an Ajax application, most applications use
- * the window.location.hash. By
- * changing the hash (via a link or JavaScript),
- * one is able to add to the browser's history
- * without changing the page. The [jQuery.event.special.hashchange event] allows
- * you to listen to when the hash is changed.
- *
- * Combined, this provides the basics needed to
- * create history enabled Ajax websites. However,
- * jQuery.Route addresses several other needs such as:
- *
- * - Pretty Routes
- * - Keeping routes independent of application code
- * - Listening to specific parts of the history changing
- * - Setup / Teardown of widgets.
- *
- * ## How it works
- *
- * $.route is a [jQuery.Observe $.Observe] that represents the
- * window.location.hash as an
- * object. For example, if the hash looks like:
- *
- * #!type=videos&id=5
- *
- * the data in $.route would look like:
- *
- * { type: 'videos', id: 5 }
- *
- *
- * $.route keeps the state of the hash in-sync with the data in
- * $.route.
- *
- * ## $.Observe
- *
- * $.route is a [jQuery.Observe $.Observe]. Understanding
- * $.Observe is essential for using $.route correctly.
- *
- * You can
- * listen to changes in an Observe with bind and
- * delegate and change $.route's properties with
- * attr and attrs.
- *
- * ### Listening to changes in an Observable
- *
- * Listen to changes in history
- * by [jQuery.Observe.prototype.bind bind]ing to
- * changes in $.route like:
- *
- * $.route.bind('change', function(ev, attr, how, newVal, oldVal) {
- *
- * })
- *
- * - attr - the name of the changed attribute
- * - how - the type of Observe change event (add, set or remove)
- * - newVal/oldVal - the new and old values of the attribute
- *
- * You can also listen to specific changes
- * with [jQuery.Observe.prototype.delegate delegate]:
- *
- * $.route.delegate('id','change', function(){ ... })
- *
- * Observe lets you listen to the following events:
- *
- * - change - any change to the object
- * - add - a property is added
- * - set - a property value is added or changed
- * - remove - a property is removed
- *
- * Listening for add is useful for widget setup
- * behavior, remove is useful for teardown.
- *
- * ### Updating an observable
- *
- * Create changes in the route data like:
- *
- * $.route.attr('type','images');
- *
- * Or change multiple properties at once with
- * [jQuery.Observe.prototype.attrs attrs]:
- *
- * $.route.attr({type: 'pages', id: 5}, true)
- *
- * When you make changes to $.route, they will automatically
- * change the hash.
- *
- * ## Creating a Route
- *
- * Use $.route(url, defaults) to create a
- * route. A route is a mapping from a url to
- * an object (that is the $.route's state).
- *
- * If no routes are added, or no route is matched,
- * $.route's data is updated with the [jQuery.String.deparam deparamed]
- * hash.
- *
- * location.hash = "#!type=videos";
- * // $.route -> {type : "videos"}
- *
- * Once routes are added and the hash changes,
- * $.route looks for matching routes and uses them
- * to update $.route's data.
- *
- * $.route( "content/:type" );
- * location.hash = "#!content/images";
- * // $.route -> {type : "images"}
- *
- * Default values can also be added:
- *
- * $.route("content/:type",{type: "videos" });
- * location.hash = "#!content/"
- * // $.route -> {type : "videos"}
- *
- * ## Delay setting $.route
- *
- * By default, $.route sets its initial data
- * on document ready. Sometimes, you want to wait to set
- * this data. To wait, call:
- *
- * $.route.ready(false);
- *
- * and when ready, call:
- *
- * $.route.ready(true);
- *
- * ## Changing the route.
- *
- * Typically, you never want to set location.hash
- * directly. Instead, you can change properties on $.route
- * like:
- *
- * $.route.attr('type', 'videos')
- *
- * This will automatically look up the appropriate
- * route and update the hash.
- *
- * Often, you want to create links. $.route provides
- * the [jQuery.route.link] and [jQuery.route.url] helpers to make this
- * easy:
- *
- * $.route.link("Videos", {type: 'videos'})
- *
- * @param {String} url the fragment identifier to match.
- * @param {Object} [defaults] an object of default values
- * @return {jQuery.route}
- */
- $.route = function( url, defaults ) {
- // Extract the variable names and replace with regEx that will match an atual URL with values.
- var names = [],
- test = url.replace(matcher, function( whole, name ) {
- names.push(name)
- // TODO: I think this should have a +
- return "([^\\/\\&]*)" // The '\\' is for string-escaping giving single '\' for regEx escaping
- });
-
- // Add route in a form that can be easily figured out
- $.route.routes[url] = {
- // A regular expression that will match the route when variable values
- // are present; i.e. for :page/:type the regEx is /([\w\.]*)/([\w\.]*)/ which
- // will match for any value of :page and :type (word chars or period).
- test: new RegExp("^" + test+"($|&)"),
- // The original URL, same as the index for this entry in routes.
- route: url,
- // An array of all the variable names in this route
- names: names,
- // Default values provided for the variables.
- defaults: defaults || {},
- // The number of parts in the URL separated by '/'.
- length: url.split('/').length
- }
- return $.route;
- };
-
- extend($.route, {
- /**
- * Parameterizes the raw JS object representation provided in data.
- * If a route matching the provided data is found that URL is built
- * from the data. Any remaining data is added at the end of the
- * URL as & separated key/value parameters.
- *
- * @param {Object} data
- * @return {String} The route URL and & separated parameters.
- */
- param: function( data ) {
- // Check if the provided data keys match the names in any routes;
- // get the one with the most matches.
- var route,
- // need it to be at least 1 match
- matches = 0,
- matchCount,
- routeName = data.route;
-
- delete data.route;
- // if we have a route name in our $.route data, use it
- if(routeName && (route = $.route.routes[routeName])){
-
- } else {
- // otherwise find route
- each($.route.routes, function(name, temp){
- matchCount = matchesData(temp, data);
- if ( matchCount > matches ) {
- route = temp;
- matches = matchCount
- }
- });
- }
- // if this is match
-
- if ( route ) {
- var cpy = extend({}, data),
- // Create the url by replacing the var names with the provided data.
- // If the default value is found an empty string is inserted.
- res = route.route.replace(matcher, function( whole, name ) {
- delete cpy[name];
- return data[name] === route.defaults[name] ? "" : encode( data[name] );
- }),
- after;
- // remove matching default values
- each(route.defaults, function(name,val){
- if(cpy[name] === val) {
- delete cpy[name]
- }
- })
-
- // The remaining elements of data are added as
- // $amp; separated parameters to the url.
- after = $.param(cpy);
- return res + (after ? "&" + after : "")
- }
- // If no route was found there is no hash URL, only paramters.
- return $.isEmptyObject(data) ? "" : "&" + $.param(data);
- },
- /**
- * Populate the JS data object from a given URL.
- *
- * @param {Object} url
- */
- deparam: function( url ) {
- // See if the url matches any routes by testing it against the route.test regEx.
- // By comparing the URL length the most specialized route that matches is used.
- var route = {
- length: -1
- };
- each($.route.routes, function(name, temp){
- if ( temp.test.test(url) && temp.length > route.length ) {
- route = temp;
- }
- });
- // If a route was matched
- if ( route.length > -1 ) {
- var // Since RegEx backreferences are used in route.test (round brackets)
- // the parts will contain the full matched string and each variable (backreferenced) value.
- parts = url.match(route.test),
- // start will contain the full matched string; parts contain the variable values.
- start = parts.shift(),
- // The remainder will be the &key=value list at the end of the URL.
- remainder = url.substr(start.length - (parts[parts.length-1] === "&" ? 1 : 0) ),
- // If there is a remainder and it contains a &key=value list deparam it.
- obj = (remainder && paramsMatcher.test(remainder)) ? $.String.deparam( remainder.slice(1) ) : {};
-
- // Add the default values for this route
- obj = extend(true, {}, route.defaults, obj);
- // Overwrite each of the default values in obj with those in parts if that part is not empty.
- each(parts,function(i, part){
- if ( part && part !== '&') {
- obj[route.names[i]] = decode( part );
- }
- });
- obj.route = route.route;
- return obj;
- }
- // If no route was matched it is parsed as a &key=value list.
- if ( url.charAt(0) !== '&' ) {
- url = '&' + url;
- }
- return paramsMatcher.test(url) ? $.String.deparam( url.slice(1) ) : {};
- },
- /**
- * @hide
- * A $.Observe that represents the state of the history.
- */
- data: new $.Observe({}),
- /**
- * @attribute
- * @type Object
- * @hide
- *
- * A list of routes recognized by the router indixed by the url used to add it.
- * Each route is an object with these members:
- *
- * - test - A regular expression that will match the route when variable values
- * are present; i.e. for :page/:type the regEx is /([\w\.]*)/([\w\.]*)/ which
- * will match for any value of :page and :type (word chars or period).
- *
- * - route - The original URL, same as the index for this entry in routes.
- *
- * - names - An array of all the variable names in this route
- *
- * - defaults - Default values provided for the variables or an empty object.
- *
- * - length - The number of parts in the URL separated by '/'.
- */
- routes: {},
- /**
- * Indicates that all routes have been added and sets $.route.data
- * based upon the routes and the current hash.
- *
- * By default, ready is fired on jQuery's ready event. Sometimes
- * you might want it to happen sooner or earlier. To do this call
- *
- * $.route.ready(false); //prevents firing by the ready event
- * $.route.ready(true); // fire the first route change
- *
- * @param {Boolean} [start]
- * @return $.route
- */
- ready: function(val) {
- if( val === false ) {
- onready = false;
- }
- if( val === true || onready === true ) {
- setState();
- }
- return $.route;
- },
- /**
- * Returns a url from the options
- * @param {Object} options
- * @param {Boolean} merge true if the options should be merged with the current options
- * @return {String}
- */
- url: function( options, merge ) {
- if (merge) {
- return "#!" + $.route.param(extend({}, curParams, options))
- } else {
- return "#!" + $.route.param(options)
- }
- },
- /**
- * Returns a link
- * @param {Object} name The text of the link.
- * @param {Object} options The route options (variables)
- * @param {Object} props Properties of the <a> other than href.
- * @param {Boolean} merge true if the options should be merged with the current options
- */
- link: function( name, options, props, merge ) {
- return "" + name + "";
- },
- /**
- * Returns true if the options represent the current page.
- * @param {Object} options
- * @return {Boolean}
- */
- current: function( options ) {
- return location.hash == "#!" + $.route.param(options)
- }
- });
- // onready
- $(function() {
- $.route.ready();
- });
-
- // The functions in the following list applied to $.route (e.g. $.route.attr('...')) will
- // instead act on the $.route.data Observe.
- each(['bind','unbind','delegate','undelegate','attr','attrs','serialize','removeAttr'], function(i, name){
- $.route[name] = function(){
- return $.route.data[name].apply($.route.data, arguments)
- }
- })
-
- var // A throttled function called multiple times will only fire once the
- // timer runs down. Each call resets the timer.
- throttle = function( func ) {
- var timer;
- return function() {
- var args = arguments,
- self = this;
- clearTimeout(timer);
- timer = setTimeout(function(){
- func.apply(self, args)
- }, 1);
- }
- },
- // Intermediate storage for $.route.data.
- curParams,
- // Deparameterizes the portion of the hash of interest and assign the
- // values to the $.route.data removing existing values no longer in the hash.
- setState = function() {
- var hash = location.hash.substr(1, 1) === '!' ?
- location.hash.slice(2) :
- location.hash.slice(1); // everything after #!
- curParams = $.route.deparam( hash );
- $.route.attrs(curParams, true);
- };
-
- // If the hash changes, update the $.route.data
- $(window).bind('hashchange', setState);
-
- // If the $.route.data changes, update the hash.
- // Using .serialize() retrieves the raw data contained in the observable.
- // This function is throttled so it only updates once even if multiple values changed.
- $.route.bind("change", throttle(function() {
- location.hash = "#!" + $.route.param($.route.serialize())
- }));
-})
\ No newline at end of file
From df1bcabbdfc80f5caa571a058e5e0a05b74c734e Mon Sep 17 00:00:00 2001
From: David Regla
Date: Mon, 30 Jan 2012 15:35:19 -0600
Subject: [PATCH 03/26] merge
---
dom/route/route.js | 471 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 471 insertions(+)
create mode 100644 dom/route/route.js
diff --git a/dom/route/route.js b/dom/route/route.js
new file mode 100644
index 00000000..4159adec
--- /dev/null
+++ b/dom/route/route.js
@@ -0,0 +1,471 @@
+steal('jquery/lang/observe', 'jquery/event/hashchange', 'jquery/lang/string/deparam',
+function( $ ) {
+
+ // Helper methods used for matching routes.
+ var
+ // RegEx used to match route variables of the type ':name'.
+ // Any word character or a period is matched.
+ matcher = /\:([\w\.]+)/g,
+ // Regular expression for identifying &key=value lists.
+ paramsMatcher = /^(?:&[^=]+=[^&]*)+/,
+ // Converts a JS Object into a list of parameters that can be
+ // inserted into an html element tag.
+ makeProps = function( props ) {
+ var html = [],
+ name, val;
+ each(props, function(name, val){
+ if ( name === 'className' ) {
+ name = 'class'
+ }
+ val && html.push(escapeHTML(name), "=\"", escapeHTML(val), "\" ");
+ })
+ return html.join("")
+ },
+ // Escapes ' and " for safe insertion into html tag parameters.
+ escapeHTML = function( content ) {
+ return content.replace(/"/g, '"').replace(/'/g, "'");
+ },
+ // Checks if a route matches the data provided. If any route variable
+ // is not present in the data the route does not match. If all route
+ // variables are present in the data the number of matches is returned
+ // to allow discerning between general and more specific routes.
+ matchesData = function(route, data) {
+ var count = 0;
+ for ( var i = 0; i < route.names.length; i++ ) {
+ if (!data.hasOwnProperty(route.names[i]) ) {
+ return -1;
+ }
+ count++;
+ }
+ return count;
+ },
+ //
+ onready = true,
+ location = window.location,
+ encode = encodeURIComponent,
+ decode = decodeURIComponent,
+ each = $.each,
+ extend = $.extend;
+
+ /**
+ * @class jQuery.route
+ * @inherits jQuery.Observe
+ * @plugin jquery/dom/route
+ * @parent dom
+ * @tag 3.2
+ *
+ * jQuery.route helps manage browser history (and
+ * client state) by
+ * synchronizing the window.location.hash with
+ * an [jQuery.Observe].
+ *
+ * ## Background Information
+ *
+ * To support the browser's back button and bookmarking
+ * in an Ajax application, most applications use
+ * the window.location.hash. By
+ * changing the hash (via a link or JavaScript),
+ * one is able to add to the browser's history
+ * without changing the page. The [jQuery.event.special.hashchange event] allows
+ * you to listen to when the hash is changed.
+ *
+ * Combined, this provides the basics needed to
+ * create history enabled Ajax websites. However,
+ * jQuery.Route addresses several other needs such as:
+ *
+ * - Pretty Routes
+ * - Keeping routes independent of application code
+ * - Listening to specific parts of the history changing
+ * - Setup / Teardown of widgets.
+ *
+ * ## How it works
+ *
+ * $.route is a [jQuery.Observe $.Observe] that represents the
+ * window.location.hash as an
+ * object. For example, if the hash looks like:
+ *
+ * #!type=videos&id=5
+ *
+ * the data in $.route would look like:
+ *
+ * { type: 'videos', id: 5 }
+ *
+ *
+ * $.route keeps the state of the hash in-sync with the data in
+ * $.route.
+ *
+ * ## $.Observe
+ *
+ * $.route is a [jQuery.Observe $.Observe]. Understanding
+ * $.Observe is essential for using $.route correctly.
+ *
+ * You can
+ * listen to changes in an Observe with bind and
+ * delegate and change $.route's properties with
+ * attr and attrs.
+ *
+ * ### Listening to changes in an Observable
+ *
+ * Listen to changes in history
+ * by [jQuery.Observe.prototype.bind bind]ing to
+ * changes in $.route like:
+ *
+ * $.route.bind('change', function(ev, attr, how, newVal, oldVal) {
+ *
+ * })
+ *
+ * - attr - the name of the changed attribute
+ * - how - the type of Observe change event (add, set or remove)
+ * - newVal/oldVal - the new and old values of the attribute
+ *
+ * You can also listen to specific changes
+ * with [jQuery.Observe.prototype.delegate delegate]:
+ *
+ * $.route.delegate('id','change', function(){ ... })
+ *
+ * Observe lets you listen to the following events:
+ *
+ * - change - any change to the object
+ * - add - a property is added
+ * - set - a property value is added or changed
+ * - remove - a property is removed
+ *
+ * Listening for add is useful for widget setup
+ * behavior, remove is useful for teardown.
+ *
+ * ### Updating an observable
+ *
+ * Create changes in the route data like:
+ *
+ * $.route.attr('type','images');
+ *
+ * Or change multiple properties at once with
+ * [jQuery.Observe.prototype.attrs attrs]:
+ *
+ * $.route.attr({type: 'pages', id: 5}, true)
+ *
+ * When you make changes to $.route, they will automatically
+ * change the hash.
+ *
+ * ## Creating a Route
+ *
+ * Use $.route(url, defaults) to create a
+ * route. A route is a mapping from a url to
+ * an object (that is the $.route's state).
+ *
+ * If no routes are added, or no route is matched,
+ * $.route's data is updated with the [jQuery.String.deparam deparamed]
+ * hash.
+ *
+ * location.hash = "#!type=videos";
+ * // $.route -> {type : "videos"}
+ *
+ * Once routes are added and the hash changes,
+ * $.route looks for matching routes and uses them
+ * to update $.route's data.
+ *
+ * $.route( "content/:type" );
+ * location.hash = "#!content/images";
+ * // $.route -> {type : "images"}
+ *
+ * Default values can also be added:
+ *
+ * $.route("content/:type",{type: "videos" });
+ * location.hash = "#!content/"
+ * // $.route -> {type : "videos"}
+ *
+ * ## Delay setting $.route
+ *
+ * By default, $.route sets its initial data
+ * on document ready. Sometimes, you want to wait to set
+ * this data. To wait, call:
+ *
+ * $.route.ready(false);
+ *
+ * and when ready, call:
+ *
+ * $.route.ready(true);
+ *
+ * ## Changing the route.
+ *
+ * Typically, you never want to set location.hash
+ * directly. Instead, you can change properties on $.route
+ * like:
+ *
+ * $.route.attr('type', 'videos')
+ *
+ * This will automatically look up the appropriate
+ * route and update the hash.
+ *
+ * Often, you want to create links. $.route provides
+ * the [jQuery.route.link] and [jQuery.route.url] helpers to make this
+ * easy:
+ *
+ * $.route.link("Videos", {type: 'videos'})
+ *
+ * @param {String} url the fragment identifier to match.
+ * @param {Object} [defaults] an object of default values
+ * @return {jQuery.route}
+ */
+ $.route = function( url, defaults ) {
+ // Extract the variable names and replace with regEx that will match an atual URL with values.
+ var names = [],
+ test = url.replace(matcher, function( whole, name ) {
+ names.push(name)
+ // TODO: I think this should have a +
+ return "([^\\/\\&]*)" // The '\\' is for string-escaping giving single '\' for regEx escaping
+ });
+
+ // Add route in a form that can be easily figured out
+ $.route.routes[url] = {
+ // A regular expression that will match the route when variable values
+ // are present; i.e. for :page/:type the regEx is /([\w\.]*)/([\w\.]*)/ which
+ // will match for any value of :page and :type (word chars or period).
+ test: new RegExp("^" + test+"($|&)"),
+ // The original URL, same as the index for this entry in routes.
+ route: url,
+ // An array of all the variable names in this route
+ names: names,
+ // Default values provided for the variables.
+ defaults: defaults || {},
+ // The number of parts in the URL separated by '/'.
+ length: url.split('/').length
+ }
+ return $.route;
+ };
+
+ extend($.route, {
+ /**
+ * Parameterizes the raw JS object representation provided in data.
+ * If a route matching the provided data is found that URL is built
+ * from the data. Any remaining data is added at the end of the
+ * URL as & separated key/value parameters.
+ *
+ * @param {Object} data
+ * @return {String} The route URL and & separated parameters.
+ */
+ param: function( data ) {
+ // Check if the provided data keys match the names in any routes;
+ // get the one with the most matches.
+ var route,
+ // need it to be at least 1 match
+ matches = 0,
+ matchCount,
+ routeName = data.route;
+
+ delete data.route;
+ // if we have a route name in our $.route data, use it
+ if(routeName && (route = $.route.routes[routeName])){
+
+ } else {
+ // otherwise find route
+ each($.route.routes, function(name, temp){
+ matchCount = matchesData(temp, data);
+ if ( matchCount > matches ) {
+ route = temp;
+ matches = matchCount
+ }
+ });
+ }
+ // if this is match
+
+ if ( route ) {
+ var cpy = extend({}, data),
+ // Create the url by replacing the var names with the provided data.
+ // If the default value is found an empty string is inserted.
+ res = route.route.replace(matcher, function( whole, name ) {
+ delete cpy[name];
+ return data[name] === route.defaults[name] ? "" : encode( data[name] );
+ }),
+ after;
+ // remove matching default values
+ each(route.defaults, function(name,val){
+ if(cpy[name] === val) {
+ delete cpy[name]
+ }
+ })
+
+ // The remaining elements of data are added as
+ // $amp; separated parameters to the url.
+ after = $.param(cpy);
+ return res + (after ? "&" + after : "")
+ }
+ // If no route was found there is no hash URL, only paramters.
+ return $.isEmptyObject(data) ? "" : "&" + $.param(data);
+ },
+ /**
+ * Populate the JS data object from a given URL.
+ *
+ * @param {Object} url
+ */
+ deparam: function( url ) {
+ // See if the url matches any routes by testing it against the route.test regEx.
+ // By comparing the URL length the most specialized route that matches is used.
+ var route = {
+ length: -1
+ };
+ each($.route.routes, function(name, temp){
+ if ( temp.test.test(url) && temp.length > route.length ) {
+ route = temp;
+ }
+ });
+ // If a route was matched
+ if ( route.length > -1 ) {
+ var // Since RegEx backreferences are used in route.test (round brackets)
+ // the parts will contain the full matched string and each variable (backreferenced) value.
+ parts = url.match(route.test),
+ // start will contain the full matched string; parts contain the variable values.
+ start = parts.shift(),
+ // The remainder will be the &key=value list at the end of the URL.
+ remainder = url.substr(start.length - (parts[parts.length-1] === "&" ? 1 : 0) ),
+ // If there is a remainder and it contains a &key=value list deparam it.
+ obj = (remainder && paramsMatcher.test(remainder)) ? $.String.deparam( remainder.slice(1) ) : {};
+
+ // Add the default values for this route
+ obj = extend(true, {}, route.defaults, obj);
+ // Overwrite each of the default values in obj with those in parts if that part is not empty.
+ each(parts,function(i, part){
+ if ( part && part !== '&') {
+ obj[route.names[i]] = decode( part );
+ }
+ });
+ obj.route = route.route;
+ return obj;
+ }
+ // If no route was matched it is parsed as a &key=value list.
+ if ( url.charAt(0) !== '&' ) {
+ url = '&' + url;
+ }
+ return paramsMatcher.test(url) ? $.String.deparam( url.slice(1) ) : {};
+ },
+ /**
+ * @hide
+ * A $.Observe that represents the state of the history.
+ */
+ data: new $.Observe({}),
+ /**
+ * @attribute
+ * @type Object
+ * @hide
+ *
+ * A list of routes recognized by the router indixed by the url used to add it.
+ * Each route is an object with these members:
+ *
+ * - test - A regular expression that will match the route when variable values
+ * are present; i.e. for :page/:type the regEx is /([\w\.]*)/([\w\.]*)/ which
+ * will match for any value of :page and :type (word chars or period).
+ *
+ * - route - The original URL, same as the index for this entry in routes.
+ *
+ * - names - An array of all the variable names in this route
+ *
+ * - defaults - Default values provided for the variables or an empty object.
+ *
+ * - length - The number of parts in the URL separated by '/'.
+ */
+ routes: {},
+ /**
+ * Indicates that all routes have been added and sets $.route.data
+ * based upon the routes and the current hash.
+ *
+ * By default, ready is fired on jQuery's ready event. Sometimes
+ * you might want it to happen sooner or earlier. To do this call
+ *
+ * $.route.ready(false); //prevents firing by the ready event
+ * $.route.ready(true); // fire the first route change
+ *
+ * @param {Boolean} [start]
+ * @return $.route
+ */
+ ready: function(val) {
+ if( val === false ) {
+ onready = false;
+ }
+ if( val === true || onready === true ) {
+ setState();
+ }
+ return $.route;
+ },
+ /**
+ * Returns a url from the options
+ * @param {Object} options
+ * @param {Boolean} merge true if the options should be merged with the current options
+ * @return {String}
+ */
+ url: function( options, merge ) {
+ if (merge) {
+ return "#!" + $.route.param(extend({}, curParams, options))
+ } else {
+ return "#!" + $.route.param(options)
+ }
+ },
+ /**
+ * Returns a link
+ * @param {Object} name The text of the link.
+ * @param {Object} options The route options (variables)
+ * @param {Object} props Properties of the <a> other than href.
+ * @param {Boolean} merge true if the options should be merged with the current options
+ */
+ link: function( name, options, props, merge ) {
+ return "" + name + "";
+ },
+ /**
+ * Returns true if the options represent the current page.
+ * @param {Object} options
+ * @return {Boolean}
+ */
+ current: function( options ) {
+ return location.hash == "#!" + $.route.param(options)
+ }
+ });
+ // onready
+ $(function() {
+ $.route.ready();
+ });
+
+ // The functions in the following list applied to $.route (e.g. $.route.attr('...')) will
+ // instead act on the $.route.data Observe.
+ each(['bind','unbind','delegate','undelegate','attr','attrs','serialize','removeAttr'], function(i, name){
+ $.route[name] = function(){
+ return $.route.data[name].apply($.route.data, arguments)
+ }
+ })
+
+ var // A throttled function called multiple times will only fire once the
+ // timer runs down. Each call resets the timer.
+ throttle = function( func ) {
+ var timer;
+ return function() {
+ var args = arguments,
+ self = this;
+ clearTimeout(timer);
+ timer = setTimeout(function(){
+ func.apply(self, args)
+ }, 1);
+ }
+ },
+ // Intermediate storage for $.route.data.
+ curParams,
+ // Deparameterizes the portion of the hash of interest and assign the
+ // values to the $.route.data removing existing values no longer in the hash.
+ setState = function() {
+ var hash = location.hash.substr(1, 1) === '!' ?
+ location.hash.slice(2) :
+ location.hash.slice(1); // everything after #!
+ curParams = $.route.deparam( hash );
+ $.route.attrs(curParams, true);
+ };
+
+ // If the hash changes, update the $.route.data
+ $(window).bind('hashchange', setState);
+
+ // If the $.route.data changes, update the hash.
+ // Using .serialize() retrieves the raw data contained in the observable.
+ // This function is throttled so it only updates once even if multiple values changed.
+ $.route.bind("change", throttle(function() {
+ location.hash = "#!" + $.route.param($.route.serialize())
+ }));
+})
\ No newline at end of file
From 8e3987e0fe7941d530c83a207f7c22e1ac475083 Mon Sep 17 00:00:00 2001
From: Frederick Polgardy
Date: Wed, 8 Feb 2012 08:10:57 -0600
Subject: [PATCH 04/26] Fixed $.Class function to return class definition (from
@fmarsoni)
---
class/class.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/class/class.js b/class/class.js
index 18854c74..8df6f95d 100644
--- a/class/class.js
+++ b/class/class.js
@@ -349,7 +349,7 @@ steal("jquery","jquery/lang/string",function( $ ) {
clss = $.Class = function() {
if (arguments.length) {
- clss.extend.apply(clss, arguments);
+ return clss.extend.apply(clss, arguments);
}
};
From 5b30e30f6a47a7f6e219b5afc3bf4d2b3f6f83dc Mon Sep 17 00:00:00 2001
From: TQ White II/qbook
Date: Wed, 15 Feb 2012 10:33:48 -0600
Subject: [PATCH 05/26] form_params() was incorrectly returning scalar form
values as arrays, now it only returns array for checkbox
---
dom/form_params/form_params.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dom/form_params/form_params.js b/dom/form_params/form_params.js
index 69f56ac2..bbf78325 100644
--- a/dom/form_params/form_params.js
+++ b/dom/form_params/form_params.js
@@ -145,7 +145,7 @@ steal("jquery/dom").then(function( $ ) {
//now we are on the last part, set the value
if (current[lastPart]) {
- if (!$.isArray(current[lastPart]) ) {
+ if (!$.isArray(current[lastPart] && type === "checkbox") ) {
current[lastPart] = current[lastPart] === undefined ? [] : [current[lastPart]];
}
if ( write ) {
From 766abeb9f60abef2b9b09c5e740b3d84b06250b4 Mon Sep 17 00:00:00 2001
From: Ralph Holzmann
Date: Wed, 15 Feb 2012 13:19:00 -0600
Subject: [PATCH 06/26] Fixed bug where last checkbox of checkboxes with
multiple names would return a scalar instead of an array. Tests attached,
100% passing.
---
dom/form_params/form_params.js | 137 +++++++++++++++-------------
dom/form_params/form_params_test.js | 7 +-
dom/form_params/test/basics.micro | 10 ++
3 files changed, 92 insertions(+), 62 deletions(-)
diff --git a/dom/form_params/form_params.js b/dom/form_params/form_params.js
index bbf78325..7e7a66cd 100644
--- a/dom/form_params/form_params.js
+++ b/dom/form_params/form_params.js
@@ -2,22 +2,58 @@
* @add jQuery.fn
*/
steal("jquery/dom").then(function( $ ) {
- var radioCheck = /radio|checkbox/i,
- keyBreaker = /[^\[\]]+/g,
- numberMatcher = /^[\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?$/;
+ var keyBreaker = /[^\[\]]+/g,
+ convertValue = function( value ) {
+ if ( $.isNumeric( value )) {
+ return parseFloat( value );
+ } else if ( value === 'true') {
+ return true;
+ } else if ( value === 'false' ) {
+ return false;
+ } else if ( value === '' ) {
+ return undefined;
+ }
+ return value;
+ },
+ nestData = function( elem, type, data, parts, value, seen ) {
+ var name = parts.shift();
+
+ if ( parts.length ) {
+ if ( ! data[ name ] ) {
+ data[ name ] = {};
+ }
+ // Recursive call
+ nestData( elem, type, data[ name ], parts, value, seen );
+ } else {
+
+ // Handle same name case, as well as "last checkbox checked"
+ // case
+ if ( name in seen && type != "radio" && ! $.isArray( data[ name ] )) {
+ if ( name in data ) {
+ data[ name ] = [ data[name] ];
+ } else {
+ data[ name ] = [];
+ }
+ } else {
+ seen[ name ] = true;
+ }
- var isNumber = function( value ) {
- if ( typeof value == 'number' ) {
- return true;
- }
+ // Finally, assign data
+ if ( ( type == "radio" || type == "checkbox" ) && ! elem.is(":checked") ) {
+ return
+ }
- if ( typeof value != 'string' ) {
- return false;
- }
+ if ( ! data[ name ] ) {
+ data[ name ] = value;
+ } else {
+ data[ name ].push( value );
+ }
+
- return value.match(numberMatcher);
- };
+ }
+ };
+
$.fn.extend({
/**
* @parent dom
@@ -53,7 +89,9 @@ steal("jquery/dom").then(function( $ ) {
* to the result. Defaults to false.
* @return {Object} An object of name-value pairs.
*/
- formParams: function( params, convert ) {
+ formParams: function( params ) {
+
+ var convert;
// Quick way to determine if something is a boolean
if ( !! params === params ) {
@@ -63,10 +101,9 @@ steal("jquery/dom").then(function( $ ) {
if ( params ) {
return this.setParams( params );
- } else if ( this[0].nodeName.toLowerCase() == 'form' && this[0].elements ) {
- return jQuery(jQuery.makeArray(this[0].elements)).getParams(convert);
+ } else if ( this.is("form") ) {
+ return this.getParams( convert );
}
- return jQuery("input[name], textarea[name], select[name]", this[0]).getParams(convert);
},
setParams: function( params ) {
@@ -100,63 +137,41 @@ steal("jquery/dom").then(function( $ ) {
},
getParams: function( convert ) {
var data = {},
+ // This is used to keep track of the checkbox names that we've
+ // already seen, so we know that we should return an array if
+ // we see it multiple times. Fixes last checkbox checked bug.
+ seen = {},
current;
- convert = convert === undefined ? false : convert;
- this.each(function() {
- var el = this,
- type = el.type && el.type.toLowerCase();
- //if we are submit, ignore
- if ((type == 'submit') || !el.name ) {
+ this.find("[name]").each(function() {
+ var $this = $(this),
+ type = $this.attr("type"),
+ name = $this.attr("name"),
+ value = $this.val(),
+ parts;
+
+ // Don't accumulate submit buttons and nameless elements
+ if ( type == "submit" || ! name ) {
return;
}
- var key = el.name,
- value = $.data(el, "value") || $.fn.val.call([el]),
- isRadioCheck = radioCheck.test(el.type),
- parts = key.match(keyBreaker),
- write = !isRadioCheck || !! el.checked,
- //make an array of values
- lastPart;
-
- if ( convert ) {
- if ( isNumber(value) ) {
- value = parseFloat(value);
- } else if ( value === 'true') {
- value = true;
- } else if ( value === 'false' ) {
- value = false;
- }
- if(value === '') {
- value = undefined;
- }
+ // Figure out name parts
+ parts = name.match( keyBreaker );
+ if ( ! parts.length ) {
+ parts = [name];
}
- // go through and create nested objects
- current = data;
- for ( var i = 0; i < parts.length - 1; i++ ) {
- if (!current[parts[i]] ) {
- current[parts[i]] = {};
- }
- current = current[parts[i]];
+ // Convert the value
+ if ( convert ) {
+ value = convertValue( value );
}
- lastPart = parts[parts.length - 1];
-
- //now we are on the last part, set the value
- if (current[lastPart]) {
- if (!$.isArray(current[lastPart] && type === "checkbox") ) {
- current[lastPart] = current[lastPart] === undefined ? [] : [current[lastPart]];
- }
- if ( write ) {
- current[lastPart].push(value);
- }
- } else if ( write || !current[lastPart] ) {
- current[lastPart] = write ? value : undefined;
- }
+ // Assign data recursively
+ nestData( $this, type, data, parts, value, seen );
});
+
return data;
}
});
diff --git a/dom/form_params/form_params_test.js b/dom/form_params/form_params_test.js
index 93d50fd1..ff158c11 100644
--- a/dom/form_params/form_params_test.js
+++ b/dom/form_params/form_params_test.js
@@ -10,6 +10,7 @@ module("jquery/dom/form_params")
test("with a form", function(){
$("#qunit-test-area").html("//jquery/dom/form_params/test/basics.micro",{})
+
var formParams = $("#qunit-test-area form").formParams() ;
ok(formParams.params.one === "1","one is right");
@@ -17,9 +18,13 @@ test("with a form", function(){
ok(formParams.params.three === "3","three is right");
same(formParams.params.four,["4","1"],"four is right");
same(formParams.params.five,["2","3"],"five is right");
-
equal(typeof formParams.id , 'string', "Id value is empty");
+ equal( typeof formParams.singleRadio, "string", "Type of single named radio is string" );
+ equal( formParams.singleRadio, "2", "Value of single named radio is right" );
+
+ ok( $.isArray(formParams.lastOneChecked), "Type of checkbox with last option checked is array" );
+ equal( formParams.lastOneChecked, "4", "Value of checkbox with the last option checked is 4" );
});
diff --git a/dom/form_params/test/basics.micro b/dom/form_params/test/basics.micro
index dfc247ff..14e76957 100644
--- a/dom/form_params/test/basics.micro
+++ b/dom/form_params/test/basics.micro
@@ -11,6 +11,10 @@
+
+
+
+