ajax 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +291 -0
  3. data/Rakefile +49 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/ajax_controller.rb +3 -0
  6. data/app/views/ajax/framework.html.erb +7 -0
  7. data/config/initializers/ajax.rb +14 -0
  8. data/lib/ajax.rb +79 -0
  9. data/lib/ajax/action_controller.rb +154 -0
  10. data/lib/ajax/action_view.rb +56 -0
  11. data/lib/ajax/helpers.rb +15 -0
  12. data/lib/ajax/helpers/request_helper.rb +76 -0
  13. data/lib/ajax/helpers/robot_helper.rb +31 -0
  14. data/lib/ajax/helpers/url_helper.rb +47 -0
  15. data/lib/ajax/railtie.rb +7 -0
  16. data/lib/ajax/routes.rb +12 -0
  17. data/lib/ajax/spec/extension.rb +34 -0
  18. data/lib/ajax/spec/helpers.rb +95 -0
  19. data/lib/ajax/tasks.rb +1 -0
  20. data/lib/rack-ajax.rb +60 -0
  21. data/lib/rack-ajax/decision_tree.rb +60 -0
  22. data/lib/rack-ajax/parser.rb +115 -0
  23. data/public/images/loading-icon-large.gif +0 -0
  24. data/public/images/loading-icon-small.gif +0 -0
  25. data/public/javascripts/ajax.js +529 -0
  26. data/public/javascripts/jquery.address-1.1.js +450 -0
  27. data/public/javascripts/jquery.address-1.1.min.js +11 -0
  28. data/public/javascripts/jquery.address-1.2.js +528 -0
  29. data/public/javascripts/jquery.address-1.2.min.js +25 -0
  30. data/public/javascripts/jquery.address-1.2rc.js +599 -0
  31. data/public/javascripts/jquery.address-1.2rc.min.js +27 -0
  32. data/public/javascripts/jquery.json-2.2.js +178 -0
  33. data/public/javascripts/jquery.json-2.2.min.js +31 -0
  34. data/rails/init.rb +4 -0
  35. data/rails/install.rb +23 -0
  36. data/rails/uninstall.rb +1 -0
  37. data/spec/ajax/helpers_spec.rb +102 -0
  38. data/spec/ajax/request_helper_spec.rb +33 -0
  39. data/spec/integration/ajax_spec.rb +146 -0
  40. data/spec/rack-ajax/parser_spec.rb +62 -0
  41. data/spec/spec.opts +1 -0
  42. data/spec/spec_helper.rb +18 -0
  43. data/tasks/ajax_tasks.rake +15 -0
  44. metadata +106 -0
@@ -0,0 +1,27 @@
1
+ /*
2
+ * jQuery Address Plugin v1.2rc
3
+ * http://www.asual.com/jquery/address/
4
+ *
5
+ * Copyright (c) 2009-2010 Rostislav Hristov
6
+ * Dual licensed under the MIT or GPL Version 2 licenses.
7
+ * http://jquery.org/license
8
+ *
9
+ * Date: 2010-03-31 11:35:36 +0300 (Wed, 31 Mar 2010)
10
+ */
11
+ (function(c){c.address=function(){var v=function(a){c(c.address).trigger(c.extend(c.Event(a),function(){for(var b={},g=c.address.parameterNames(),h=0,q=g.length;h<q;h++)b[g[h]]=c.address.parameter(g[h]);return{value:c.address.value(),path:c.address.path(),pathNames:c.address.pathNames(),parameterNames:g,parameters:b,queryString:c.address.queryString()}}.call(c.address)))},y=function(a,b,g){if(g||b)c(c.address).bind(a,g||b,g&&b);return c.address},n=function(){var a=d.href.indexOf("#");return a!=-1?
12
+ W(J(t(d.href.substr(a+1),j))):""},X=function(){return"javascript"},Y=function(a,b){if(i.strict)a=b?a.substr(0,1)!="/"?"/"+a:a:a==""?"/":a;return a},K=function(a,b){return u&&d.protocol=="file:"?b?f.replace(/\?/,"%3F"):f.replace(/%253F/,"?"):a},t=function(a,b){if(i.crawlable&&b)return(a!=""?"!":"")+a;return a.replace(/^\!/,"")},w=function(a,b){return parseInt(a.css(b),10)},Z=function(a){for(var b,g,h=0,q=a.childNodes.length;h<q;h++){if(a.childNodes[h].src)b=String(a.childNodes[h].src);if(g=Z(a.childNodes[h]))b=
13
+ g}return b},H=function(){if(!L){var a=n(),b=f!=a;if(x&&m<523){if(D!=F.length){D=F.length;if(typeof z[D-1]!=A)f=z[D-1];G(j)}}else if(u&&m<7&&b)d.reload();else if(b){f=a;G(j)}}},G=function(a){v($);v(a?aa:ba);E(ca,10)},ca=function(){var a=(d.pathname+(/\/$/.test(d.pathname)?"":"/")+c.address.value()).replace(/\/\//,"/").replace(/^\/$/,""),b=window[i.tracker];if(typeof b==M)b(a);else if(typeof _gaq!=A&&typeof _gaq.push==M)_gaq.push(["_trackPageview",a]);else if(typeof pageTracker!=A&&typeof pageTracker._trackPageview==
14
+ M)pageTracker._trackPageview(a);else typeof urchinTracker==M&&urchinTracker(a)},da=function(){var a=l.contentWindow.document;a.open();a.write("<html><head><title>"+k.title+"</title><script>var "+o+' = "'+n()+'";<\/script></head></html>');a.close()},fa=function(){if(!ea){ea=e;if(i.wrap){var a=c("body");wrap=c("body > *").wrapAll('<div style="padding:'+(w(a,"marginTop")+w(a,"paddingTop"))+"px "+(w(a,"marginRight")+w(a,"paddingRight"))+"px "+(w(a,"marginBottom")+w(a,"paddingBottom"))+"px "+(w(a,"marginLeft")+
15
+ w(a,"paddingLeft"))+'px;" />').parent().wrap('<div id="'+o+'" style="height:100%; overflow:auto;'+(x?window.statusbar.visible&&!/chrome/i.test(O)?"":" resize:both;":"")+'" />');c("html, body").css({height:"100%",margin:0,padding:0,overflow:"hidden"});x&&c('<style type="text/css" />').appendTo("head").text("#"+o+"::-webkit-resizer { background-color: #fff; }")}if(u&&m<8){a=k.getElementsByTagName("frameset")[0];l=k.createElement((a?"":"i")+"frame");if(a){a.insertAdjacentElement("beforeEnd",l);a[a.cols?
16
+ "cols":"rows"]+=",0";l.src=X()+":"+j;l.noResize=e;l.frameBorder=l.frameSpacing=0}else{l.src=X()+":"+j;l.style.display="none";k.body.insertAdjacentElement("afterBegin",l)}E(function(){c(l).bind("load",function(){var b=l.contentWindow;f=typeof b[o]!=A?b[o]:"";if(f!=n()){G(j);d.hash=K(t(f,e),e)}});typeof l.contentWindow[o]==A&&da()},50)}else if(x){if(m<418){c(k.body).append('<form id="'+o+'" style="position:absolute;top:-9999px;" method="get"></form>');P=k.getElementById(o)}if(typeof d[o]==A)d[o]={};
17
+ if(typeof d[o][d.pathname]!=A)z=d[o][d.pathname].split(",")}E(function(){v("init");G(j)},1);if(u&&m>7||!u&&"on"+I in p)if(p.addEventListener)p.addEventListener(I,H,false);else p.attachEvent&&p.attachEvent("on"+I,H);else ja(H,50);c("a[rel*=address:]").address()}},ka=function(){if(p.removeEventListener)p.removeEventListener(I,H,false);else p.detachEvent&&p.detachEvent("on"+I,H)},o="jQueryAddress",M="function",A="undefined",I="hashchange",$="change",aa="internalChange",ba="externalChange",e=true,j=false,
18
+ i={autoUpdate:e,crawlable:j,history:e,strict:e,wrap:j},r=c.browser,m=parseFloat(c.browser.version),ga=r.mozilla,u=r.msie,s=r.opera,x=r.safari,Q=j,p=function(){try{return top.document!==undefined?top:window}catch(a){return window}}(),k=p.document,F=p.history,d=p.location,ja=setInterval,E=setTimeout,J=decodeURI,W=encodeURI,O=navigator.userAgent,l,P,B=Z(document),ha=B?B.indexOf("?"):-1,R=k.title,D=F.length,L=j,ea=j,S=e,ia=e,N=j,z=[],f=n();if(u){m=parseFloat(O.substr(O.indexOf("MSIE")+4));if(k.documentMode&&
19
+ k.documentMode!=m)m=k.documentMode!=8?7:8;c(document).bind("propertychange",function(){if(k.title!=R&&k.title.indexOf("#"+n())!=-1)k.title=R})}if(Q=ga&&m>=1||u&&m>=6||s&&m>=9.5||x&&m>=312){for(r=1;r<D;r++)z.push("");z.push(f);if(u&&d.hash!=f)d.hash="#"+K(t(f,e),e);if(s)history.navigationMode="compatible";if(B&&ha!=-1){B=B.substr(ha+1).split("&");for(r=0;r<B.length;r++){s=B[r].split("=");if(/^(autoUpdate|crawlable|history|strict|wrap)$/.test(s[0]))i[s[0]]=isNaN(s[1])?/^(true|yes)$/i.test(s[1]):parseInt(s[1],
20
+ 10)!==0;if(/^tracker$/.test(s[0]))i[s[0]]=s[1]}}document.readyState=="complete"&&fa();c(fa);c(window).bind("unload",ka)}else if(!Q&&n()!=""||x&&m<418&&n()!=""&&d.search!=""){k.open();k.write('<html><head><meta http-equiv="refresh" content="0;url='+encodeURI(d.href.substr(0,d.href.indexOf("#")))+'" /></head></html>');k.close()}else ca();return{init:function(a,b){return y("init",a,b)},change:function(a,b){return y($,a,b)},internalChange:function(a,b){return y(aa,a,b)},externalChange:function(a,b){return y(ba,
21
+ a,b)},baseURL:function(){var a=d.href;if(n()!="")a=a.substr(0,a.indexOf("#"));if(/\/$/.test(a))a=a.substr(0,a.length-1);return a},autoUpdate:function(a){if(a!==undefined){i.autoUpdate=a;return this}return i.autoUpdate},crawlable:function(a){if(a!==undefined){i.crawlable=a;return this}return i.crawlable},history:function(a){if(a!==undefined){i.history=a;return this}return i.history},strict:function(a){if(a!==undefined){i.strict=a;return this}return i.strict},tracker:function(a){if(a!==undefined){i.tracker=
22
+ a;return this}return i.tracker},wrap:function(a){if(a!==undefined){i.wrap=a;return this}return i.wrap},update:function(){N=e;this.value(f);N=j;return this},title:function(a){if(a!==undefined){a=J(a);E(function(){R=k.title=a;if(ia&&l&&l.contentWindow&&l.contentWindow.document){l.contentWindow.document.title=a;ia=j}if(!S&&ga)d.replace(d.href.indexOf("#")!=-1?d.href:d.href+"#");S=j},50);return this}return k.title},value:function(a){if(a!==undefined){a=W(J(Y(a,e)));if(a=="/")a="";if(f==a&&!N)return;S=
23
+ e;f=a;if(i.autoUpdate||N){L=e;G(e);z[F.length]=f;if(x)if(i.history){d[o][d.pathname]=z.toString();D=F.length+1;if(m<418){if(d.search==""){P.action="#"+t(f,e);P.submit()}}else if(m<523||f==""){a=k.createEvent("MouseEvents");a.initEvent("click",e,e);var b=k.createElement("a");b.href="#"+t(f,e);b.dispatchEvent(a)}else d.hash="#"+t(f,e)}else d.replace("#"+t(f,e));else if(f!=n())if(i.history)d.hash="#"+K(t(f,e),e);else d.replace("#"+t(f,e));u&&m<8&&i.history&&E(da,50);if(x)E(function(){L=j},1);else L=
24
+ j}return this}if(!Q)return null;return J(Y(K(f,j),j))},path:function(a){if(a!==undefined){var b=this.queryString(),g=this.hash();this.value(a+(b?"?"+b:"")+(g?"#"+g:""));return this}return this.value().split("#")[0].split("?")[0]},queryString:function(a){if(a!==undefined){var b=this.hash();this.value(this.path()+(a?"?"+a:"")+(b?"#"+b:""));return this}a=this.value().split("?");return a.slice(1,a.length).join("?").split("#")[0]},parameter:function(a,b,g){var h,q;if(b!==undefined){var T=this.parameterNames();
25
+ q=[];for(h=0;h<T.length;h++){var U=T[h],C=this.parameter(U);if(typeof C=="string")C=[C];if(U==a)C=b===null||b==""?[]:g?C.concat([b]):[b];for(var V=0;V<C.length;V++)q.push(U+"="+C[V])}c.inArray(a,T)==-1&&q.push(a+"="+b);this.queryString(q.join("&"));return this}if(b=this.queryString()){q=b.split("&");b=[];for(h=0;h<q.length;h++){g=q[h].split("=");g[0]==a&&b.push(g[1])}if(b.length!==0)return b.length!=1?b:b[0]}},pathNames:function(){var a=this.path(),b=a.replace(/\/{2,9}/g,"/").split("/");if(a.substr(0,
26
+ 1)=="/"||a.length===0)b.splice(0,1);a.substr(a.length-1,1)=="/"&&b.splice(b.length-1,1);return b},parameterNames:function(){var a=this.queryString(),b=[];if(a&&a.indexOf("=")!=-1){a=a.split("&");for(var g=0;g<a.length;g++){var h=a[g].split("=")[0];c.inArray(h,b)==-1&&b.push(h)}}return b},hash:function(a){if(a!==undefined){this.value(this.value().split("#")[0]+(a?"#"+a:""));return this}a=this.value().split("#");return a.slice(1,a.length).join("#")}}}();c.fn.address=function(v){this.each(function(){if(this.tagName.toLowerCase()==
27
+ "form")c("form").live("submit",function(){var n=v?v.call(this):c(this).attr("action")+"?"+c(this).serialize();c.address.value(n);return false});else{var y=function(){var n=v?v.call(this):/address:/.test(c(this).attr("rel"))?c(this).attr("rel").split("address:")[1].split(" ")[0]:c(this).attr("href").replace(/^#\!?/,"");c.address.value(n);return false};c(this).click(y).live("click",y)}})}})(jQuery);
@@ -0,0 +1,178 @@
1
+ /*
2
+ * jQuery JSON Plugin
3
+ * version: 2.1 (2009-08-14)
4
+ *
5
+ * This document is licensed as free software under the terms of the
6
+ * MIT License: http://www.opensource.org/licenses/mit-license.php
7
+ *
8
+ * Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
9
+ * website's http://www.json.org/json2.js, which proclaims:
10
+ * "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
11
+ * I uphold.
12
+ *
13
+ * It is also influenced heavily by MochiKit's serializeJSON, which is
14
+ * copyrighted 2005 by Bob Ippolito.
15
+ */
16
+
17
+ (function($) {
18
+ /** jQuery.toJSON( json-serializble )
19
+ Converts the given argument into a JSON respresentation.
20
+
21
+ If an object has a "toJSON" function, that will be used to get the representation.
22
+ Non-integer/string keys are skipped in the object, as are keys that point to a function.
23
+
24
+ json-serializble:
25
+ The *thing* to be converted.
26
+ **/
27
+ $.toJSON = function(o)
28
+ {
29
+ if (typeof(JSON) == 'object' && JSON.stringify)
30
+ return JSON.stringify(o);
31
+
32
+ var type = typeof(o);
33
+
34
+ if (o === null)
35
+ return "null";
36
+
37
+ if (type == "undefined")
38
+ return undefined;
39
+
40
+ if (type == "number" || type == "boolean")
41
+ return o + "";
42
+
43
+ if (type == "string")
44
+ return $.quoteString(o);
45
+
46
+ if (type == 'object')
47
+ {
48
+ if (typeof o.toJSON == "function")
49
+ return $.toJSON( o.toJSON() );
50
+
51
+ if (o.constructor === Date)
52
+ {
53
+ var month = o.getUTCMonth() + 1;
54
+ if (month < 10) month = '0' + month;
55
+
56
+ var day = o.getUTCDate();
57
+ if (day < 10) day = '0' + day;
58
+
59
+ var year = o.getUTCFullYear();
60
+
61
+ var hours = o.getUTCHours();
62
+ if (hours < 10) hours = '0' + hours;
63
+
64
+ var minutes = o.getUTCMinutes();
65
+ if (minutes < 10) minutes = '0' + minutes;
66
+
67
+ var seconds = o.getUTCSeconds();
68
+ if (seconds < 10) seconds = '0' + seconds;
69
+
70
+ var milli = o.getUTCMilliseconds();
71
+ if (milli < 100) milli = '0' + milli;
72
+ if (milli < 10) milli = '0' + milli;
73
+
74
+ return '"' + year + '-' + month + '-' + day + 'T' +
75
+ hours + ':' + minutes + ':' + seconds +
76
+ '.' + milli + 'Z"';
77
+ }
78
+
79
+ if (o.constructor === Array)
80
+ {
81
+ var ret = [];
82
+ for (var i = 0; i < o.length; i++)
83
+ ret.push( $.toJSON(o[i]) || "null" );
84
+
85
+ return "[" + ret.join(",") + "]";
86
+ }
87
+
88
+ var pairs = [];
89
+ for (var k in o) {
90
+ var name;
91
+ var type = typeof k;
92
+
93
+ if (type == "number")
94
+ name = '"' + k + '"';
95
+ else if (type == "string")
96
+ name = $.quoteString(k);
97
+ else
98
+ continue; //skip non-string or number keys
99
+
100
+ if (typeof o[k] == "function")
101
+ continue; //skip pairs where the value is a function.
102
+
103
+ var val = $.toJSON(o[k]);
104
+
105
+ pairs.push(name + ":" + val);
106
+ }
107
+
108
+ return "{" + pairs.join(", ") + "}";
109
+ }
110
+ };
111
+
112
+ /** jQuery.evalJSON(src)
113
+ Evaluates a given piece of json source.
114
+ **/
115
+ $.evalJSON = function(src)
116
+ {
117
+ if (typeof(JSON) == 'object' && JSON.parse)
118
+ return JSON.parse(src);
119
+ return eval("(" + src + ")");
120
+ };
121
+
122
+ /** jQuery.secureEvalJSON(src)
123
+ Evals JSON in a way that is *more* secure.
124
+ **/
125
+ $.secureEvalJSON = function(src)
126
+ {
127
+ if (typeof(JSON) == 'object' && JSON.parse)
128
+ return JSON.parse(src);
129
+
130
+ var filtered = src;
131
+ filtered = filtered.replace(/\\["\\\/bfnrtu]/g, '@');
132
+ filtered = filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
133
+ filtered = filtered.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
134
+
135
+ if (/^[\],:{}\s]*$/.test(filtered))
136
+ return eval("(" + src + ")");
137
+ else
138
+ throw new SyntaxError("Error parsing JSON, source is not valid.");
139
+ };
140
+
141
+ /** jQuery.quoteString(string)
142
+ Returns a string-repr of a string, escaping quotes intelligently.
143
+ Mostly a support function for toJSON.
144
+
145
+ Examples:
146
+ >>> jQuery.quoteString("apple")
147
+ "apple"
148
+
149
+ >>> jQuery.quoteString('"Where are we going?", she asked.')
150
+ "\"Where are we going?\", she asked."
151
+ **/
152
+ $.quoteString = function(string)
153
+ {
154
+ if (string.match(_escapeable))
155
+ {
156
+ return '"' + string.replace(_escapeable, function (a)
157
+ {
158
+ var c = _meta[a];
159
+ if (typeof c === 'string') return c;
160
+ c = a.charCodeAt();
161
+ return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
162
+ }) + '"';
163
+ }
164
+ return '"' + string + '"';
165
+ };
166
+
167
+ var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
168
+
169
+ var _meta = {
170
+ '\b': '\\b',
171
+ '\t': '\\t',
172
+ '\n': '\\n',
173
+ '\f': '\\f',
174
+ '\r': '\\r',
175
+ '"' : '\\"',
176
+ '\\': '\\\\'
177
+ };
178
+ })(jQuery);
@@ -0,0 +1,31 @@
1
+
2
+ (function($){$.toJSON=function(o)
3
+ {if(typeof(JSON)=='object'&&JSON.stringify)
4
+ return JSON.stringify(o);var type=typeof(o);if(o===null)
5
+ return"null";if(type=="undefined")
6
+ return undefined;if(type=="number"||type=="boolean")
7
+ return o+"";if(type=="string")
8
+ return $.quoteString(o);if(type=='object')
9
+ {if(typeof o.toJSON=="function")
10
+ return $.toJSON(o.toJSON());if(o.constructor===Date)
11
+ {var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+
12
+ hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
13
+ if(o.constructor===Array)
14
+ {var ret=[];for(var i=0;i<o.length;i++)
15
+ ret.push($.toJSON(o[i])||"null");return"["+ret.join(",")+"]";}
16
+ var pairs=[];for(var k in o){var name;var type=typeof k;if(type=="number")
17
+ name='"'+k+'"';else if(type=="string")
18
+ name=$.quoteString(k);else
19
+ continue;if(typeof o[k]=="function")
20
+ continue;var val=$.toJSON(o[k]);pairs.push(name+":"+val);}
21
+ return"{"+pairs.join(", ")+"}";}};$.evalJSON=function(src)
22
+ {if(typeof(JSON)=='object'&&JSON.parse)
23
+ return JSON.parse(src);return eval("("+src+")");};$.secureEvalJSON=function(src)
24
+ {if(typeof(JSON)=='object'&&JSON.parse)
25
+ return JSON.parse(src);var filtered=src;filtered=filtered.replace(/\\["\\\/bfnrtu]/g,'@');filtered=filtered.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']');filtered=filtered.replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered))
26
+ return eval("("+src+")");else
27
+ throw new SyntaxError("Error parsing JSON, source is not valid.");};$.quoteString=function(string)
28
+ {if(string.match(_escapeable))
29
+ {return'"'+string.replace(_escapeable,function(a)
30
+ {var c=_meta[a];if(typeof c==='string')return c;c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
31
+ return'"'+string+'"';};var _escapeable=/["\\\x00-\x1f\x7f-\x9f]/g;var _meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};})(jQuery);
data/rails/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'ajax'
2
+ require 'rack-ajax'
3
+
4
+ Ajax.install_for_rails
data/rails/install.rb ADDED
@@ -0,0 +1,23 @@
1
+ AJAX_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
2
+
3
+ %w[
4
+ app/controllers/ajax_controller.rb
5
+ app/views/ajax/framework.html.erb
6
+ config/initializers/ajax.rb
7
+ public/javascripts/ajax.js
8
+ public/javascripts/jquery.address-1.2rc.js
9
+ public/javascripts/jquery.address-1.2rc.min.js
10
+ public/javascripts/jquery.json-2.2.min.js
11
+ public/images/loading-icon-small.gif
12
+ ].each do |file|
13
+ if File.exist?(File.join(Rails.root, file))
14
+ puts "skipped: #{file} exists!"
15
+ else
16
+ begin
17
+ FileUtils.cp(File.join(AJAX_ROOT, file), File.join(Rails.root, file))
18
+ puts "created: #{file}"
19
+ rescue Exception => e
20
+ puts "skipped: #{file} #{e.message}"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ context 'Ajax::UrlHelpers' do
4
+ DOMAINS = %w[musicsocial.com.local altnet.com amusicstreamingservice.com stage.altnet.com rails1.creagency.com.au]
5
+
6
+ describe "(URL) hashed_url_from_traditional" do
7
+ it "should handle a query string" do
8
+ Ajax.hashed_url_from_traditional('/Beyonce?one=1').should == '/#/Beyonce?one=1'
9
+ end
10
+
11
+ it "should ignore the fragment" do
12
+ Ajax.hashed_url_from_traditional('/Beyonce?one=1#fragment').should == '/#/Beyonce?one=1'
13
+ end
14
+
15
+ it "should handle no query string" do
16
+ Ajax.hashed_url_from_traditional('/Beyonce').should == '/#/Beyonce'
17
+ end
18
+
19
+ DOMAINS.each do |domain|
20
+ it "should work for domain #{domain}" do
21
+ Ajax.hashed_url_from_traditional("http://#{domain}/playlists").should == "http://#{domain}/#/playlists"
22
+ end
23
+ end
24
+ end
25
+
26
+ describe "(URL) hashed_url_from_fragment" do
27
+ it "should strip double slashes" do
28
+ Ajax.hashed_url_from_fragment('/Beyonce#/Akon').should == '/#/Akon'
29
+ Ajax.hashed_url_from_fragment('/Beyonce#Akon').should == '/#/Akon'
30
+ end
31
+
32
+ it "should handle no fragment" do
33
+ Ajax.hashed_url_from_fragment('/Beyonce').should == '/#/'
34
+ end
35
+
36
+ DOMAINS.each do |domain|
37
+ it "should work for domain #{domain}" do
38
+ Ajax.hashed_url_from_fragment("http://#{domain}").should == "http://#{domain}/#/"
39
+ Ajax.hashed_url_from_fragment("http://#{domain}/").should == "http://#{domain}/#/"
40
+ Ajax.hashed_url_from_fragment("http://#{domain}/Beyonce/#/playlists").should == "http://#{domain}/#/playlists"
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "(boolean) url_is_root?" do
46
+ it "should detect root urls" do
47
+ Ajax.url_is_root?('/#/Beyonce?query2').should be(true)
48
+ Ajax.url_is_root?('/').should be(true)
49
+ end
50
+
51
+ it "should detect non-root urls" do
52
+ Ajax.url_is_root?('/Beyonce').should be(false)
53
+ end
54
+
55
+ it "should support full URLs" do
56
+ Ajax.is_hashed_url?('http://musicsocial.com.local/#/playlists').should be(true)
57
+ end
58
+ end
59
+
60
+ describe "(boolean) is_hashed_url?" do
61
+ it "should return false for fragments that don't start with /" do
62
+ Ajax.is_hashed_url?('/Beyonce#Akon').should be(false)
63
+ Ajax.is_hashed_url?('/Beyonce?query#Akon/').should be(false)
64
+ end
65
+
66
+ it "should return false for no fragment" do
67
+ Ajax.is_hashed_url?('/Beyonce?query%23/').should be(false)
68
+ end
69
+
70
+ it "should return true if the fragment starts with /" do
71
+ Ajax.is_hashed_url?('/Beyonce#/Akon').should be(true)
72
+ Ajax.is_hashed_url?('/#/Akon').should be(true)
73
+ end
74
+
75
+ DOMAINS.each do |domain|
76
+ it "should work for domain #{domain}" do
77
+ Ajax.is_hashed_url?("http://#{domain}/#/playlists").should be(true)
78
+ Ajax.is_hashed_url?("http://#{domain}/playlists").should be(false)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "(URL) traditional_url_from_fragment" do
84
+ it "should handle slashes" do
85
+ Ajax.traditional_url_from_fragment('/Beyonce#Akon').should == '/Akon'
86
+ Ajax.traditional_url_from_fragment('/Beyonce#/Akon').should == '/Akon'
87
+ Ajax.traditional_url_from_fragment('/Beyonce#/Akon/').should == '/Akon/'
88
+ end
89
+
90
+ it "should handle no fragment" do
91
+ Ajax.traditional_url_from_fragment('/Beyonce').should == '/'
92
+ end
93
+
94
+ DOMAINS.each do |domain|
95
+ it "should work for domain #{domain}" do
96
+ Ajax.traditional_url_from_fragment("http://#{domain}/Beyonce/#playlists").should == "http://#{domain}/playlists"
97
+ Ajax.traditional_url_from_fragment("http://#{domain}/Beyonce/#/playlists").should == "http://#{domain}/playlists"
98
+ end
99
+ end
100
+ end
101
+ end
102
+
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ include Ajax::Helpers::RequestHelper
4
+
5
+ describe 'Ajax::Helpers::RequestHelpers' do
6
+ describe 'set_header' do
7
+ before :each do
8
+ @headers = {}
9
+ end
10
+
11
+ it "should add headers" do
12
+ set_header @headers, :tab, '#main .home_tab'
13
+ @headers['Ajax-Info']['tab'].should == '#main .home_tab'
14
+ end
15
+
16
+ it "should add assets" do
17
+ set_header @headers, :assets, { :key => ['value'] }
18
+ @headers['Ajax-Info']['assets'].should == { :key => ['value'] }
19
+ end
20
+
21
+ it "should merge hashes" do
22
+ set_header @headers, :assets, { :key => ['value1'] }
23
+ set_header @headers, :assets, { :key => ['value2'] }
24
+ @headers['Ajax-Info']['assets'].should == { :key => ['value1', 'value2'] }
25
+ end
26
+
27
+ it "should concat arrays" do
28
+ set_header @headers, :callbacks, ['one']
29
+ set_header @headers, :callbacks, ['two']
30
+ @headers['Ajax-Info']['callbacks'].should == ['one', 'two']
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+ require 'uri'
3
+ require 'ajax/spec/helpers'
4
+
5
+ include Ajax::Spec::Helpers
6
+
7
+ # Test the Rack::Ajax handling of urls according to our block from
8
+ # <tt>config/initializers/ajax.rb</tt>
9
+ #
10
+ # Test Rack middleware using integration tests because the Spec controller tests
11
+ # do not invoke Rack.
12
+ context 'Rack::Ajax' do
13
+ before :all do
14
+ mock_ajax # Force a return from Rack::Ajax
15
+ end
16
+
17
+ after :all do
18
+ unmock_ajax
19
+ end
20
+
21
+ context "XMLHttpRequest" do
22
+ context "hashed url" do
23
+ it "should rewrite GET request" do
24
+ xhr(:get, '/?query1#/Beyonce?query2')
25
+ should_rewrite_to('/Beyonce?query2')
26
+ end
27
+
28
+ it "should not modify POST" do
29
+ xhr(:post, '/#/user_session/new?param1=1&param2=2')
30
+ should_not_modify_request
31
+ end
32
+ end
33
+
34
+ context "traditional url" do
35
+ it "should not be modified" do
36
+ xhr(:get, '/Beyonce')
37
+ should_not_modify_request
38
+ end
39
+
40
+ it "should not be modified" do
41
+ xhr(:post, '/user_session/new?param1=1&param2=2')
42
+ should_not_modify_request
43
+ end
44
+ end
45
+ end
46
+
47
+ context "request for root url" do
48
+ it "should not be modified" do
49
+ get('/')
50
+ should_not_modify_request
51
+ end
52
+
53
+ it "should not be modified" do
54
+ get('/?query_string')
55
+ should_not_modify_request
56
+ end
57
+
58
+ it "should be rewritten if it is hashed" do
59
+ get('/?query1#/Beyonce?query2')
60
+ should_rewrite_to('/Beyonce?query2')
61
+ end
62
+ end
63
+
64
+ context "robot" do
65
+ before :each do
66
+ @user = login_robot_user
67
+ end
68
+
69
+ it "should not modify request for root" do
70
+ get('/')
71
+ should_not_modify_request
72
+ end
73
+
74
+ it "should not modify traditional requests" do
75
+ get('/Beyonce')
76
+ should_not_modify_request
77
+ end
78
+
79
+ context "request hashed" do
80
+ context "non-root url" do
81
+ it "should not modify the request" do
82
+ get('/Akon/?query1#/Beyonce?query2')
83
+ should_not_modify_request
84
+ end
85
+ end
86
+
87
+ context "root url" do
88
+ it "should rewrite to traditional url" do
89
+ get('/#/Beyonce?query2')
90
+ should_rewrite_to('/Beyonce?query2')
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ context "regular user" do
97
+ it "should not modify request for root" do
98
+ get('/')
99
+ should_not_modify_request
100
+ end
101
+
102
+ it "should ignore query string on root url" do
103
+ get('/?query1#/Beyonce?query2')
104
+ should_rewrite_to('/Beyonce?query2')
105
+ end
106
+
107
+ context "request hashed" do
108
+ context "non-root url" do
109
+ it "should redirect to hashed part at root" do
110
+ get('/Akon/?query1#/Beyonce?query2')
111
+ should_redirect_to('/#/Beyonce?query2')
112
+ end
113
+ end
114
+
115
+ context "root url" do
116
+ it "should rewrite to traditional url" do
117
+ get('/#/Beyonce?query2')
118
+ should_rewrite_to('/Beyonce?query2')
119
+ end
120
+ end
121
+ end
122
+
123
+ context "request traditional url" do
124
+ it "should not be modified" do
125
+ get('/')
126
+ should_not_modify_request
127
+ end
128
+ it "should not be modified" do
129
+ get('/?query_string')
130
+ should_not_modify_request
131
+ end
132
+
133
+ it "should redirect GET request" do
134
+ get('/Beyonce')
135
+ should_redirect_to('/#/Beyonce')
136
+ end
137
+
138
+ it "should not modify non-GET request" do
139
+ %w[post put delete].each do |method|
140
+ send(method, '/')
141
+ should_not_modify_request
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end