eyeballs 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/CHANGELOG +15 -0
  2. data/README.md +293 -0
  3. data/Rakefile +45 -0
  4. data/bin/eyeballs +9 -0
  5. data/config.ru +2 -0
  6. data/dist/jquery-1.4.2.min.js +154 -0
  7. data/dist/jquery.livequery.js +226 -0
  8. data/dist/mustache.js +324 -0
  9. data/eyeballs.gemspec +86 -0
  10. data/eyeballs.js.gemspec +83 -0
  11. data/lib/eyeballs.rb +11 -0
  12. data/lib/eyeballs/app_detector.rb +29 -0
  13. data/lib/eyeballs/app_generator.rb +30 -0
  14. data/lib/eyeballs/cli.rb +14 -0
  15. data/lib/eyeballs/controller_generator.rb +26 -0
  16. data/lib/eyeballs/model_generator.rb +26 -0
  17. data/lib/eyeballs/scaffold_generator.rb +66 -0
  18. data/spec/app_generator_spec.rb +51 -0
  19. data/spec/controller_generator_spec.rb +22 -0
  20. data/spec/model_generator_spec.rb +23 -0
  21. data/spec/rack_app_detector_spec.rb +25 -0
  22. data/spec/scaffold_generator_spec.rb +42 -0
  23. data/spec/spec_helper.rb +42 -0
  24. data/src/jquery.o_O.couchdb.js +107 -0
  25. data/src/jquery.o_O.dom.js +37 -0
  26. data/src/jquery.o_O.js +120 -0
  27. data/src/jquery.o_O.rails.js +68 -0
  28. data/src/o_O.js +286 -0
  29. data/src/o_O.localstorage.js +60 -0
  30. data/templates/app_root/index.html +26 -0
  31. data/templates/controller.js +3 -0
  32. data/templates/model.js +3 -0
  33. data/templates/scaffold_controller.js +75 -0
  34. data/templates/scaffold_edit.html.mustache +13 -0
  35. data/templates/scaffold_index.html +47 -0
  36. data/templates/scaffold_partial.html.mustache +12 -0
  37. data/test/unit/qunit.css +119 -0
  38. data/test/unit/qunit.js +1069 -0
  39. data/test/unit/test_controller.html +137 -0
  40. data/test/unit/test_dom.html +81 -0
  41. data/test/unit/test_dom_with_callbacks.html +121 -0
  42. data/test/unit/test_form.html +42 -0
  43. data/test/unit/test_localstorage.html +79 -0
  44. data/test/unit/test_model.html +136 -0
  45. data/test/unit/test_model_with_callbacks.html +118 -0
  46. data/test/unit/test_rails.html +97 -0
  47. metadata +117 -0
@@ -0,0 +1,226 @@
1
+ /*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
2
+ * Dual licensed under the MIT (MIT_LICENSE.txt)
3
+ * and GPL Version 2 (GPL_LICENSE.txt) licenses.
4
+ *
5
+ * Version: 1.1.1
6
+ * Requires jQuery 1.3+
7
+ * Docs: http://docs.jquery.com/Plugins/livequery
8
+ */
9
+
10
+ (function($) {
11
+
12
+ $.extend($.fn, {
13
+ livequery: function(type, fn, fn2) {
14
+ var self = this, q;
15
+
16
+ // Handle different call patterns
17
+ if ($.isFunction(type))
18
+ fn2 = fn, fn = type, type = undefined;
19
+
20
+ // See if Live Query already exists
21
+ $.each( $.livequery.queries, function(i, query) {
22
+ if ( self.selector == query.selector && self.context == query.context &&
23
+ type == query.type && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) )
24
+ // Found the query, exit the each loop
25
+ return (q = query) && false;
26
+ });
27
+
28
+ // Create new Live Query if it wasn't found
29
+ q = q || new $.livequery(this.selector, this.context, type, fn, fn2);
30
+
31
+ // Make sure it is running
32
+ q.stopped = false;
33
+
34
+ // Run it immediately for the first time
35
+ q.run();
36
+
37
+ // Contnue the chain
38
+ return this;
39
+ },
40
+
41
+ expire: function(type, fn, fn2) {
42
+ var self = this;
43
+
44
+ // Handle different call patterns
45
+ if ($.isFunction(type))
46
+ fn2 = fn, fn = type, type = undefined;
47
+
48
+ // Find the Live Query based on arguments and stop it
49
+ $.each( $.livequery.queries, function(i, query) {
50
+ if ( self.selector == query.selector && self.context == query.context &&
51
+ (!type || type == query.type) && (!fn || fn.$lqguid == query.fn.$lqguid) && (!fn2 || fn2.$lqguid == query.fn2.$lqguid) && !this.stopped )
52
+ $.livequery.stop(query.id);
53
+ });
54
+
55
+ // Continue the chain
56
+ return this;
57
+ }
58
+ });
59
+
60
+ $.livequery = function(selector, context, type, fn, fn2) {
61
+ this.selector = selector;
62
+ this.context = context;
63
+ this.type = type;
64
+ this.fn = fn;
65
+ this.fn2 = fn2;
66
+ this.elements = [];
67
+ this.stopped = false;
68
+
69
+ // The id is the index of the Live Query in $.livequery.queries
70
+ this.id = $.livequery.queries.push(this)-1;
71
+
72
+ // Mark the functions for matching later on
73
+ fn.$lqguid = fn.$lqguid || $.livequery.guid++;
74
+ if (fn2) fn2.$lqguid = fn2.$lqguid || $.livequery.guid++;
75
+
76
+ // Return the Live Query
77
+ return this;
78
+ };
79
+
80
+ $.livequery.prototype = {
81
+ stop: function() {
82
+ var query = this;
83
+
84
+ if ( this.type )
85
+ // Unbind all bound events
86
+ this.elements.unbind(this.type, this.fn);
87
+ else if (this.fn2)
88
+ // Call the second function for all matched elements
89
+ this.elements.each(function(i, el) {
90
+ query.fn2.apply(el);
91
+ });
92
+
93
+ // Clear out matched elements
94
+ this.elements = [];
95
+
96
+ // Stop the Live Query from running until restarted
97
+ this.stopped = true;
98
+ },
99
+
100
+ run: function() {
101
+ // Short-circuit if stopped
102
+ if ( this.stopped ) return;
103
+ var query = this;
104
+
105
+ var oEls = this.elements,
106
+ els = $(this.selector, this.context),
107
+ nEls = els.not(oEls);
108
+
109
+ // Set elements to the latest set of matched elements
110
+ this.elements = els;
111
+
112
+ if (this.type) {
113
+ // Bind events to newly matched elements
114
+ nEls.bind(this.type, this.fn);
115
+
116
+ // Unbind events to elements no longer matched
117
+ if (oEls.length > 0)
118
+ $.each(oEls, function(i, el) {
119
+ if ( $.inArray(el, els) < 0 )
120
+ $.event.remove(el, query.type, query.fn);
121
+ });
122
+ }
123
+ else {
124
+ // Call the first function for newly matched elements
125
+ nEls.each(function() {
126
+ query.fn.apply(this);
127
+ });
128
+
129
+ // Call the second function for elements no longer matched
130
+ if ( this.fn2 && oEls.length > 0 )
131
+ $.each(oEls, function(i, el) {
132
+ if ( $.inArray(el, els) < 0 )
133
+ query.fn2.apply(el);
134
+ });
135
+ }
136
+ }
137
+ };
138
+
139
+ $.extend($.livequery, {
140
+ guid: 0,
141
+ queries: [],
142
+ queue: [],
143
+ running: false,
144
+ timeout: null,
145
+
146
+ checkQueue: function() {
147
+ if ( $.livequery.running && $.livequery.queue.length ) {
148
+ var length = $.livequery.queue.length;
149
+ // Run each Live Query currently in the queue
150
+ while ( length-- )
151
+ $.livequery.queries[ $.livequery.queue.shift() ].run();
152
+ }
153
+ },
154
+
155
+ pause: function() {
156
+ // Don't run anymore Live Queries until restarted
157
+ $.livequery.running = false;
158
+ },
159
+
160
+ play: function() {
161
+ // Restart Live Queries
162
+ $.livequery.running = true;
163
+ // Request a run of the Live Queries
164
+ $.livequery.run();
165
+ },
166
+
167
+ registerPlugin: function() {
168
+ $.each( arguments, function(i,n) {
169
+ // Short-circuit if the method doesn't exist
170
+ if (!$.fn[n]) return;
171
+
172
+ // Save a reference to the original method
173
+ var old = $.fn[n];
174
+
175
+ // Create a new method
176
+ $.fn[n] = function() {
177
+ // Call the original method
178
+ var r = old.apply(this, arguments);
179
+
180
+ // Request a run of the Live Queries
181
+ $.livequery.run();
182
+
183
+ // Return the original methods result
184
+ return r;
185
+ }
186
+ });
187
+ },
188
+
189
+ run: function(id) {
190
+ if (id != undefined) {
191
+ // Put the particular Live Query in the queue if it doesn't already exist
192
+ if ( $.inArray(id, $.livequery.queue) < 0 )
193
+ $.livequery.queue.push( id );
194
+ }
195
+ else
196
+ // Put each Live Query in the queue if it doesn't already exist
197
+ $.each( $.livequery.queries, function(id) {
198
+ if ( $.inArray(id, $.livequery.queue) < 0 )
199
+ $.livequery.queue.push( id );
200
+ });
201
+
202
+ // Clear timeout if it already exists
203
+ if ($.livequery.timeout) clearTimeout($.livequery.timeout);
204
+ // Create a timeout to check the queue and actually run the Live Queries
205
+ $.livequery.timeout = setTimeout($.livequery.checkQueue, 20);
206
+ },
207
+
208
+ stop: function(id) {
209
+ if (id != undefined)
210
+ // Stop are particular Live Query
211
+ $.livequery.queries[ id ].stop();
212
+ else
213
+ // Stop all Live Queries
214
+ $.each( $.livequery.queries, function(id) {
215
+ $.livequery.queries[ id ].stop();
216
+ });
217
+ }
218
+ });
219
+
220
+ // Register core DOM manipulation methods
221
+ $.livequery.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove', 'html');
222
+
223
+ // Run Live Queries when the Document is ready
224
+ $(function() { $.livequery.play(); });
225
+
226
+ })(jQuery);
data/dist/mustache.js ADDED
@@ -0,0 +1,324 @@
1
+ /*
2
+ mustache.js — Logic-less templates in JavaScript
3
+
4
+ See http://mustache.github.com/ for more info.
5
+ */
6
+
7
+ var Mustache = function() {
8
+ var Renderer = function() {};
9
+
10
+ Renderer.prototype = {
11
+ otag: "{{",
12
+ ctag: "}}",
13
+ pragmas: {},
14
+ buffer: [],
15
+ pragmas_implemented: {
16
+ "IMPLICIT-ITERATOR": true
17
+ },
18
+
19
+ render: function(template, context, partials, in_recursion) {
20
+ // reset buffer
21
+ // TODO: make this non-lazy
22
+ if(!in_recursion)
23
+ this.buffer = [];
24
+ // fail fast
25
+ if(!this.includes("", template)) {
26
+ if(in_recursion) {
27
+ return template;
28
+ } else {
29
+ this.send(template);
30
+ return;
31
+ }
32
+ }
33
+
34
+ if(!in_recursion) {
35
+ this.buffer = [];
36
+ }
37
+
38
+ template = this.render_pragmas(template);
39
+ var html = this.render_section(template, context, partials);
40
+ if(in_recursion) {
41
+ return this.render_tags(html, context, partials, in_recursion);
42
+ }
43
+
44
+ this.render_tags(html, context, partials, in_recursion);
45
+ },
46
+
47
+ /*
48
+ Sends parsed lines
49
+ */
50
+ send: function(line) {
51
+ if(line != "") {
52
+ this.buffer.push(line);
53
+ }
54
+ },
55
+
56
+ /*
57
+ Looks for %PRAGMAS
58
+ */
59
+ render_pragmas: function(template) {
60
+ // no pragmas
61
+ if(!this.includes("%", template)) {
62
+ return template;
63
+ }
64
+
65
+ var that = this;
66
+ var regex = new RegExp(this.otag + "%([\\w_-]+) ?([\\w]+=[\\w]+)?"
67
+ + this.ctag);
68
+ return template.replace(regex, function(match, pragma, options) {
69
+ if(!that.pragmas_implemented[pragma]) {
70
+ throw({message: "This implementation of mustache doesn't understand the '"
71
+ + pragma + "' pragma"});
72
+ }
73
+ that.pragmas[pragma] = {};
74
+ if(options) {
75
+ var opts = options.split("=");
76
+ that.pragmas[pragma][opts[0]] = opts[1];
77
+ }
78
+ return "";
79
+ // ignore unknown pragmas silently
80
+ });
81
+ },
82
+
83
+ /*
84
+ Tries to find a partial in the global scope and render it
85
+ */
86
+ render_partial: function(name, context, partials) {
87
+ if(!partials || !partials[name]) {
88
+ throw({message: "unknown_partial '" + name + "'"});
89
+ }
90
+ if(typeof(context[name]) != "object") {
91
+ return partials[name];
92
+ }
93
+ return this.render(partials[name], context[name], partials, true);
94
+ },
95
+
96
+ /*
97
+ Renders inverted (^) and normal (#) sections
98
+ */
99
+ render_section: function(template, context, partials) {
100
+ if(!this.includes("#", template) && !this.includes("^", template)) {
101
+ return template;
102
+ }
103
+
104
+ var that = this;
105
+ // CSW - Added "+?" so it finds the tighest bound, not the widest
106
+ var regex = new RegExp(this.otag + "(\\^|\\#)(.+)" + this.ctag +
107
+ "\\s*([\\s\\S]+?)" + this.otag + "\\/\\2" + this.ctag +
108
+ "\\s*", "mg");
109
+
110
+ // for each {{#foo}}{{/foo}} section do...
111
+ return template.replace(regex, function(match, type, name, content) {
112
+ var value = that.find(name, context);
113
+ if(type == "^") { // inverted section
114
+ if(!value || that.is_array(value) && value.length == 0) {
115
+ // false or empty list, render it
116
+ return that.render(content, context, partials, true);
117
+ } else {
118
+ return "";
119
+ }
120
+ } else if(type == "#") { // normal section
121
+ if(that.is_array(value)) { // Enumerable, Let's loop!
122
+ return that.map(value, function(row) {
123
+ return that.render(content, that.merge(context,
124
+ that.create_context(row)), partials, true);
125
+ }).join("");
126
+ } else if(that.is_object(value)) { // Object, Use it as subcontext!
127
+ return that.render(content,
128
+ that.merge(context, that.create_context(value)), partials, true);
129
+ } else if(typeof value === "function") {
130
+ // higher order section
131
+ return value.call(context, content, function(text) {
132
+ return that.render(text, context, partials, true);
133
+ })
134
+ } else if(value) { // boolean section
135
+ return that.render(content, context, partials, true);
136
+ } else {
137
+ return "";
138
+ }
139
+ }
140
+ });
141
+ },
142
+
143
+ /*
144
+ Replace {{foo}} and friends with values from our view
145
+ */
146
+ render_tags: function(template, context, partials, in_recursion) {
147
+ // tit for tat
148
+ var that = this;
149
+
150
+ var new_regex = function() {
151
+ return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#\^]+?)\\1?" +
152
+ that.ctag + "+", "g");
153
+ };
154
+
155
+ var regex = new_regex();
156
+ var lines = template.split("\n");
157
+ for (var i=0; i < lines.length; i++) {
158
+ lines[i] = lines[i].replace(regex, function(match, operator, name) {
159
+ switch(operator) {
160
+ case "!": // ignore comments
161
+ return "";
162
+ case "=": // set new delimiters, rebuild the replace regexp
163
+ that.set_delimiters(name);
164
+ regex = new_regex();
165
+ return "";
166
+ case ">": // render partial
167
+ return that.render_partial(name, context, partials);
168
+ case "{": // the triple mustache is unescaped
169
+ return that.find(name, context);
170
+ default: // escape the value
171
+ return that.escape(that.find(name, context));
172
+ }
173
+ }, this);
174
+ if(!in_recursion) {
175
+ this.send(lines[i]);
176
+ }
177
+ }
178
+
179
+ if(in_recursion) {
180
+ return lines.join("\n");
181
+ }
182
+ },
183
+
184
+ set_delimiters: function(delimiters) {
185
+ var dels = delimiters.split(" ");
186
+ this.otag = this.escape_regex(dels[0]);
187
+ this.ctag = this.escape_regex(dels[1]);
188
+ },
189
+
190
+ escape_regex: function(text) {
191
+ // thank you Simon Willison
192
+ if(!arguments.callee.sRE) {
193
+ var specials = [
194
+ '/', '.', '*', '+', '?', '|',
195
+ '(', ')', '[', ']', '{', '}', '\\'
196
+ ];
197
+ arguments.callee.sRE = new RegExp(
198
+ '(\\' + specials.join('|\\') + ')', 'g'
199
+ );
200
+ }
201
+ return text.replace(arguments.callee.sRE, '\\$1');
202
+ },
203
+
204
+ /*
205
+ find `name` in current `context`. That is find me a value
206
+ from the view object
207
+ */
208
+ find: function(name, context) {
209
+ name = this.trim(name);
210
+ if(typeof context[name] === "function") {
211
+ return context[name].apply(context);
212
+ }
213
+ if(context[name] !== undefined) {
214
+ return context[name];
215
+ }
216
+ // silently ignore unkown variables
217
+ return "";
218
+ },
219
+
220
+ // Utility methods
221
+
222
+ /* includes tag */
223
+ includes: function(needle, haystack) {
224
+ return haystack.indexOf(this.otag + needle) != -1;
225
+ },
226
+
227
+ /*
228
+ Does away with nasty characters
229
+ */
230
+ escape: function(s) {
231
+ return ((s == null) ? "" : s).toString().replace(/&(?!\w+;)|["<>\\]/g, function(s) {
232
+ switch(s) {
233
+ case "&": return "&amp;";
234
+ case "\\": return "\\\\";;
235
+ case '"': return '\"';;
236
+ case "<": return "&lt;";
237
+ case ">": return "&gt;";
238
+ default: return s;
239
+ }
240
+ });
241
+ },
242
+
243
+ /*
244
+ Merges all properties of object `b` into object `a`.
245
+ `b.property` overwrites a.property`
246
+ */
247
+ merge: function(a, b) {
248
+ var _new = {};
249
+ for(var name in a) {
250
+ if(a.hasOwnProperty(name)) {
251
+ _new[name] = a[name];
252
+ }
253
+ };
254
+ for(var name in b) {
255
+ if(b.hasOwnProperty(name)) {
256
+ _new[name] = b[name];
257
+ }
258
+ };
259
+ return _new;
260
+ },
261
+
262
+ // by @langalex, support for arrays of strings
263
+ create_context: function(_context) {
264
+ if(this.is_object(_context)) {
265
+ return _context;
266
+ } else if(this.pragmas["IMPLICIT-ITERATOR"]) {
267
+ var iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator || ".";
268
+ var ctx = {};
269
+ ctx[iterator] = _context;
270
+ return ctx;
271
+ }
272
+ },
273
+
274
+ is_object: function(a) {
275
+ return a && typeof a == "object";
276
+ },
277
+
278
+ is_array: function(a) {
279
+ return Object.prototype.toString.call(a) === '[object Array]';
280
+ },
281
+
282
+ /*
283
+ Gets rid of leading and trailing whitespace
284
+ */
285
+ trim: function(s) {
286
+ return s.replace(/^\s*|\s*$/g, "");
287
+ },
288
+
289
+ /*
290
+ Why, why, why? Because IE. Cry, cry cry.
291
+ */
292
+ map: function(array, fn) {
293
+ if (typeof array.map == "function") {
294
+ return array.map(fn);
295
+ } else {
296
+ var r = [];
297
+ var l = array.length;
298
+ for(var i=0;i<l;i++) {
299
+ r.push(fn(array[i]));
300
+ }
301
+ return r;
302
+ }
303
+ }
304
+ };
305
+
306
+ return({
307
+ name: "mustache.js",
308
+ version: "0.3.0-dev",
309
+
310
+ /*
311
+ Turns a template and view into HTML
312
+ */
313
+ to_html: function(template, view, partials, send_fun) {
314
+ var renderer = new Renderer();
315
+ if(send_fun) {
316
+ renderer.send = send_fun;
317
+ }
318
+ renderer.render(template, view, partials);
319
+ if(!send_fun) {
320
+ return renderer.buffer.join("\n");
321
+ }
322
+ }
323
+ });
324
+ }();