diff --git a/README b/README index 12846a17..f5535b09 100644 --- a/README +++ b/README @@ -10,15 +10,16 @@ A. How to get (and contribute) JMVC http://github.com/jupiterjs/steal and http://github.com/jupiterjs/jquerymx - 3. Add steal and javascriptmvc as submodules of your project... + 3. Add steal and jquerymx as submodules of your project... git submodule add git@github.com:_YOU_/steal.git steal git submodule add git@github.com:_YOU_/jquerymx.git jquery - * Notice javascriptmvc is under the jquery folder + * Notice jquerymx is under the jquery folder 4. Learn a little more about submodules ... http://johnleach.co.uk/words/archives/2008/10/12/323/git-submodules-in-n-easy-steps - 5. Make changes in steal or jmvc, and push them back to your fork. + 5. Make changes in steal or jquerymx, and push them back to your fork. 6. Make a pull request to your fork. + diff --git a/build.js b/build.js index 80e4bd62..1d840746 100644 --- a/build.js +++ b/build.js @@ -40,14 +40,6 @@ var i, fileName, cmd, "dom/within", "dom/cur_styles", "model", - { - plugin: "model/associations", - exclude: ["jquery/class/class.js", - "jquery/lang/lang.js", - "jquery/event/destroyed/destroyed.js", - "jquery/lang/openajax/openajax.js", - "jquery/model/model.js"] - }, { plugin: "model/backup", exclude: ["jquery/class/class.js", @@ -98,6 +90,7 @@ var i, fileName, cmd, ] +steal.File('jquery/dist').mkdir(); steal('steal/build/pluginify').then( function(s){ var plugin, exclude, fileDest, fileName; for(i=0; i< stl.dependencies.length; d++) { + var depend = stl.dependencies[d]; + if (depend.options.rootSrc !== "jquery/jquery.js") { + dependencies.push(depend.options.rootSrc); + } + } + } + }) + + s.File("jquery/dist/standalone").mkdirs(); + s.File("jquery/dist/standalone/dependencies.json").save($.toJSON(files)); + //get each file ... + print("Creating jquery/dist/standalone/") + var compressor = s.build.builders.scripts.compressors[ "localClosure"]() + for(var path in files){ + if(path == "jquery/jquery.js"){ + continue; + } + var content = readFile(path); + var funcContent = s.build.pluginify.getFunction(content); + if(typeof funcContent == "undefined"){ + content = ""; + } else { + content = "("+s.build.pluginify.getFunction(content)+")(jQuery);"; + } + var out = path.replace(/\/\w+\.js/,"").replace(/\//g,"."); + content = s.build.builders.scripts.clean(content); + print(" "+out+""); + content = s.build.builders.scripts.clean(content); + s.File("jquery/dist/standalone/"+out+".js").save(content); + s.File("jquery/dist/standalone/"+out+".min.js").save(compressor(content)); + } + + }) + + /* var pageSteal = steal.build.open("steal/rhino/empty.html").steal, steals = pageSteal.total, - //hash of names to steals + files = {}, depends = function(stl, steals){ if(stl.dependencies){ @@ -75,31 +122,9 @@ steal('steal/build/pluginify','steal/build/apps','steal/build/scripts').then( fu } } } - }) + })*/ + - steal.File("jquery/dist/standalone").mkdir(); - steal.File("jquery/dist/standalone/dependencies.json").save($.toJSON(files)); - //get each file ... - print("Creating jquery/dist/standalone/") - var compressor = steal.build.builders.scripts.compressors[ "localClosure"]() - for(var path in files){ - if(path == "jquery/jquery.js"){ - continue; - } - var content = readFile(path); - var funcContent = s.build.pluginify.getFunction(content); - if(typeof funcContent == "undefined"){ - content = ""; - } else { - content = "("+s.build.pluginify.getFunction(content)+")(jQuery);"; - } - var out = path.replace(/\/\w+\.js/,"").replace(/\//g,"."); - content = steal.build.builders.scripts.clean(content); - print(" "+out+""); - content = steal.build.builders.scripts.clean(content); - s.File("jquery/dist/standalone/"+out+".js").save(content); - s.File("jquery/dist/standalone/"+out+".min.js").save(compressor(content)); - } }) \ No newline at end of file diff --git a/class/class.html b/class/class.html index a7027986..ef16143d 100644 --- a/class/class.html +++ b/class/class.html @@ -62,8 +62,8 @@

History Tabs

+ + + \ No newline at end of file diff --git a/controller/pages/listening.md b/controller/pages/listening.md index 684c1c46..c27ab175 100644 --- a/controller/pages/listening.md +++ b/controller/pages/listening.md @@ -41,7 +41,7 @@ But to correct for this, you just need to add the function to the [jQuery.Controller.static.listensTo listensTo] property. Here's how: - $.Controller.extend("MyShow",{ + $.Controller("MyShow",{ listensTo: ["show"] },{ show: function( el, ev ) { diff --git a/controller/route/qunit.html b/controller/route/qunit.html new file mode 100644 index 00000000..25edc491 --- /dev/null +++ b/controller/route/qunit.html @@ -0,0 +1,18 @@ + + + + + route QUnit Test + + + + +

route Test Suite

+

+
+

+
+
    +
    + + \ No newline at end of file diff --git a/controller/route/route.html b/controller/route/route.html new file mode 100644 index 00000000..178af929 --- /dev/null +++ b/controller/route/route.html @@ -0,0 +1,35 @@ + + + + route + + + +

    route Demo

    + foo/bar + foo/car + empty + + + + \ No newline at end of file diff --git a/controller/route/route.js b/controller/route/route.js new file mode 100644 index 00000000..bc0f1c67 --- /dev/null +++ b/controller/route/route.js @@ -0,0 +1,31 @@ +steal('jquery/dom/route','jquery/controller', function(){ + /** + * + * ":type route" // + * + * @param {Object} el + * @param {Object} event + * @param {Object} selector + * @param {Object} cb + */ + jQuery.Controller.processors.route = function(el, event, selector, funcName, controller){ + $.route(selector||"") + var batchNum; + var check = function(ev, attr, how){ + if($.route.attr('route') === (selector||"") && + (ev.batchNum === undefined || ev.batchNum !== batchNum ) ){ + + batchNum = ev.batchNum; + + var d = $.route.attrs(); + delete d.route; + + controller[funcName](d) + } + } + $.route.bind('change',check); + return function(){ + $.route.unbind('change',check) + } + } +}) diff --git a/controller/route/route_test.js b/controller/route/route_test.js new file mode 100644 index 00000000..6a140d6b --- /dev/null +++ b/controller/route/route_test.js @@ -0,0 +1,10 @@ +steal('funcunit/qunit','./route',function(){ + +module("route"); + +test("route testing works", function(){ + ok(true,"an assert is run"); +}); + + +}); \ No newline at end of file diff --git a/controller/subscribe/subscribe.html b/controller/subscribe/subscribe.html index ebabb0c5..a26d72d5 100644 --- a/controller/subscribe/subscribe.html +++ b/controller/subscribe/subscribe.html @@ -32,7 +32,7 @@

    Turn OFF Above

    \ No newline at end of file diff --git a/dom/route/route.js b/dom/route/route.js index 8d646b07..4159adec 100644 --- a/dom/route/route.js +++ b/dom/route/route.js @@ -1,4 +1,4 @@ -steal('jquery/lang/observe', 'jquery/event/hashchange', 'jquery/lang/string/deparam', 'jquery/lang/observe/delegate', +steal('jquery/lang/observe', 'jquery/event/hashchange', 'jquery/lang/string/deparam', function( $ ) { // Helper methods used for matching routes. @@ -13,13 +13,12 @@ function( $ ) { makeProps = function( props ) { var html = [], name, val; - for ( name in props ) { - val = props[name] + 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. @@ -42,7 +41,11 @@ function( $ ) { }, // onready = true, - location = window.location; + location = window.location, + encode = encodeURIComponent, + decode = decodeURIComponent, + each = $.each, + extend = $.extend; /** * @class jQuery.route @@ -139,7 +142,7 @@ function( $ ) { * Or change multiple properties at once with * [jQuery.Observe.prototype.attrs attrs]: * - * $.route.attrs({type: 'pages', id: 5}, true) + * $.route.attr({type: 'pages', id: 5}, true) * * When you make changes to $.route, they will automatically * change the hash. @@ -204,20 +207,21 @@ function( $ ) { * @param {Object} [defaults] an object of default values * @return {jQuery.route} */ - var $route = $.route = function( url, defaults ) { + $.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) - return "([\\w\\.]*)" // The '\\' is for string-escaping giving single '\' for regEx escaping + // 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] = { + $.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), + 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 @@ -227,10 +231,10 @@ function( $ ) { // The number of parts in the URL separated by '/'. length: url.split('/').length } - return $route; + return $.route; }; - $.extend($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 @@ -244,33 +248,42 @@ function( $ ) { // Check if the provided data keys match the names in any routes; // get the one with the most matches. var route, - matches = -1, - temp, - matchCount; - for ( var name in $route.routes ) { - temp = $route.routes[name], - matchCount = matchesData(temp, data); - if ( matchCount > matches ) { - route = temp; - matches = matchCount - } + // 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), + 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] ? "" : data[name]; + return data[name] === route.defaults[name] ? "" : encode( data[name] ); }), after; - // remove matching default values - for(name in route.defaults) { - if(cpy[name] === route.defaults[name]) { + 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. @@ -291,35 +304,38 @@ function( $ ) { var route = { length: -1 }; - for ( var name in $route.routes ) { - var temp = $route.routes[name] + 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 mathced string; parts contain the variable values. + // 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), + 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); + obj = extend(true, {}, route.defaults, obj); // Overwrite each of the default values in obj with those in parts if that part is not empty. - for ( var p = 0; p < parts.length; p++ ) { - if ( parts[p] ) { - obj[route.names[p]] = parts[p] + 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) ) : {}; }, /** @@ -330,22 +346,36 @@ function( $ ) { /** * @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). + * + * - 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 '/'. + * + * - 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 + * @return $.route */ ready: function(val) { if( val === false ) { @@ -354,7 +384,7 @@ function( $ ) { if( val === true || onready === true ) { setState(); } - return $route; + return $.route; }, /** * Returns a url from the options @@ -364,9 +394,9 @@ function( $ ) { */ url: function( options, merge ) { if (merge) { - return "#!" + $route.param($.extend({}, curParams, options)) + return "#!" + $.route.param(extend({}, curParams, options)) } else { - return "#!" + $route.param(options) + return "#!" + $.route.param(options) } }, /** @@ -378,29 +408,18 @@ function( $ ) { */ link: function( name, options, props, merge ) { return "" + name + ""; }, /** - * Returns if the options represent the current page. + * Returns true if the options represent the current page. * @param {Object} options + * @return {Boolean} */ current: function( options ) { - return location.hash == "#!" + $route.param(options) - }, - /** - * Change the current page using either a data object or a url string. - * @param {Object|String} loc The object with attributes or hash string. - * @param {Boolean} remove true to remove properties not in loc, only if loc === Object, default true - */ - set: function(loc, remove) { - if (typeof loc == "string") { - location.hash = "#!" + loc; - } else if ($.isPlainObject( loc )) { - $route.attrs( loc, (typeof remove == "undefined") ? true : remove ); - } - } + return location.hash == "#!" + $.route.param(options) + } }); // onready $(function() { @@ -409,19 +428,23 @@ function( $ ) { // 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) + 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, time ) { + throttle = function( func ) { var timer; return function() { + var args = arguments, + self = this; clearTimeout(timer); - timer = setTimeout(func, time || 1); + timer = setTimeout(function(){ + func.apply(self, args) + }, 1); } }, // Intermediate storage for $.route.data. @@ -429,17 +452,11 @@ function( $ ) { // 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() { - // commented out code handles people setting attrs before onready - //if( $.isEmptyObject( $route.data.serialize() ) ) { - var hash = location.hash[1] === '!' ? - location.hash.slice(2) : - location.hash.slice(1); // everything after #! - curParams = $route.deparam( hash ); - $route.attrs(curParams, true); - //} else { - // window.location.hash = "#!" + $route.param($route.data.serialize()) - //} - + 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 @@ -448,7 +465,7 @@ function( $ ) { // 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.data.bind("change", throttle(function() { - window.location.hash = "#!" + $route.param($route.data.serialize()) + $.route.bind("change", throttle(function() { + location.hash = "#!" + $.route.param($.route.serialize()) })); }) \ No newline at end of file diff --git a/dom/route/route_test.js b/dom/route/route_test.js index db048125..f487da94 100644 --- a/dom/route/route_test.js +++ b/dom/route/route_test.js @@ -10,18 +10,21 @@ test("deparam", function(){ var obj = $.route.deparam("jQuery.Controller"); same(obj, { - page : "jQuery.Controller" + page : "jQuery.Controller", + route: ":page" }); obj = $.route.deparam(""); same(obj, { - page : "index" + page : "index", + route: ":page" }); obj = $.route.deparam("jQuery.Controller&where=there"); same(obj, { page : "jQuery.Controller", - where: "there" + where: "there", + route: ":page" }); $.route.routes = {}; @@ -34,7 +37,8 @@ test("deparam", function(){ same(obj, { page : "jQuery.Controller", index: "foo", - where: "there" + where: "there", + route: ":page/:index" }); }) @@ -57,10 +61,24 @@ test("deparam of invalid url", function(){ same(obj, { var1: 'val1', var2: 'val2', - var3: 'val3' + var3: 'val3', + route: "pages/:var1/:var2/:var3" }); }) +test("deparam of url with non-generated hash (manual override)", function(){ + $.route.routes = {}; + + // This won't be set like this by route, but it could easily happen via a + // user manually changing the URL or when porting a prior URL structure. + obj = $.route.deparam("page=foo&bar=baz&where=there"); + same(obj, { + page: 'foo', + bar: 'baz', + where: 'there' + }); +}) + test("param", function(){ $.route.routes = {}; $.route("pages/:page",{ @@ -144,19 +162,31 @@ test("param-deparam", function(){ type: "foo" }) - var data = {page: "jQuery.Controller", type: "document", bar: "baz", where: "there"}; + var data = {page: "jQuery.Controller", + type: "document", + bar: "baz", + where: "there"}; var res = $.route.param(data); var obj = $.route.deparam(res); - same(data, obj) - + delete obj.route + same(obj,data ) + return; data = {page: "jQuery.Controller", type: "foo", bar: "baz", where: "there"}; res = $.route.param(data); obj = $.route.deparam(res); + delete obj.route; same(data, obj) + + data = {page: " a ", type: " / "}; + res = $.route.param(data); + obj = $.route.deparam(res); + delete obj.route; + same(obj ,data ,"slashes and spaces") data = {page: "index", type: "foo", bar: "baz", where: "there"}; res = $.route.param(data); obj = $.route.deparam(res); + delete obj.route; same(data, obj) $.route.routes = {}; @@ -174,12 +204,14 @@ test("precident", function(){ var obj = $.route.deparam("jQuery.Controller"); same(obj, { - who : "jQuery.Controller" + who : "jQuery.Controller", + route: ":who" }); obj = $.route.deparam("search/jQuery.Controller"); same(obj, { - search : "jQuery.Controller" + search : "jQuery.Controller", + route: "search/:search" },"bad deparam"); equal( $.route.param({ @@ -212,4 +244,24 @@ test("linkTo", function(){ equal( res, 'Hello'); }) +test("param with route defined", function(){ + $.route.routes = {}; + $.route("holler") + $.route("foo"); + + var res = $.route.param({foo: "abc",route: "foo"}); + + equal(res, "foo&foo=abc") +}) + +test("route endings", function(){ + $.route.routes = {}; + $.route("foo",{foo: true}); + $.route("food",{food: true}) + + var res = $.route.deparam("food") + ok(res.food, "we get food back") + +}) + }) diff --git a/dom/selection/selection.js b/dom/selection/selection.js index 40d78ce6..45cc2280 100644 --- a/dom/selection/selection.js +++ b/dom/selection/selection.js @@ -195,7 +195,7 @@ getCharElement = function( elems , range, len ) { * returns an object with: * * - __start__ - The number of characters from the start of the element to the start of the selection. - * - __end__ - The number of characters from teh start of the element to the end of the selection. + * - __end__ - The number of characters from the start of the element to the end of the selection. * - __range__ - A [jQuery.Range $.Range] that represents the current selection. * * This lets you get the selected text in a textarea like: diff --git a/dom/selection/selection_test.js b/dom/selection/selection_test.js index 2a7e5131..28886444 100644 --- a/dom/selection/selection_test.js +++ b/dom/selection/selection_test.js @@ -12,7 +12,7 @@ test("getCharElement", function(){ setTimeout(function(){ var types = ['textarea','#inp','#1','#2']; for(var i =0; i< types.length; i++){ - console.log(types[i]) + //console.log(types[i]) $(types[i]).selection(1,5); } /* diff --git a/dom/within/within.js b/dom/within/within.js index ca0f4401..818a18e0 100644 --- a/dom/within/within.js +++ b/dom/within/within.js @@ -65,7 +65,11 @@ $.fn.withinBox = function(left, top, width, height, cache){ if(this == document.documentElement) return this.ret.push(this); - var offset = cache ? jQuery.data(this,"offset", q.offset()) : q.offset(); + var offset = cache ? + jQuery.data(this,"offset") || + jQuery.data(this,"offset", q.offset()) : + q.offset(); + var ew = q.width(), eh = q.height(); diff --git a/download/download.html b/download/download.html index 83f69ac2..98535d6c 100644 --- a/download/download.html +++ b/download/download.html @@ -45,26 +45,6 @@

    Controller

    Organize event handlers using event delegation
    -
    - - - -
    Add page history support to Controller
    -
    - -
    - - - -
    Add pub/sub support to Controller
    -
    - -
    - - - -
    Helpers that tie view templates to a controller instance
    -
    @@ -82,12 +62,6 @@

    Model

    A basic skeleton to organize pieces of your application's data layer
    -
    - - - -
    Get data for related records
    -
    @@ -104,13 +78,6 @@

    Model

    -
    - - - -
    A storeable list of model instances
    -
    -