jim 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/HISTORY +28 -0
  2. data/README.rdoc +1 -1
  3. data/Rakefile +2 -1
  4. data/jim.gemspec +67 -9
  5. data/lib/jim/bundler.rb +14 -11
  6. data/lib/jim/cli.rb +51 -9
  7. data/lib/jim/index.rb +3 -7
  8. data/lib/jim/installer.rb +140 -22
  9. data/lib/jim/templates/commands +21 -6
  10. data/lib/jim/version_parser.rb +2 -3
  11. data/lib/jim.rb +19 -2
  12. data/test/fixtures/jimfile +3 -3
  13. data/test/fixtures/sammy-0.5.0/HISTORY.md +135 -0
  14. data/test/fixtures/sammy-0.5.0/LICENSE +22 -0
  15. data/test/fixtures/sammy-0.5.0/README.md +81 -0
  16. data/test/fixtures/sammy-0.5.0/Rakefile +174 -0
  17. data/test/fixtures/sammy-0.5.0/examples/backend/README.md +23 -0
  18. data/test/fixtures/sammy-0.5.0/examples/backend/Rakefile +15 -0
  19. data/test/fixtures/sammy-0.5.0/examples/backend/app.rb +17 -0
  20. data/test/fixtures/sammy-0.5.0/examples/backend/app.ru +3 -0
  21. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/app.js +106 -0
  22. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/jquery.cloudkit.js +840 -0
  23. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/jquery.js +19 -0
  24. data/test/fixtures/sammy-0.5.0/examples/backend/public/javascripts/sammy.js +1013 -0
  25. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/index.html.erb +11 -0
  26. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/task.html.erb +4 -0
  27. data/test/fixtures/sammy-0.5.0/examples/backend/public/templates/task_details.html.erb +4 -0
  28. data/test/fixtures/sammy-0.5.0/examples/backend/views/app.sass +63 -0
  29. data/test/fixtures/sammy-0.5.0/examples/backend/views/index.haml +18 -0
  30. data/test/fixtures/sammy-0.5.0/examples/form_handling/files/form.html +12 -0
  31. data/test/fixtures/sammy-0.5.0/examples/form_handling/index.html +65 -0
  32. data/test/fixtures/sammy-0.5.0/examples/hello_world/index.html +50 -0
  33. data/test/fixtures/sammy-0.5.0/examples/location_override/README.md +15 -0
  34. data/test/fixtures/sammy-0.5.0/examples/location_override/data.html +110 -0
  35. data/test/fixtures/sammy-0.5.0/examples/location_override/index.html +79 -0
  36. data/test/fixtures/sammy-0.5.0/examples/location_override/test.html +121 -0
  37. data/test/fixtures/sammy-0.5.0/lib/min/sammy-0.5.0.min.js +5 -0
  38. data/test/fixtures/sammy-0.5.0/lib/min/sammy-lastest.min.js +5 -0
  39. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.cache.js +117 -0
  40. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.haml.js +539 -0
  41. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.json.js +362 -0
  42. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.mustache.js +415 -0
  43. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.nested_params.js +118 -0
  44. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.storage.js +515 -0
  45. data/test/fixtures/sammy-0.5.0/lib/plugins/sammy.template.js +117 -0
  46. data/test/fixtures/sammy-0.5.0/lib/sammy.js +1367 -0
  47. data/test/fixtures/sammy-0.5.0/test/fixtures/partial +1 -0
  48. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.html +1 -0
  49. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.noengine +1 -0
  50. data/test/fixtures/sammy-0.5.0/test/fixtures/partial.template +1 -0
  51. data/test/fixtures/sammy-0.5.0/test/index.html +84 -0
  52. data/test/fixtures/sammy-0.5.0/test/test_sammy_application.js +953 -0
  53. data/test/fixtures/sammy-0.5.0/test/test_sammy_event_context.js +252 -0
  54. data/test/fixtures/sammy-0.5.0/test/test_sammy_location_proxy.js +91 -0
  55. data/test/fixtures/sammy-0.5.0/test/test_sammy_plugins.js +296 -0
  56. data/test/fixtures/sammy-0.5.0/test/test_sammy_storage.js +175 -0
  57. data/test/fixtures/sammy-0.5.0/test/test_server +27 -0
  58. data/test/fixtures/sammy-0.5.0/vendor/jquery-1.4.1.js +6078 -0
  59. data/test/fixtures/sammy-0.5.0/vendor/jquery-1.4.1.min.js +152 -0
  60. data/test/fixtures/sammy-0.5.0/vendor/jsdoc/doc.haml +58 -0
  61. data/test/fixtures/sammy-0.5.0/vendor/jsdoc/jsdoc.rb +143 -0
  62. data/test/fixtures/sammy-0.5.0/vendor/jslitmus.js +670 -0
  63. data/test/fixtures/sammy-0.5.0/vendor/qunit/qunit.css +119 -0
  64. data/test/fixtures/sammy-0.5.0/vendor/qunit/qunit.js +1043 -0
  65. data/test/fixtures/sammy-0.5.0/vendor/qunit-spec.js +127 -0
  66. data/test/helper.rb +23 -3
  67. data/test/test_jim_bundler.rb +9 -8
  68. data/test/test_jim_cli.rb +21 -12
  69. data/test/test_jim_installer.rb +152 -35
  70. data/test/test_jim_version_parser.rb +4 -0
  71. metadata +117 -27
  72. data/.document +0 -5
@@ -0,0 +1,840 @@
1
+ /*
2
+ json2.js
3
+ 2008-03-14
4
+
5
+ Public Domain
6
+
7
+ No warranty expressed or implied. Use at your own risk.
8
+
9
+ See http://www.JSON.org/js.html
10
+
11
+ This is a reference implementation. You are free to copy, modify, or
12
+ redistribute.
13
+
14
+ Use your own copy. It is extremely unwise to load third party
15
+ code into your pages.
16
+ */
17
+
18
+ if (!this.JSON) {
19
+
20
+ JSON = function () {
21
+
22
+ function f(n) { // Format integers to have at least two digits.
23
+ return n < 10 ? '0' + n : n;
24
+ }
25
+
26
+ Date.prototype.toJSON = function () {
27
+ return this.getUTCFullYear() + '-' +
28
+ f(this.getUTCMonth() + 1) + '-' +
29
+ f(this.getUTCDate()) + 'T' +
30
+ f(this.getUTCHours()) + ':' +
31
+ f(this.getUTCMinutes()) + ':' +
32
+ f(this.getUTCSeconds()) + 'Z';
33
+ };
34
+
35
+
36
+ var m = { // table of character substitutions
37
+ '\b': '\\b',
38
+ '\t': '\\t',
39
+ '\n': '\\n',
40
+ '\f': '\\f',
41
+ '\r': '\\r',
42
+ '"' : '\\"',
43
+ '\\': '\\\\'
44
+ };
45
+
46
+ function stringify(value, whitelist) {
47
+ var a, // The array holding the partial texts.
48
+ i, // The loop counter.
49
+ k, // The member key.
50
+ l, // Length.
51
+ r = /["\\\x00-\x1f\x7f-\x9f]/g,
52
+ v; // The member value.
53
+
54
+ switch (typeof value) {
55
+ case 'string':
56
+
57
+ return r.test(value) ?
58
+ '"' + value.replace(r, function (a) {
59
+ var c = m[a];
60
+ if (c) {
61
+ return c;
62
+ }
63
+ c = a.charCodeAt();
64
+ return '\\u00' + Math.floor(c / 16).toString(16) +
65
+ (c % 16).toString(16);
66
+ }) + '"' :
67
+ '"' + value + '"';
68
+
69
+ case 'number':
70
+
71
+ return isFinite(value) ? String(value) : 'null';
72
+
73
+ case 'boolean':
74
+ case 'null':
75
+ return String(value);
76
+
77
+ case 'object':
78
+
79
+ if (!value) {
80
+ return 'null';
81
+ }
82
+
83
+ if (typeof value.toJSON === 'function') {
84
+ return stringify(value.toJSON());
85
+ }
86
+ a = [];
87
+ if (typeof value.length === 'number' &&
88
+ !(value.propertyIsEnumerable('length'))) {
89
+
90
+ l = value.length;
91
+ for (i = 0; i < l; i += 1) {
92
+ a.push(stringify(value[i], whitelist) || 'null');
93
+ }
94
+
95
+ return '[' + a.join(',') + ']';
96
+ }
97
+ if (whitelist) {
98
+ l = whitelist.length;
99
+ for (i = 0; i < l; i += 1) {
100
+ k = whitelist[i];
101
+ if (typeof k === 'string') {
102
+ v = stringify(value[k], whitelist);
103
+ if (v) {
104
+ a.push(stringify(k) + ':' + v);
105
+ }
106
+ }
107
+ }
108
+ } else {
109
+
110
+ for (k in value) {
111
+ if (typeof k === 'string') {
112
+ v = stringify(value[k], whitelist);
113
+ if (v) {
114
+ a.push(stringify(k) + ':' + v);
115
+ }
116
+ }
117
+ }
118
+ }
119
+
120
+ return '{' + a.join(',') + '}';
121
+ }
122
+ }
123
+
124
+ return {
125
+ stringify: stringify,
126
+ parse: function (text, filter) {
127
+ var j;
128
+
129
+ function walk(k, v) {
130
+ var i, n;
131
+ if (v && typeof v === 'object') {
132
+ for (i in v) {
133
+ if (Object.prototype.hasOwnProperty.apply(v, [i])) {
134
+ n = walk(i, v[i]);
135
+ if (n !== undefined) {
136
+ v[i] = n;
137
+ } else {
138
+ delete v[i];
139
+ }
140
+ }
141
+ }
142
+ }
143
+ return filter(k, v);
144
+ }
145
+
146
+ if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
147
+ replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
148
+ replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
149
+
150
+ j = eval('(' + text + ')');
151
+
152
+ return typeof filter === 'function' ? walk('', j) : j;
153
+ }
154
+
155
+ throw new SyntaxError('parseJSON');
156
+ }
157
+ };
158
+ }();
159
+ }
160
+
161
+ /*
162
+ Copyright Jason E. Smith 2008 Licensed under the Apache License, Version 2.0 (the "License");
163
+ You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
164
+ */
165
+
166
+ /*
167
+ * CREDITS:
168
+ * Thanks to Kris Zyp from SitePen for contributing his source for
169
+ * a standalone port of JSONQuery (from the dojox.json.query module).
170
+ *
171
+ * OVERVIEW:
172
+ * JSONQuery.js is a standalone port of the dojox.json.query module. It is intended as
173
+ * a dropin solution with zero dependencies. JSONQuery is intended to succeed and improve upon
174
+ * the JSONPath api (http://goessner.net/articles/JsonPath/) which offers rich powerful
175
+ * querying capabilities similar to those of XQuery.
176
+ *
177
+ * EXAMPLES / USAGE:
178
+ * see http://www.sitepen.com/blog/2008/07/16/jsonquery-data-querying-beyond-jsonpath/
179
+ *
180
+ * *Ripped from original source.
181
+ * JSONQuery(queryString,object)
182
+ and
183
+ JSONQuery(queryString)(object)
184
+ always return identical results. The first one immediately evaluates, the second one returns a
185
+ function that then evaluates the object.
186
+
187
+ example:
188
+ JSONQuery("foo",{foo:"bar"})
189
+ This will return "bar".
190
+
191
+ example:
192
+ evaluator = JSONQuery("?foo='bar'&rating>3");
193
+ This creates a function that finds all the objects in an array with a property
194
+ foo that is equals to "bar" and with a rating property with a value greater
195
+ than 3.
196
+ evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}])
197
+ This returns:
198
+ {foo:"bar",rating:4}
199
+
200
+ example:
201
+ evaluator = JSONQuery("$[?price<15.00][\rating][0:10]");
202
+ This finds objects in array with a price less than 15.00 and sorts then
203
+ by rating, highest rated first, and returns the first ten items in from this
204
+ filtered and sorted list.
205
+
206
+ example:
207
+ var data = {customers:[
208
+ {name:"Susan", purchases:29},
209
+ {name:"Kim", purchases:150},
210
+ {name:"Jake", purchases:27}
211
+ ]};
212
+
213
+ var results = json.JSONQuery("$.customers[?purchases > 21 & name='Jake'][\\purchases]",data);
214
+
215
+ returns customers sorted by higest number of purchases to lowest.
216
+
217
+ */
218
+
219
+ (function(){
220
+ function map(arr, fun /*, thisp*/){
221
+ var len = arr.length;
222
+ if (typeof fun != "function")
223
+ throw new TypeError();
224
+
225
+ var res = new Array(len);
226
+ var thisp = arguments[2];
227
+ for (var i = 0; i < len; i++) {
228
+ if (i in arr)
229
+ res[i] = fun.call(thisp, arr[i], i, arr);
230
+ }
231
+
232
+ return res;
233
+ }
234
+
235
+ function filter(arr, fun /*, thisp*/){
236
+ var len = arr.length;
237
+ if (typeof fun != "function")
238
+ throw new TypeError();
239
+
240
+ var res = new Array();
241
+ var thisp = arguments[2];
242
+ for (var i = 0; i < len; i++) {
243
+ if (i in arr) {
244
+ var val = arr[i]; // in case fun mutates this
245
+ if (fun.call(thisp, val, i, arr))
246
+ res.push(val);
247
+ }
248
+ }
249
+
250
+ return res;
251
+ };
252
+
253
+ function slice(obj,start,end,step){
254
+ // handles slice operations: [3:6:2]
255
+ var len=obj.length,results = [];
256
+ end = end || len;
257
+ start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start);
258
+ end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end);
259
+ for(var i=start; i<end; i+=step){
260
+ results.push(obj[i]);
261
+ }
262
+ return results;
263
+ }
264
+ function expand(obj,name){
265
+ // handles ..name, .*, [*], [val1,val2], [val]
266
+ // name can be a property to search for, undefined for full recursive, or an array for picking by index
267
+ var results = [];
268
+ function walk(obj){
269
+ if(name){
270
+ if(name===true && !(obj instanceof Array)){
271
+ //recursive object search
272
+ results.push(obj);
273
+ }else if(obj[name]){
274
+ // found the name, add to our results
275
+ results.push(obj[name]);
276
+ }
277
+ }
278
+ for(var i in obj){
279
+ var val = obj[i];
280
+ if(!name){
281
+ // if we don't have a name we are just getting all the properties values (.* or [*])
282
+ results.push(val);
283
+ }else if(val && typeof val == 'object'){
284
+
285
+ walk(val);
286
+ }
287
+ }
288
+ }
289
+ if(name instanceof Array){
290
+ // this is called when multiple items are in the brackets: [3,4,5]
291
+ if(name.length==1){
292
+ // this can happen as a result of the parser becoming confused about commas
293
+ // in the brackets like [@.func(4,2)]. Fixing the parser would require recursive
294
+ // analsys, very expensive, but this fixes the problem nicely.
295
+ return obj[name[0]];
296
+ }
297
+ for(var i = 0; i < name.length; i++){
298
+ results.push(obj[name[i]]);
299
+ }
300
+ }else{
301
+ // otherwise we expanding
302
+ walk(obj);
303
+ }
304
+ return results;
305
+ }
306
+
307
+ function distinctFilter(array, callback){
308
+ // does the filter with removal of duplicates in O(n)
309
+ var outArr = [];
310
+ var primitives = {};
311
+ for(var i=0,l=array.length; i<l; ++i){
312
+ var value = array[i];
313
+ if(callback(value, i, array)){
314
+ if((typeof value == 'object') && value){
315
+ // with objects we prevent duplicates with a marker property
316
+ if(!value.__included){
317
+ value.__included = true;
318
+ outArr.push(value);
319
+ }
320
+ }else if(!primitives[value + typeof value]){
321
+ // with primitives we prevent duplicates by putting it in a map
322
+ primitives[value + typeof value] = true;
323
+ outArr.push(value);
324
+ }
325
+ }
326
+ }
327
+ for(i=0,l=outArr.length; i<l; ++i){
328
+ // cleanup the marker properties
329
+ if(outArr[i]){
330
+ delete outArr[i].__included;
331
+ }
332
+ }
333
+ return outArr;
334
+ }
335
+
336
+ var JSONQuery = function(/*String*/query,/*Object?*/obj){
337
+ // summary:
338
+ // Performs a JSONQuery on the provided object and returns the results.
339
+ // If no object is provided (just a query), it returns a "compiled" function that evaluates objects
340
+ // according to the provided query.
341
+ // query:
342
+ // Query string
343
+ // obj:
344
+ // Target of the JSONQuery
345
+ //
346
+ // description:
347
+ // JSONQuery provides a comprehensive set of data querying tools including filtering,
348
+ // recursive search, sorting, mapping, range selection, and powerful expressions with
349
+ // wildcard string comparisons and various operators. JSONQuery generally supersets
350
+ // JSONPath and provides syntax that matches and behaves like JavaScript where
351
+ // possible.
352
+ //
353
+ // JSONQuery evaluations begin with the provided object, which can referenced with
354
+ // $. From
355
+ // the starting object, various operators can be successively applied, each operating
356
+ // on the result of the last operation.
357
+ //
358
+ // Supported Operators:
359
+ // --------------------
360
+ // * .property - This will return the provided property of the object, behaving exactly
361
+ // like JavaScript.
362
+ // * [expression] - This returns the property name/index defined by the evaluation of
363
+ // the provided expression, behaving exactly like JavaScript.
364
+ // * [?expression] - This will perform a filter operation on an array, returning all the
365
+ // items in an array that match the provided expression. This operator does not
366
+ // need to be in brackets, you can simply use ?expression, but since it does not
367
+ // have any containment, no operators can be used afterwards when used
368
+ // without brackets.
369
+ // * [^?expression] - This will perform a distinct filter operation on an array. This behaves
370
+ // as [?expression] except that it will remove any duplicate values/objects from the
371
+ // result set.
372
+ // * [/expression], [\expression], [/expression, /expression] - This performs a sort
373
+ // operation on an array, with sort based on the provide expression. Multiple comma delimited sort
374
+ // expressions can be provided for multiple sort orders (first being highest priority). /
375
+ // indicates ascending order and \ indicates descending order
376
+ // * [=expression] - This performs a map operation on an array, creating a new array
377
+ // with each item being the evaluation of the expression for each item in the source array.
378
+ // * [start:end:step] - This performs an array slice/range operation, returning the elements
379
+ // from the optional start index to the optional end index, stepping by the optional step number.
380
+ // * [expr,expr] - This a union operator, returning an array of all the property/index values from
381
+ // the evaluation of the comma delimited expressions.
382
+ // * .* or [*] - This returns the values of all the properties of the current object.
383
+ // * $ - This is the root object, If a JSONQuery expression does not being with a $,
384
+ // it will be auto-inserted at the beginning.
385
+ // * @ - This is the current object in filter, sort, and map expressions. This is generally
386
+ // not necessary, names are auto-converted to property references of the current object
387
+ // in expressions.
388
+ // * ..property - Performs a recursive search for the given property name, returning
389
+ // an array of all values with such a property name in the current object and any subobjects
390
+ // * expr = expr - Performs a comparison (like JS's ==). When comparing to
391
+ // a string, the comparison string may contain wildcards * (matches any number of
392
+ // characters) and ? (matches any single character).
393
+ // * expr ~ expr - Performs a string comparison with case insensitivity.
394
+ // * ..[?expression] - This will perform a deep search filter operation on all the objects and
395
+ // subobjects of the current data. Rather than only searching an array, this will search
396
+ // property values, arrays, and their children.
397
+ // * $1,$2,$3, etc. - These are references to extra parameters passed to the query
398
+ // function or the evaluator function.
399
+ // * +, -, /, *, &, |, %, (, ), <, >, <=, >=, != - These operators behave just as they do
400
+ // in JavaScript.
401
+ //
402
+ //
403
+ //
404
+ // | dojox.json.query(queryString,object)
405
+ // and
406
+ // | dojox.json.query(queryString)(object)
407
+ // always return identical results. The first one immediately evaluates, the second one returns a
408
+ // function that then evaluates the object.
409
+ //
410
+ // example:
411
+ // | dojox.json.query("foo",{foo:"bar"})
412
+ // This will return "bar".
413
+ //
414
+ // example:
415
+ // | evaluator = dojox.json.query("?foo='bar'&rating>3");
416
+ // This creates a function that finds all the objects in an array with a property
417
+ // foo that is equals to "bar" and with a rating property with a value greater
418
+ // than 3.
419
+ // | evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}])
420
+ // This returns:
421
+ // | {foo:"bar",rating:4}
422
+ //
423
+ // example:
424
+ // | evaluator = dojox.json.query("$[?price<15.00][\rating][0:10]");
425
+ // This finds objects in array with a price less than 15.00 and sorts then
426
+ // by rating, highest rated first, and returns the first ten items in from this
427
+ // filtered and sorted list.
428
+ tokens = [];
429
+ var depth = 0;
430
+ var str = [];
431
+ query = query.replace(/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g,function(t){
432
+ depth += t == '[' ? 1 : t == ']' ? -1 : 0; // keep track of bracket depth
433
+ return (t == ']' && depth > 0) ? '`]' : // we mark all the inner brackets as skippable
434
+ (t.charAt(0) == '"' || t.charAt(0) == "'") ? "`" + (str.push(t) - 1) :// and replace all the strings
435
+ t;
436
+ });
437
+ var prefix = '';
438
+ function call(name){
439
+ // creates a function call and puts the expression so far in a parameter for a call
440
+ prefix = name + "(" + prefix;
441
+ }
442
+ function makeRegex(t,a,b,c,d){
443
+ // creates a regular expression matcher for when wildcards and ignore case is used
444
+ return str[d].match(/[\*\?]/) || c == '~' ?
445
+ "/^" + str[d].substring(1,str[d].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,".$1") + (c == '~' ? '$/i' : '$/') + ".test(" + a + ")" :
446
+ t;
447
+ }
448
+ query.replace(/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/,function(){
449
+ throw new Error("Unsafe function call");
450
+ });
451
+
452
+ query = query.replace(/([^<>=]=)([^=])/g,"$1=$2"). // change the equals to comparisons
453
+ replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(t){
454
+ return t.charAt(0) == '.' ? t : // leave .prop alone
455
+ t == '@' ? "$obj" :// the reference to the current object
456
+ (t.match(/:|^(\$|Math|true|false|null)$/) ? "" : "$obj.") + t; // plain names should be properties of root... unless they are a label in object initializer
457
+ }).
458
+ replace(/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/g,function(t,a,b){
459
+ var oper = t.match(/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/); // [?expr] and ?expr and [=expr and =expr
460
+ if(oper){
461
+ var prefix = '';
462
+ if(t.match(/^\./)){
463
+ // recursive object search
464
+ call("expand");
465
+ prefix = ",true)";
466
+ }
467
+ call(oper[1].match(/\=/) ? "map" : oper[1].match(/\^/) ? "distinctFilter" : "filter");
468
+ return prefix + ",function($obj){return " + oper[2] + "})";
469
+ }
470
+ oper = t.match(/^\[\s*([\/\\].*)\]/); // [/sortexpr,\sortexpr]
471
+ if(oper){
472
+ // make a copy of the array and then sort it using the sorting expression
473
+ return ".concat().sort(function(a,b){" + oper[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(t,a,b){
474
+ return "var av= " + b.replace(/\$obj/,"a") + ",bv= " + b.replace(/\$obj/,"b") + // FIXME: Should check to make sure the $obj token isn't followed by characters
475
+ ";if(av>bv||bv==null){return " + (a== "/" ? 1 : -1) +";}\n" +
476
+ "if(bv>av||av==null){return " + (a== "/" ? -1 : 1) +";}\n";
477
+ }) + "})";
478
+ }
479
+ oper = t.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/); // slice [0:3]
480
+ if(oper){
481
+ call("slice");
482
+ return "," + (oper[1] || 0) + "," + (oper[2] || 0) + "," + (oper[3] || 1) + ")";
483
+ }
484
+ if(t.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){ // ..prop and [*]
485
+ call("expand");
486
+ return (t.charAt(1) == '.' ?
487
+ ",'" + b + "'" : // ..prop
488
+ t.match(/,/) ?
489
+ "," + t : // [prop1,prop2]
490
+ "") + ")"; // [*]
491
+ }
492
+ return t;
493
+ }).
494
+ replace(/(\$obj\s*(\.\s*[\w_$]+\s*)*)(==|~)\s*`([0-9]+)/g,makeRegex). // create regex matching
495
+ replace(/`([0-9]+)\s*(==|~)\s*(\$obj(\s*\.\s*[\w_$]+)*)/g,function(t,a,b,c,d){ // and do it for reverse =
496
+ return makeRegex(t,c,d,b,a);
497
+ });
498
+ query = prefix + (query.charAt(0) == '$' ? "" : "$") + query.replace(/`([0-9]+|\])/g,function(t,a){
499
+ //restore the strings
500
+ return a == ']' ? ']' : str[a];
501
+ });
502
+ // create a function within this scope (so it can use expand and slice)
503
+
504
+ var executor = eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return " + query + "}");
505
+ for(var i = 0;i<arguments.length-1;i++){
506
+ arguments[i] = arguments[i+1];
507
+ }
508
+ return obj ? executor.apply(this,arguments) : executor;
509
+ };
510
+
511
+ if(typeof namespace == "function"){
512
+ namespace("json::JSONQuery", JSONQuery);
513
+ }
514
+ else {
515
+ window["JSONQuery"] = JSONQuery;
516
+ }
517
+ })();
518
+
519
+
520
+ //------------------------------------------------------------------------------
521
+ //
522
+ // jquery.cloudkit.js source
523
+ //
524
+ // Copyright (c) 2008, 2009 Jon Crosby http://joncrosby.me
525
+ //
526
+ // For the complete source with the bundled dependencies,
527
+ // run 'rake dist' and use the contents of the dist directory.
528
+ //
529
+ //------------------------------------------------------------------------------
530
+
531
+ (function($) {
532
+
533
+ $.cloudkit = $.cloudkit || {};
534
+
535
+ //----------------------------------------------------------------------------
536
+ // Resource Model
537
+ //----------------------------------------------------------------------------
538
+ var buildResource = function(collection, spec, metadata) {
539
+ var that = {};
540
+ var meta = {};
541
+ var json = spec;
542
+
543
+ // return a key that is unique across all local items
544
+ var generateId = function() {
545
+ return (new Date).getTime() + '-' + Math.floor(Math.random()*10000);
546
+ };
547
+
548
+ var saveFromRemote = function() {
549
+ meta = metadata;
550
+ meta.id = generateId();
551
+ };
552
+
553
+ that.save = function(callbacks) {
554
+ if (!(typeof metadata === 'undefined')) {
555
+ return saveFromRemote();
556
+ }
557
+ $.ajax({
558
+ type: 'POST',
559
+ url: collection,
560
+ data: JSON.stringify(spec),
561
+ contentType: 'application/json',
562
+ dataType: 'json',
563
+ processData: false,
564
+ complete: function(response, statusText) {
565
+ if (response.status == 201) {
566
+ meta = JSON.parse(response.responseText);
567
+ meta.id = generateId();
568
+ callbacks.success();
569
+ } else {
570
+ callbacks.error(response.status);
571
+ }
572
+ }
573
+ });
574
+ };
575
+
576
+ that.update = function(spec, callbacks) {
577
+ var id = meta.id;
578
+ $.ajax({
579
+ type: 'PUT',
580
+ url: meta.uri,
581
+ data: JSON.stringify($.extend(json,spec)),
582
+ contentType: 'application/json',
583
+ dataType: 'json',
584
+ beforeSend: function(xhr) {
585
+ xhr.setRequestHeader('If-Match', meta.etag);
586
+ },
587
+ processData: false,
588
+ complete: function(response, statusText) {
589
+ if (response.status == 200) {
590
+ meta = JSON.parse(response.responseText);
591
+ meta.id = id;
592
+ json = $.extend(json,spec);
593
+ callbacks.success();
594
+ } else {
595
+ // TODO implement default 412 strategy as progressive diff/merge
596
+ callbacks.error(response.status);
597
+ }
598
+ }
599
+ });
600
+ };
601
+
602
+ that.destroy = function(callbacks) {
603
+ var id = meta.id
604
+ $.ajax({
605
+ type: 'DELETE',
606
+ url: meta.uri,
607
+ dataType: 'json',
608
+ beforeSend: function(xhr) {
609
+ xhr.setRequestHeader('If-Match', meta.etag);
610
+ },
611
+ processData: false,
612
+ complete: function(response, statusText) {
613
+ meta = JSON.parse(response.responseText);
614
+ meta.id = id;
615
+ if (response.status == 200) {
616
+ meta.deleted = true;
617
+ callbacks.success();
618
+ } else {
619
+ callbacks.error(response.status);
620
+ }
621
+ }
622
+ });
623
+ };
624
+
625
+ that.json = function() {
626
+ return json;
627
+ };
628
+
629
+ that.id = function() {
630
+ return meta.id;
631
+ };
632
+
633
+ that.uri = function() {
634
+ return meta.uri;
635
+ };
636
+
637
+ that.isDeleted = function() {
638
+ return (meta.deleted == true);
639
+ };
640
+
641
+ that.attr = function(name, value) {
642
+ if (typeof json[name] != 'undefined') {
643
+ switch(typeof value) {
644
+ case 'undefined':
645
+ return json[name]; break;
646
+ case 'function':
647
+ return json[name] = value.apply(json[name]); break;
648
+ default:
649
+ return json[name] = value;
650
+ }
651
+ } else if (typeof meta[name] != 'undefined') {
652
+ return meta[name];
653
+ }
654
+ };
655
+
656
+ return that;
657
+ };
658
+
659
+ //----------------------------------------------------------------------------
660
+ // Internal Data Store
661
+ //----------------------------------------------------------------------------
662
+ var buildStore = function(collection) {
663
+ var that = {};
664
+
665
+ var key = function(resource) {
666
+ return collection+resource.id();
667
+ };
668
+
669
+ var persist = function(resource) {
670
+ var k = key(resource);
671
+ $.data(window, k, resource);
672
+ var index = $.data(window, collection+'index') || [];
673
+ index.push(k);
674
+ $.data(window, collection+'index', index);
675
+ };
676
+
677
+ that.create = function(spec, callbacks) {
678
+ resource = buildResource(collection, spec);
679
+ resource.save({
680
+ success: function() {
681
+ persist(resource);
682
+ callbacks.success(resource);
683
+ },
684
+ error: function(status) {
685
+ callbacks.error(status);
686
+ }
687
+ });
688
+ };
689
+
690
+ that.createFromRemote = function(spec, metadata) {
691
+ resource = buildResource(collection, spec, metadata);
692
+ resource.save();
693
+ persist(resource);
694
+ return resource;
695
+ };
696
+
697
+ that.all = function(spec) {
698
+ // TODO - don't ignore spec
699
+ var result = [];
700
+ var index = $.data(window, collection+'index');
701
+ $(index).each(function(count, id) {
702
+ var item = $.data(window, id);
703
+ if (!item.isDeleted()) {
704
+ result.push(item);
705
+ }
706
+ });
707
+ return result;
708
+ };
709
+
710
+ that.get = function(id) {
711
+ return $.data(window, collection+id);
712
+ };
713
+
714
+ that.query = function(spec) {
715
+ var jsonObjects = [];
716
+ var self = this;
717
+ $(this.all()).each(function(index, item) {
718
+ json = $.extend(item.json(), {'___id___':item.id()});
719
+ jsonObjects.push(json);
720
+ });
721
+ var query_result = JSONQuery(spec, jsonObjects);
722
+ var resources = []
723
+ $(query_result).each(function(index, item) {
724
+ resources.push(self.get(item['___id___']));
725
+ });
726
+ return resources;
727
+ }
728
+
729
+ return that;
730
+ };
731
+
732
+ //----------------------------------------------------------------------------
733
+ // Private API
734
+ //----------------------------------------------------------------------------
735
+
736
+ var collectionURIs = []; // collection URIs found during boot via discovery
737
+ var collections = {}; // local stores, one per remote resource collection
738
+
739
+ // load remote collection URIs
740
+ var loadMeta = function(callbacks) {
741
+ $.ajax({
742
+ type: 'GET',
743
+ url: '/cloudkit-meta',
744
+ complete: function(response, statusText) {
745
+ data = JSON.parse(response.responseText);
746
+ if (response.status == 200) {
747
+ collectionURIs = data.uris;
748
+ callbacks.success();
749
+ } else if (response.status >= 400) {
750
+ callbacks.error(response.status);
751
+ } else {
752
+ callbacks.error('unexpected error');
753
+ }
754
+ }
755
+ });
756
+ };
757
+
758
+ // configure a local collection
759
+ var configureCollection = function(collection) {
760
+ $.data(window, collection+'index', []);
761
+ var name = collection.replace(/^\//, '');
762
+ collections[name] = buildStore(collection);
763
+ };
764
+
765
+ // load remote data into local store
766
+ var populateCollectionsFromRemote = function(index, callbacks) {
767
+ if (index == collectionURIs.length) {
768
+ callbacks.success();
769
+ return;
770
+ }
771
+ $.ajax({
772
+ type: 'GET',
773
+ url: collectionURIs[index]+"/_resolved",
774
+ dataType: 'json',
775
+ processData: false,
776
+ complete: function(response, statusText) {
777
+ if (response.status == 200) {
778
+ var resources = JSON.parse(response.responseText).documents;
779
+ var name = collectionURIs[index].replace(/^\//, '');
780
+ for (var i = 0; i < resources.length; i++) {
781
+ var resource = resources[i];
782
+ collections[name].createFromRemote(
783
+ JSON.parse(resource.document),
784
+ {
785
+ uri: resource.uri,
786
+ etag: resource.etag,
787
+ last_modified: resource.last_modified
788
+ }
789
+ );
790
+ }
791
+ populateCollectionsFromRemote(index+1, callbacks);
792
+ } else {
793
+ callbacks.error(response.status);
794
+ }
795
+ }
796
+ });
797
+ };
798
+
799
+ // extend jquery
800
+ $.fn.extend($.cloudkit, {
801
+
802
+ //--------------------------------------------------------------------------
803
+ // Public API
804
+ //--------------------------------------------------------------------------
805
+
806
+ // setup the local store
807
+ boot: function(callbacks) {
808
+ collectionURIs = [];
809
+ collections = [];
810
+ loadMeta({
811
+ success: function() {
812
+ $(collectionURIs).each(function(index, collection) {
813
+ configureCollection(collection);
814
+ });
815
+ populateCollectionsFromRemote(0, {
816
+ success: function() {
817
+ callbacks.success();
818
+ },
819
+ error: function(status) {
820
+ callbacks.error(status);
821
+ }
822
+ });
823
+ },
824
+ error: function(status) {
825
+ callbacks.error(status);
826
+ }
827
+ });
828
+ },
829
+
830
+ // return all collections
831
+ collections: function() {
832
+ return collections;
833
+ },
834
+
835
+ // return a specific collection
836
+ collection: function(name) {
837
+ return this.collections()[name];
838
+ }
839
+ });
840
+ })(jQuery);