rasputin 0.10.2 → 0.10.3

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.
@@ -86,10 +86,15 @@ Handlebars.registerHelper('unless', function(context, options) {
86
86
  Handlebars.registerHelper('with', function(context, options) {
87
87
  return options.fn(context);
88
88
  });
89
+
90
+ Handlebars.registerHelper('log', function(context) {
91
+ Handlebars.log(context);
92
+ });
89
93
  ;
90
94
  // lib/handlebars/compiler/parser.js
91
95
  /* Jison generated parser */
92
96
  var handlebars = (function(){
97
+
93
98
  var parser = {trace: function trace() { },
94
99
  yy: {},
95
100
  symbols_: {"error":2,"root":3,"program":4,"EOF":5,"statements":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"OPEN_PARTIAL":24,"params":25,"hash":26,"param":27,"STRING":28,"INTEGER":29,"BOOLEAN":30,"hashSegments":31,"hashSegment":32,"ID":33,"EQUALS":34,"pathSegments":35,"SEP":36,"$accept":0,"$end":1},
@@ -369,7 +374,9 @@ parse: function parse(input) {
369
374
 
370
375
  return true;
371
376
  }};/* Jison generated lexer */
372
- var lexer = (function(){var lexer = ({EOF:1,
377
+ var lexer = (function(){
378
+
379
+ var lexer = ({EOF:1,
373
380
  parseError:function parseError(str, hash) {
374
381
  if (this.yy.parseError) {
375
382
  this.yy.parseError(str, hash);
@@ -531,18 +538,38 @@ case 21: return 29;
531
538
  break;
532
539
  case 22: return 33;
533
540
  break;
534
- case 23: return 'INVALID';
541
+ case 23: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33;
542
+ break;
543
+ case 24: return 'INVALID';
535
544
  break;
536
- case 24: return 5;
545
+ case 25: return 5;
537
546
  break;
538
547
  }
539
548
  };
540
- lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s/.])/,/^./,/^$/];
541
- lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24],"inclusive":false},"INITIAL":{"rules":[0,1,24],"inclusive":true}};return lexer;})()
549
+ lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s/.])/,/^\[.*\]/,/^./,/^$/];
550
+ lexer.conditions = {"mu":{"rules":[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],"inclusive":false},"INITIAL":{"rules":[0,1,25],"inclusive":true}};return lexer;})()
542
551
  parser.lexer = lexer;
543
552
  return parser;
544
553
  })();
545
-
554
+ if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
555
+ exports.parser = handlebars;
556
+ exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
557
+ exports.main = function commonjsMain(args) {
558
+ if (!args[1])
559
+ throw new Error('Usage: '+args[0]+' FILE');
560
+ if (typeof process !== 'undefined') {
561
+ var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
562
+ } else {
563
+ var cwd = require("file").path(require("file").cwd());
564
+ var source = cwd.join(args[1]).read({charset: "utf-8"});
565
+ }
566
+ return exports.parser.parse(source);
567
+ }
568
+ if (typeof module !== 'undefined' && require.main === module) {
569
+ exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
570
+ }
571
+ };
572
+ ;
546
573
  // lib/handlebars/compiler/base.js
547
574
  Handlebars.Parser = handlebars;
548
575
 
@@ -829,7 +856,8 @@ Handlebars.JavaScriptCompiler = function() {};
829
856
  'each': true,
830
857
  'if': true,
831
858
  'unless': true,
832
- 'with': true
859
+ 'with': true,
860
+ 'log': true
833
861
  };
834
862
  if (knownHelpers) {
835
863
  for (var name in knownHelpers) {
@@ -1039,12 +1067,13 @@ Handlebars.JavaScriptCompiler = function() {};
1039
1067
  // PUBLIC API: You can override these methods in a subclass to provide
1040
1068
  // alternative compiled forms for name lookup and buffering semantics
1041
1069
  nameLookup: function(parent, name, type) {
1042
- if(JavaScriptCompiler.RESERVED_WORDS[name] || name.indexOf('-') !== -1 || !isNaN(name)) {
1043
- return parent + "['" + name + "']";
1044
- } else if (/^[0-9]+$/.test(name)) {
1070
+ if (/^[0-9]+$/.test(name)) {
1045
1071
  return parent + "[" + name + "]";
1046
- } else {
1047
- return parent + "." + name;
1072
+ } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
1073
+ return parent + "." + name;
1074
+ }
1075
+ else {
1076
+ return parent + "['" + name + "']";
1048
1077
  }
1049
1078
  },
1050
1079
 
@@ -1059,6 +1088,8 @@ Handlebars.JavaScriptCompiler = function() {};
1059
1088
  initializeBuffer: function() {
1060
1089
  return this.quotedString("");
1061
1090
  },
1091
+
1092
+ namespace: "Handlebars",
1062
1093
  // END PUBLIC API
1063
1094
 
1064
1095
  compile: function(environment, options, context, asObject) {
@@ -1129,8 +1160,9 @@ Handlebars.JavaScriptCompiler = function() {};
1129
1160
  var out = [];
1130
1161
 
1131
1162
  if (!this.isChild) {
1132
- var copies = "helpers = helpers || Handlebars.helpers;";
1133
- if(this.environment.usePartial) { copies = copies + " partials = partials || Handlebars.partials;"; }
1163
+ var namespace = this.namespace;
1164
+ var copies = "helpers = helpers || " + namespace + ".helpers;";
1165
+ if(this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
1134
1166
  out.push(copies);
1135
1167
  } else {
1136
1168
  out.push('');
@@ -1185,8 +1217,6 @@ Handlebars.JavaScriptCompiler = function() {};
1185
1217
  params.push("depth" + this.environment.depths.list[i]);
1186
1218
  }
1187
1219
 
1188
- if(params.length === 4 && !this.environment.usePartial) { params.pop(); }
1189
-
1190
1220
  if (asObject) {
1191
1221
  params.push(this.source.join("\n "));
1192
1222
 
@@ -1247,6 +1277,7 @@ Handlebars.JavaScriptCompiler = function() {};
1247
1277
  + this.nameLookup('depth' + this.lastContext, name, 'context');
1248
1278
  }
1249
1279
 
1280
+ toPush += ';';
1250
1281
  this.source.push(toPush);
1251
1282
  } else {
1252
1283
  this.pushStack('depth' + this.lastContext);
@@ -1255,7 +1286,8 @@ Handlebars.JavaScriptCompiler = function() {};
1255
1286
 
1256
1287
  lookup: function(name) {
1257
1288
  var topStack = this.topStack();
1258
- this.source.push(topStack + " = " + this.nameLookup(topStack, name, 'context') + ";");
1289
+ this.source.push(topStack + " = (" + topStack + " === null || " + topStack + " === undefined || " + topStack + " === false ? " +
1290
+ topStack + " : " + this.nameLookup(topStack, name, 'context') + ");");
1259
1291
  },
1260
1292
 
1261
1293
  pushStringParam: function(string) {
@@ -1449,15 +1481,22 @@ Handlebars.JavaScriptCompiler = function() {};
1449
1481
  };
1450
1482
 
1451
1483
  var reservedWords = ("break case catch continue default delete do else finally " +
1452
- "for function if in instanceof new return switch this throw " +
1484
+ "for function if in instanceof new return switch this throw " +
1453
1485
  "try typeof var void while with null true false").split(" ");
1454
1486
 
1455
- compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
1487
+ var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
1456
1488
 
1457
1489
  for(var i=0, l=reservedWords.length; i<l; i++) {
1458
1490
  compilerWords[reservedWords[i]] = true;
1459
1491
  }
1460
1492
 
1493
+ JavaScriptCompiler.isValidJavaScriptVariableName = function(name) {
1494
+ if(!JavaScriptCompiler.RESERVED_WORDS[name] && /^[a-zA-Z_$][0-9a-zA-Z_$]+$/.test(name)) {
1495
+ return true;
1496
+ }
1497
+ return false;
1498
+ }
1499
+
1461
1500
  })(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
1462
1501
 
1463
1502
  Handlebars.precompile = function(string, options) {
@@ -1471,10 +1510,21 @@ Handlebars.precompile = function(string, options) {
1471
1510
  Handlebars.compile = function(string, options) {
1472
1511
  options = options || {};
1473
1512
 
1474
- var ast = Handlebars.parse(string);
1475
- var environment = new Handlebars.Compiler().compile(ast, options);
1476
- var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
1477
- return Handlebars.template(templateSpec);
1513
+ var compiled;
1514
+ function compile() {
1515
+ var ast = Handlebars.parse(string);
1516
+ var environment = new Handlebars.Compiler().compile(ast, options);
1517
+ var templateSpec = new Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
1518
+ return Handlebars.template(templateSpec);
1519
+ }
1520
+
1521
+ // Template is only compiled on first use and cached after that point.
1522
+ return function(context, options) {
1523
+ if (!compiled) {
1524
+ compiled = compile();
1525
+ }
1526
+ return compiled.call(this, context, options);
1527
+ };
1478
1528
  };
1479
1529
  ;
1480
1530
  // lib/handlebars/vm.js
@@ -1544,7 +1594,7 @@ Handlebars.template = Handlebars.VM.template;
1544
1594
  https://github.com/SlexAxton/sc-handlebars
1545
1595
  */
1546
1596
 
1547
- var SC = { Handlebars : {} };
1597
+ var SC = { Handlebars : Object.create(Handlebars) };
1548
1598
  this.SC = SC;
1549
1599
 
1550
1600
  SC.Handlebars.Compiler = function() {};
@@ -1554,6 +1604,7 @@ SC.Handlebars.Compiler.prototype.compiler = SC.Handlebars.Compiler;
1554
1604
  SC.Handlebars.JavaScriptCompiler = function() {};
1555
1605
  SC.Handlebars.JavaScriptCompiler.prototype = Object.create(Handlebars.JavaScriptCompiler.prototype);
1556
1606
  SC.Handlebars.JavaScriptCompiler.prototype.compiler = SC.Handlebars.JavaScriptCompiler;
1607
+ SC.Handlebars.JavaScriptCompiler.prototype.namespace = "SC.Handlebars";
1557
1608
 
1558
1609
  /**
1559
1610
  Override the default property lookup semantics of Handlebars.
@@ -9,7 +9,7 @@ module Rasputin
9
9
  def evaluate(scope, locals, &block)
10
10
  if Rails.configuration.rasputin.precompile_handlebars
11
11
  func = Rasputin::Handlebars.compile(data)
12
- "SC.TEMPLATES[#{template_path(scope.logical_path).inspect}] = Handlebars.template(#{func});"
12
+ "SC.TEMPLATES[#{template_path(scope.logical_path).inspect}] = SC.Handlebars.template(#{func});"
13
13
  else
14
14
  "SC.TEMPLATES[#{template_path(scope.logical_path).inspect}] = SC.Handlebars.compile(#{indent(data).inspect});"
15
15
  end
@@ -1,3 +1,3 @@
1
1
  module Rasputin
2
- VERSION = "0.10.2"
2
+ VERSION = "0.10.3"
3
3
  end
@@ -15,10 +15,9 @@ Gem::Specification.new do |s|
15
15
  s.add_runtime_dependency 'railties', '~> 3.1.0'
16
16
  s.add_runtime_dependency 'actionpack', '~> 3.1.0'
17
17
  s.add_runtime_dependency 'sprockets', '~> 2.0.0'
18
- #s.add_runtime_dependency 'jquery-rails', '~> 1.0'
18
+ s.add_runtime_dependency 'jquery-rails', '~> 1.0'
19
19
 
20
20
  s.files = `git ls-files`.split("\n")
21
21
  #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
- #s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
22
  s.require_paths = ["lib"]
24
23
  end
@@ -1,3 +1,4 @@
1
+
1
2
  (function(exports) {
2
3
  // ==========================================================================
3
4
  // Project: SproutCore IndexSet
@@ -1250,1176 +1251,6 @@ SC.IndexSet.reopenClass({
1250
1251
  // License: Licensed under MIT license (see license.js)
1251
1252
  // ==========================================================================
1252
1253
 
1253
- var get = SC.get, set = SC.set;
1254
-
1255
- // simple copy op needed for just this code.
1256
- function copy(opts) {
1257
- var ret = {};
1258
- for(var key in opts) {
1259
- if (opts.hasOwnProperty(key)) ret[key] = opts[key];
1260
- }
1261
- return ret;
1262
- }
1263
-
1264
- /**
1265
- Standard error thrown by `SC.Scanner` when it runs out of bounds
1266
-
1267
- @static
1268
- @constant
1269
- @type Error
1270
- */
1271
- SC.SCANNER_OUT_OF_BOUNDS_ERROR = "Out of bounds.";
1272
-
1273
- /**
1274
- Standard error thrown by `SC.Scanner` when you pass a value not an integer.
1275
-
1276
- @static
1277
- @constant
1278
- @type Error
1279
- */
1280
- SC.SCANNER_INT_ERROR = "Not an int.";
1281
-
1282
- /**
1283
- Standard error thrown by `SC.Scanner` when it cannot find a string to skip.
1284
-
1285
- @static
1286
- @constant
1287
- @type Error
1288
- */
1289
- SC.SCANNER_SKIP_ERROR = "Did not find the string to skip.";
1290
-
1291
- /**
1292
- Standard error thrown by `SC.Scanner` when it can any kind a string in the
1293
- matching array.
1294
-
1295
- @static
1296
- @constant
1297
- @type Error
1298
- */
1299
- SC.SCANNER_SCAN_ARRAY_ERROR = "Did not find any string of the given array to scan.";
1300
-
1301
- /**
1302
- Standard error thrown when trying to compare two dates in different
1303
- timezones.
1304
-
1305
- @static
1306
- @constant
1307
- @type Error
1308
- */
1309
- SC.DATETIME_COMPAREDATE_TIMEZONE_ERROR = "Can't compare the dates of two DateTimes that don't have the same timezone.";
1310
-
1311
- /**
1312
- Standard ISO8601 date format
1313
-
1314
- @static
1315
- @type String
1316
- @default '%Y-%m-%dT%H:%M:%S%Z'
1317
- @constant
1318
- */
1319
- SC.DATETIME_ISO8601 = '%Y-%m-%dT%H:%M:%S%Z';
1320
-
1321
-
1322
- /**
1323
- @ignore
1324
- @private
1325
-
1326
- A Scanner reads a string and interprets the characters into numbers. You
1327
- assign the scanner's string on initialization and the scanner progresses
1328
- through the characters of that string from beginning to end as you request
1329
- items.
1330
-
1331
- Scanners are used by `DateTime` to convert strings into `DateTime` objects.
1332
-
1333
- @extends SC.Object
1334
- @since SproutCore 1.0
1335
- @author Martin Ottenwaelter
1336
- */
1337
- var Scanner = SC.Object.extend({
1338
-
1339
- /**
1340
- The string to scan. You usually pass it to the create method:
1341
-
1342
- Scanner.create({string: 'May, 8th'});
1343
-
1344
- @type String
1345
- */
1346
- string: null,
1347
-
1348
- /**
1349
- The current scan location. It is incremented by the scanner as the
1350
- characters are processed.
1351
- The default is 0: the beginning of the string.
1352
-
1353
- @type Integer
1354
- */
1355
- scanLocation: 0,
1356
-
1357
- /**
1358
- Reads some characters from the string, and increments the scan location
1359
- accordingly.
1360
-
1361
- @param {Integer} len The amount of characters to read
1362
- @throws {SC.SCANNER_OUT_OF_BOUNDS_ERROR} If asked to read too many characters
1363
- @returns {String} The characters
1364
- */
1365
- scan: function(len) {
1366
- if (this.scanLocation + len > this.length) {
1367
- throw new Error(SC.SCANNER_OUT_OF_BOUNDS_ERROR);
1368
- }
1369
- var str = this.string.substr(this.scanLocation, len);
1370
- this.scanLocation += len;
1371
- return str;
1372
- },
1373
-
1374
- /**
1375
- Reads some characters from the string and interprets it as an integer.
1376
-
1377
- @param {Integer} min_len The minimum amount of characters to read
1378
- @param {Integer} [max_len] The maximum amount of characters to read (defaults to the minimum)
1379
- @throws {SC.SCANNER_INT_ERROR} If asked to read non numeric characters
1380
- @returns {Integer} The scanned integer
1381
- */
1382
- scanInt: function(min_len, max_len) {
1383
- if (max_len === undefined) max_len = min_len;
1384
- var str = this.scan(max_len);
1385
- var re = new RegExp("^\\d{" + min_len + "," + max_len + "}");
1386
- var match = str.match(re);
1387
- if (!match) throw new Error(SC.SCANNER_INT_ERROR);
1388
- if (match[0].length < max_len) {
1389
- this.scanLocation += match[0].length - max_len;
1390
- }
1391
- return parseInt(match[0], 10);
1392
- },
1393
-
1394
- /**
1395
- Attempts to skip a given string.
1396
-
1397
- @param {String} str The string to skip
1398
- @throws {SC.SCANNER_SKIP_ERROR} If the given string could not be scanned
1399
- @returns {Boolean} YES if the given string was successfully scanned, NO otherwise
1400
- */
1401
- skipString: function(str) {
1402
- if (this.scan(str.length) !== str) {
1403
- throw new Error(SC.SCANNER_SKIP_ERROR);
1404
- }
1405
-
1406
- return YES;
1407
- },
1408
-
1409
- /**
1410
- Attempts to scan any string in a given array.
1411
-
1412
- @param {Array} ary the array of strings to scan
1413
- @throws {SC.SCANNER_SCAN_ARRAY_ERROR} If no string of the given array is found
1414
- @returns {Integer} The index of the scanned string of the given array
1415
- */
1416
- scanArray: function(ary) {
1417
- for (var i = 0, len = ary.length; i < len; i++) {
1418
- if (this.scan(ary[i].length) === ary[i]) {
1419
- return i;
1420
- }
1421
- this.scanLocation -= ary[i].length;
1422
- }
1423
- throw new Error(SC.SCANNER_SCAN_ARRAY_ERROR);
1424
- }
1425
-
1426
- });
1427
-
1428
-
1429
- /** @class
1430
-
1431
- A class representation of a date and time. It's basically a wrapper around
1432
- the Date javascript object, KVO-friendly and with common date/time
1433
- manipulation methods.
1434
-
1435
- This object differs from the standard JS Date object, however, in that it
1436
- supports time zones other than UTC and that local to the machine on which
1437
- it is running. Any time zone can be specified when creating an
1438
- `SC.DateTime` object, e.g.
1439
-
1440
- // Creates a DateTime representing 5am in Washington, DC and 10am in
1441
- // London
1442
- var d = SC.DateTime.create({ hour: 5, timezone: 300 }); // -5 hours from UTC
1443
- var e = SC.DateTime.create({ hour: 10, timezone: 0 }); // same time, specified in UTC
1444
-
1445
- and it is true that `d.isEqual(e)`.
1446
-
1447
- The time zone specified upon creation is permanent, and any calls to
1448
- `get()` on that instance will return values expressed in that time zone. So,
1449
-
1450
- d.hour returns 5.
1451
- e.hour returns 10.
1452
-
1453
- but
1454
-
1455
- d.milliseconds === e.milliseconds
1456
-
1457
- is true, since they are technically the same position in time.
1458
-
1459
- @extends SC.Object
1460
- @extends SC.Freezable
1461
- @extends SC.Copyable
1462
- @author Martin Ottenwaelter
1463
- @author Jonathan Lewis
1464
- @author Josh Holt
1465
- @since SproutCore 1.0
1466
- */
1467
- SC.DateTime = SC.Object.extend(SC.Freezable, SC.Copyable,
1468
- /** @scope SC.DateTime.prototype */ {
1469
-
1470
- /**
1471
- @private
1472
-
1473
- Internal representation of a date: the number of milliseconds
1474
- since January, 1st 1970 00:00:00.0 UTC.
1475
-
1476
- @property
1477
- @type {Integer}
1478
- */
1479
- _ms: 0,
1480
-
1481
- /** @read-only
1482
- The offset, in minutes, between UTC and the object's timezone.
1483
- All calls to `get()` will use this time zone to translate date/time
1484
- values into the zone specified here.
1485
-
1486
- @type Integer
1487
- */
1488
- timezone: 0,
1489
-
1490
- /**
1491
- A `SC.DateTime` instance is frozen by default for better performance.
1492
-
1493
- @type Boolean
1494
- */
1495
- isFrozen: YES,
1496
-
1497
- /**
1498
- Returns a new `SC.DateTime` object where one or more of the elements have
1499
- been changed according to the options parameter. The time options (hour,
1500
- minute, sec, usec) reset cascadingly, so if only the hour is passed, then
1501
- minute, sec, and usec is set to 0. If the hour and minute is passed, then
1502
- sec and usec is set to 0.
1503
-
1504
- If a time zone is passed in the options hash, all dates and times are
1505
- assumed to be local to it, and the returned `SC.DateTime` instance has
1506
- that time zone. If none is passed, it defaults to `SC.DateTime.timezone`.
1507
-
1508
- Note that passing only a time zone does not affect the actual milliseconds
1509
- since Jan 1, 1970, only the time zone in which it is expressed when
1510
- displayed.
1511
-
1512
- @see SC.DateTime#create for the list of options you can pass
1513
- @returns {SC.DateTime} copy of receiver
1514
- */
1515
- adjust: function(options, resetCascadingly) {
1516
- var timezone;
1517
-
1518
- options = options ? copy(options) : {};
1519
- timezone = (options.timezone !== undefined) ? options.timezone : (this.timezone !== undefined) ? this.timezone : 0;
1520
-
1521
- return this.constructor._adjust(options, this._ms, timezone, resetCascadingly)._createFromCurrentState();
1522
- },
1523
-
1524
- /**
1525
- Returns a new `SC.DateTime` object advanced according the the given
1526
- parameters. Don't use floating point values, it might give unpredicatble results.
1527
-
1528
- @see SC.DateTime#create for the list of options you can pass
1529
- @param {Hash} options the amount of date/time to advance the receiver
1530
- @returns {DateTime} copy of the receiver
1531
- */
1532
- advance: function(options) {
1533
- return this.constructor._advance(options, this._ms, this.timezone)._createFromCurrentState();
1534
- },
1535
-
1536
- /**
1537
- Generic getter.
1538
-
1539
- The properties you can get are:
1540
- - `year`
1541
- - `month` (January is 1, contrary to JavaScript Dates for which January is 0)
1542
- - `day`
1543
- - `dayOfWeek` (Sunday is 0)
1544
- - `hour`
1545
- - `minute`
1546
- - `second`
1547
- - `millisecond`
1548
- - `milliseconds`, the number of milliseconds since
1549
- January, 1st 1970 00:00:00.0 UTC
1550
- - `isLeapYear`, a boolean value indicating whether the receiver's year
1551
- is a leap year
1552
- - `daysInMonth`, the number of days of the receiver's current month
1553
- - `dayOfYear`, January 1st is 1, December 31th is 365 for a common year
1554
- - `week` or `week1`, the week number of the current year, starting with
1555
- the first Sunday as the first day of the first week (00..53)
1556
- - `week0`, the week number of the current year, starting with
1557
- the first Monday as the first day of the first week (00..53)
1558
- - `lastMonday`, `lastTuesday`, etc., `nextMonday`,
1559
- `nextTuesday`, etc., the date of the last or next weekday in
1560
- comparison to the receiver.
1561
-
1562
- @param {String} key the property name to get
1563
- @return the value asked for
1564
- */
1565
- unknownProperty: function(key) {
1566
- return this.constructor._get(key, this._ms, this.timezone);
1567
- },
1568
-
1569
- /**
1570
- Formats the receiver according to the given format string. Should behave
1571
- like the C strftime function.
1572
-
1573
- The format parameter can contain the following characters:
1574
- - %a -- The abbreviated weekday name (``Sun'')
1575
- - %A -- The full weekday name (``Sunday'')
1576
- - %b -- The abbreviated month name (``Jan'')
1577
- - %B -- The full month name (``January'')
1578
- - %c -- The preferred local date and time representation
1579
- - %d -- Day of the month (01..31)
1580
- - %D -- Day of the month (0..31)
1581
- - %h -- Hour of the day, 24-hour clock (0..23)
1582
- - %H -- Hour of the day, 24-hour clock (00..23)
1583
- - %i -- Hour of the day, 12-hour clock (1..12)
1584
- - %I -- Hour of the day, 12-hour clock (01..12)
1585
- - %j -- Day of the year (001..366)
1586
- - %m -- Month of the year (01..12)
1587
- - %M -- Minute of the hour (00..59)
1588
- - %p -- Meridian indicator (``AM'' or ``PM'')
1589
- - %S -- Second of the minute (00..60)
1590
- - %s -- Milliseconds of the second (000..999)
1591
- - %U -- Week number of the current year,
1592
- starting with the first Sunday as the first
1593
- day of the first week (00..53)
1594
- - %W -- Week number of the current year,
1595
- starting with the first Monday as the first
1596
- day of the first week (00..53)
1597
- - %w -- Day of the week (Sunday is 0, 0..6)
1598
- - %x -- Preferred representation for the date alone, no time
1599
- - %X -- Preferred representation for the time alone, no date
1600
- - %y -- Year without a century (00..99)
1601
- - %Y -- Year with century
1602
- - %Z -- Time zone (ISO 8601 formatted)
1603
- - %% -- Literal ``%'' character
1604
-
1605
- @param {String} format the format string
1606
- @return {String} the formatted string
1607
- */
1608
- toFormattedString: function(fmt) {
1609
- return this.constructor._toFormattedString(fmt, this._ms, this.timezone);
1610
- },
1611
-
1612
- /**
1613
- Formats the receiver according ISO 8601 standard. It is equivalent to
1614
- calling toFormattedString with the `'%Y-%m-%dT%H:%M:%S%Z'` format string.
1615
-
1616
- @return {String} the formatted string
1617
- */
1618
- toISO8601: function(){
1619
- return this.constructor._toFormattedString(SC.DATETIME_ISO8601, this._ms, this.timezone);
1620
- },
1621
-
1622
- /**
1623
- @private
1624
-
1625
- Creates a string representation of the receiver.
1626
-
1627
- (Debuggers often call the `toString` method. Because of the way
1628
- `SC.DateTime` is designed, calling `SC.DateTime._toFormattedString` would
1629
- have a nasty side effect. We shouldn't therefore call any of
1630
- `SC.DateTime`'s methods from `toString`)
1631
-
1632
- @returns {String}
1633
- */
1634
- toString: function() {
1635
- return "UTC: " +
1636
- new Date(this._ms).toUTCString() +
1637
- ", timezone: " +
1638
- this.timezone;
1639
- },
1640
-
1641
- /**
1642
- Returns `YES` if the passed `SC.DateTime` is equal to the receiver, ie: if their
1643
- number of milliseconds since January, 1st 1970 00:00:00.0 UTC are equal.
1644
- This is the preferred method for testing equality.
1645
-
1646
- @see SC.DateTime#compare
1647
- @param {SC.DateTime} aDateTime the DateTime to compare to
1648
- @returns {Boolean}
1649
- */
1650
- isEqual: function(aDateTime) {
1651
- return SC.DateTime.compare(this, aDateTime) === 0;
1652
- },
1653
-
1654
- /**
1655
- Returns a copy of the receiver. Because of the way `SC.DateTime` is designed,
1656
- it just returns the receiver.
1657
-
1658
- @returns {SC.DateTime}
1659
- */
1660
- copy: function() {
1661
- return this;
1662
- },
1663
-
1664
- /**
1665
- Returns a copy of the receiver with the timezone set to the passed
1666
- timezone. The returned value is equal to the receiver (ie `SC.Compare`
1667
- returns 0), it is just the timezone representation that changes.
1668
-
1669
- If you don't pass any argument, the target timezone is assumed to be 0,
1670
- ie UTC.
1671
-
1672
- Note that this method does not change the underlying position in time,
1673
- but only the time zone in which it is displayed. In other words, the underlying
1674
- number of milliseconds since Jan 1, 1970 does not change.
1675
-
1676
- @return {SC.DateTime}
1677
- */
1678
- toTimezone: function(timezone) {
1679
- if (timezone === undefined) timezone = 0;
1680
- return this.advance({ timezone: timezone - this.timezone });
1681
- }
1682
-
1683
- });
1684
-
1685
- SC.DateTime.reopenClass(SC.Comparable,
1686
- /** @scope SC.DateTime */ {
1687
-
1688
- /**
1689
- The default format (ISO 8601) in which DateTimes are stored in a record.
1690
- Change this value if your backend sends and receives dates in another
1691
- format.
1692
-
1693
- This value can also be customized on a per-attribute basis with the format
1694
- property. For example:
1695
-
1696
- SC.Record.attr(SC.DateTime, { format: '%d/%m/%Y %H:%M:%S' })
1697
-
1698
- @type String
1699
- @default SC.DATETIME_ISO8601
1700
- */
1701
- recordFormat: SC.DATETIME_ISO8601,
1702
-
1703
- /**
1704
- @type Array
1705
- @default ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
1706
- */
1707
- dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1708
-
1709
- /**
1710
- @private
1711
-
1712
- The English day names used for the 'lastMonday', 'nextTuesday', etc., getters.
1713
-
1714
- @type Array
1715
- */
1716
- _englishDayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
1717
-
1718
- /**
1719
- @type Array
1720
- @default ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
1721
- */
1722
- abbreviatedDayNames: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
1723
-
1724
- /**
1725
- @type Array
1726
- @default ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
1727
- */
1728
- monthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
1729
-
1730
- /**
1731
- @type Array
1732
- @default ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
1733
- */
1734
- abbreviatedMonthNames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
1735
-
1736
- /**
1737
- @private
1738
-
1739
- The unique internal `Date` object used to make computations. Better
1740
- performance is obtained by having only one Date object for the whole
1741
- application and manipulating it with `setTime()` and `getTime()`.
1742
-
1743
- Note that since this is used for internal calculations across many
1744
- `SC.DateTime` instances, it is not guaranteed to store the date/time that
1745
- any one `SC.DateTime` instance represents. So it might be that
1746
-
1747
- this._date.getTime() !== this._ms
1748
-
1749
- Be sure to set it before using for internal calculations if necessary.
1750
-
1751
- @type Date
1752
- */
1753
- _date: new Date(),
1754
-
1755
- /**
1756
- @private
1757
-
1758
- The offset, in minutes, between UTC and the currently manipulated
1759
- `SC.DateTime` instance.
1760
-
1761
- @type Integer
1762
- */
1763
- _tz: 0,
1764
-
1765
- /**
1766
- The offset, in minutes, between UTC and the local system time. This
1767
- property is computed at loading time and should never be changed.
1768
-
1769
- @type Integer
1770
- @default new Date().getTimezoneOffset()
1771
- @constant
1772
- */
1773
- timezone: new Date().getTimezoneOffset(),
1774
-
1775
- /**
1776
- @private
1777
-
1778
- A cache of `SC.DateTime` instances. If you attempt to create a `SC.DateTime`
1779
- instance that has already been created, then it will return the cached
1780
- value.
1781
-
1782
- @type Array
1783
- */
1784
- _dt_cache: {},
1785
-
1786
- /**
1787
- @private
1788
-
1789
- The index of the lastest cached value. Used with `_DT_CACHE_MAX_LENGTH` to
1790
- limit the size of the cache.
1791
-
1792
- @type Integer
1793
- */
1794
- _dt_cache_index: -1,
1795
-
1796
- /**
1797
- @private
1798
-
1799
- The maximum length of `_dt_cache`. If this limit is reached, then the cache
1800
- is overwritten, starting with the oldest element.
1801
-
1802
- @type Integer
1803
- */
1804
- _DT_CACHE_MAX_LENGTH: 1000,
1805
-
1806
- /**
1807
- @private
1808
-
1809
- Both args are optional, but will only overwrite `_date` and `_tz` if
1810
- defined. This method does not affect the DateTime instance's actual time,
1811
- but simply initializes the one `_date` instance to a time relevant for a
1812
- calculation. (`this._date` is just a resource optimization)
1813
-
1814
- This is mainly used as a way to store a recursion starting state during
1815
- internal calculations.
1816
-
1817
- 'milliseconds' is time since Jan 1, 1970.
1818
- 'timezone' is the current time zone we want to be working in internally.
1819
-
1820
- Returns a hash of the previous milliseconds and time zone in case they
1821
- are wanted for later restoration.
1822
- */
1823
- _setCalcState: function(ms, timezone) {
1824
- var previous = {
1825
- milliseconds: this._date.getTime(),
1826
- timezone: this._tz
1827
- };
1828
-
1829
- if (ms !== undefined) this._date.setTime(ms);
1830
- if (timezone !== undefined) this._tz = timezone;
1831
-
1832
- return previous;
1833
- },
1834
-
1835
- /**
1836
- @private
1837
-
1838
- By this time, any time zone setting on 'hash' will be ignored.
1839
- 'timezone' will be used, or the last this._tz.
1840
- */
1841
- _setCalcStateFromHash: function(hash, timezone) {
1842
- var tz = (timezone !== undefined) ? timezone : this._tz; // use the last-known time zone if necessary
1843
- var ms = this._toMilliseconds(hash, this._ms, tz); // convert the hash (local to specified time zone) to milliseconds (in UTC)
1844
- return this._setCalcState(ms, tz); // now call the one we really wanted
1845
- },
1846
-
1847
- /**
1848
- @private
1849
- @see SC.DateTime#unknownProperty
1850
- */
1851
- _get: function(key, start, timezone) {
1852
- var ms, tz, doy, m, y, firstDayOfWeek, dayOfWeek, dayOfYear, prefix, suffix;
1853
- var currentWeekday, targetWeekday;
1854
- var d = this._date;
1855
- var originalTime, v = null;
1856
-
1857
- // Set up an absolute date/time using the given milliseconds since Jan 1, 1970.
1858
- // Only do it if we're given a time value, though, otherwise we want to use the
1859
- // last one we had because this `_get()` method is recursive.
1860
- //
1861
- // Note that because these private time calc methods are recursive, and because all DateTime instances
1862
- // share an internal this._date and `this._tz` state for doing calculations, methods
1863
- // that modify `this._date` or `this._tz` should restore the last state before exiting
1864
- // to avoid obscure calculation bugs. So we save the original state here, and restore
1865
- // it before returning at the end.
1866
- originalTime = this._setCalcState(start, timezone); // save so we can restore it to how it was before we got here
1867
-
1868
- // Check this first because it is an absolute value -- no tweaks necessary when calling for milliseconds
1869
- if (key === 'milliseconds') {
1870
- v = d.getTime();
1871
- }
1872
- else if (key === 'timezone') {
1873
- v = this._tz;
1874
- }
1875
-
1876
- // 'nextWeekday' or 'lastWeekday'.
1877
- // We want to do this calculation in local time, before shifting UTC below.
1878
- if (v === null) {
1879
- prefix = key.slice(0, 4);
1880
- suffix = key.slice(4);
1881
- if (prefix === 'last' || prefix === 'next') {
1882
- currentWeekday = this._get('dayOfWeek', start, timezone);
1883
- targetWeekday = this._englishDayNames.indexOf(suffix);
1884
- if (targetWeekday >= 0) {
1885
- var delta = targetWeekday - currentWeekday;
1886
- if (prefix === 'last' && delta >= 0) delta -= 7;
1887
- if (prefix === 'next' && delta < 0) delta += 7;
1888
- this._advance({ day: delta }, start, timezone);
1889
- v = this._createFromCurrentState();
1890
- }
1891
- }
1892
- }
1893
-
1894
- if (v === null) {
1895
- // need to adjust for alternate display time zone.
1896
- // Before calculating, we need to get everything into a common time zone to
1897
- // negate the effects of local machine time (so we can use all the 'getUTC...() methods on Date).
1898
- if (timezone !== undefined) {
1899
- this._setCalcState(d.getTime() - (timezone * 60000), 0); // make this instance's time zone the new UTC temporarily
1900
- }
1901
-
1902
- // simple keys
1903
- switch (key) {
1904
- case 'year':
1905
- v = d.getUTCFullYear(); //TODO: investigate why some libraries do getFullYear().toString() or getFullYear()+""
1906
- break;
1907
- case 'month':
1908
- v = d.getUTCMonth()+1; // January is 0 in JavaScript
1909
- break;
1910
- case 'day':
1911
- v = d.getUTCDate();
1912
- break;
1913
- case 'dayOfWeek':
1914
- v = d.getUTCDay();
1915
- break;
1916
- case 'hour':
1917
- v = d.getUTCHours();
1918
- break;
1919
- case 'minute':
1920
- v = d.getUTCMinutes();
1921
- break;
1922
- case 'second':
1923
- v = d.getUTCSeconds();
1924
- break;
1925
- case 'millisecond':
1926
- v = d.getUTCMilliseconds();
1927
- break;
1928
- }
1929
-
1930
- // isLeapYear
1931
- if ((v === null) && (key === 'isLeapYear')) {
1932
- y = this._get('year');
1933
- v = (y%4 === 0 && y%100 !== 0) || y%400 === 0;
1934
- }
1935
-
1936
- // daysInMonth
1937
- if ((v === null) && (key === 'daysInMonth')) {
1938
- switch (this._get('month')) {
1939
- case 4:
1940
- case 6:
1941
- case 9:
1942
- case 11:
1943
- v = 30;
1944
- break;
1945
- case 2:
1946
- v = this._get('isLeapYear') ? 29 : 28;
1947
- break;
1948
- default:
1949
- v = 31;
1950
- break;
1951
- }
1952
- }
1953
-
1954
- // dayOfYear
1955
- if ((v === null) && (key === 'dayOfYear')) {
1956
- ms = d.getTime(); // save time
1957
- doy = this._get('day');
1958
- this._setCalcStateFromHash({ day: 1 });
1959
- for (m = this._get('month') - 1; m > 0; m--) {
1960
- this._setCalcStateFromHash({ month: m });
1961
- doy += this._get('daysInMonth');
1962
- }
1963
- d.setTime(ms); // restore time
1964
- v = doy;
1965
- }
1966
-
1967
- // week, week0 or week1
1968
- if ((v === null) && (key.slice(0, 4) === 'week')) {
1969
- // firstDayOfWeek should be 0 (Sunday) or 1 (Monday)
1970
- firstDayOfWeek = key.length === 4 ? 1 : parseInt(key.slice('4'), 10);
1971
- dayOfWeek = this._get('dayOfWeek');
1972
- dayOfYear = this._get('dayOfYear') - 1;
1973
- if (firstDayOfWeek === 0) {
1974
- v = parseInt((dayOfYear - dayOfWeek + 7) / 7, 10);
1975
- }
1976
- else {
1977
- v = parseInt((dayOfYear - (dayOfWeek - 1 + 7) % 7 + 7) / 7, 10);
1978
- }
1979
- }
1980
- }
1981
-
1982
- // restore the internal calculation state in case someone else was in the
1983
- // middle of a calculation (we might be recursing).
1984
- this._setCalcState(originalTime.milliseconds, originalTime.timezone);
1985
-
1986
- return v;
1987
- },
1988
-
1989
- /**
1990
- @private
1991
-
1992
- Sets the internal calculation state to something specified.
1993
- */
1994
- _adjust: function(options, start, timezone, resetCascadingly) {
1995
- var opts = options ? copy(options) : {};
1996
- var ms = this._toMilliseconds(options, start, timezone, resetCascadingly);
1997
- this._setCalcState(ms, timezone);
1998
- return this; // for chaining
1999
- },
2000
-
2001
- /**
2002
- @private
2003
- @see SC.DateTime#advance
2004
- */
2005
- _advance: function(options, start, timezone) {
2006
- var opts = options ? copy(options) : {};
2007
- var tz;
2008
-
2009
- for (var key in opts) {
2010
- opts[key] += this._get(key, start, timezone);
2011
- }
2012
-
2013
- // The time zone can be advanced by a delta as well, so try to use the
2014
- // new value if there is one.
2015
- tz = (opts.timezone !== undefined) ? opts.timezone : timezone; // watch out for zero, which is acceptable as a time zone
2016
-
2017
- return this._adjust(opts, start, tz, NO);
2018
- },
2019
-
2020
- /*
2021
- @private
2022
-
2023
- Converts a standard date/time options hash to an integer representing that position
2024
- in time relative to Jan 1, 1970
2025
- */
2026
- _toMilliseconds: function(options, start, timezone, resetCascadingly) {
2027
- var opts = options ? copy(options) : {};
2028
- var d = this._date;
2029
- var previousMilliseconds = d.getTime(); // rather than create a new Date object, we'll reuse the instance we have for calculations, then restore it
2030
- var ms, tz;
2031
-
2032
- // Initialize our internal for-calculations Date object to our current date/time.
2033
- // Note that this object was created in the local machine time zone, so when we set
2034
- // its params later, it will be assuming these values to be in the same time zone as it is.
2035
- // It's ok for start to be null, in which case we'll just keep whatever we had in 'd' before.
2036
- if (!SC.none(start)) {
2037
- d.setTime(start); // using milliseconds here specifies an absolute location in time, regardless of time zone, so that's nice
2038
- }
2039
-
2040
- // We have to get all time expressions, both in 'options' (assume to be in time zone 'timezone')
2041
- // and in 'd', to the same time zone before we can any calculations correctly. So because the Date object provides
2042
- // a suite of UTC getters and setters, we'll temporarily redefine 'timezone' as our new
2043
- // 'UTC', so we don't have to worry about local machine time. We do this by subtracting
2044
- // milliseconds for the time zone offset. Then we'll do all our calculations, then convert
2045
- // it back to real UTC.
2046
-
2047
- // (Zero time zone is considered a valid value.)
2048
- tz = (timezone !== undefined) ? timezone : (this.timezone !== undefined) ? this.timezone : 0;
2049
- d.setTime(d.getTime() - (tz * 60000)); // redefine 'UTC' to establish a new local absolute so we can use all the 'getUTC...()' Date methods
2050
-
2051
- // the time options (hour, minute, sec, millisecond)
2052
- // reset cascadingly (see documentation)
2053
- if (resetCascadingly === undefined || resetCascadingly === YES) {
2054
- if ( !SC.none(opts.hour) && SC.none(opts.minute)) {
2055
- opts.minute = 0;
2056
- }
2057
- if (!(SC.none(opts.hour) && SC.none(opts.minute))
2058
- && SC.none(opts.second)) {
2059
- opts.second = 0;
2060
- }
2061
- if (!(SC.none(opts.hour) && SC.none(opts.minute) && SC.none(opts.second))
2062
- && SC.none(opts.millisecond)) {
2063
- opts.millisecond = 0;
2064
- }
2065
- }
2066
-
2067
- // Get the current values for any not provided in the options hash.
2068
- // Since everything is in 'UTC' now, use the UTC accessors. We do this because,
2069
- // according to javascript Date spec, you have to set year, month, and day together
2070
- // if you're setting any one of them. So we'll use the provided Date.UTC() method
2071
- // to get milliseconds, and we need to get any missing values first...
2072
- if (SC.none(opts.year)) opts.year = d.getUTCFullYear();
2073
- if (SC.none(opts.month)) opts.month = d.getUTCMonth() + 1; // January is 0 in JavaScript
2074
- if (SC.none(opts.day)) opts.day = d.getUTCDate();
2075
- if (SC.none(opts.hour)) opts.hour = d.getUTCHours();
2076
- if (SC.none(opts.minute)) opts.minute = d.getUTCMinutes();
2077
- if (SC.none(opts.second)) opts.second = d.getUTCSeconds();
2078
- if (SC.none(opts.millisecond)) opts.millisecond = d.getUTCMilliseconds();
2079
-
2080
- // Ask the JS Date to calculate milliseconds for us (still in redefined UTC). It
2081
- // is best to set them all together because, for example, a day value means different things
2082
- // to the JS Date object depending on which month or year it is. It can now handle that stuff
2083
- // internally as it's made to do.
2084
- ms = Date.UTC(opts.year, opts.month - 1, opts.day, opts.hour, opts.minute, opts.second, opts.millisecond);
2085
-
2086
- // Now that we've done all our calculations in a common time zone, add back the offset
2087
- // to move back to real UTC.
2088
- d.setTime(ms + (tz * 60000));
2089
- ms = d.getTime(); // now get the corrected milliseconds value
2090
-
2091
- // Restore what was there previously before leaving in case someone called this method
2092
- // in the middle of another calculation.
2093
- d.setTime(previousMilliseconds);
2094
-
2095
- return ms;
2096
- },
2097
-
2098
- /**
2099
- Returns a new `SC.DateTime` object advanced according the the given parameters.
2100
- The parameters can be:
2101
-
2102
- - none, to create a `SC.DateTime` instance initialized to the current
2103
- date and time in the local timezone,
2104
- - a integer, the number of milliseconds since
2105
- January, 1st 1970 00:00:00.0 UTC
2106
- - a options hash that can contain any of the following properties: year,
2107
- month, day, hour, minute, second, millisecond, timezone
2108
-
2109
- Note that if you attempt to create a `SC.DateTime` instance that has already
2110
- been created, then, for performance reasons, a cached value may be
2111
- returned.
2112
-
2113
- The timezone option is the offset, in minutes, between UTC and local time.
2114
- If you don't pass a timezone option, the date object is created in the
2115
- local timezone. If you want to create a UTC+2 (CEST) date, for example,
2116
- then you should pass a timezone of -120.
2117
-
2118
- @param options one of the three kind of parameters descibed above
2119
- @returns {SC.DateTime} the SC.DateTime instance that corresponds to the
2120
- passed parameters, possibly fetched from cache
2121
- */
2122
- create: function() {
2123
- var arg = arguments.length === 0 ? {} : arguments[0];
2124
- var timezone;
2125
-
2126
- // if simply milliseconds since Jan 1, 1970 are given, just use those
2127
- if (SC.typeOf(arg) === 'number') {
2128
- arg = { milliseconds: arg };
2129
- }
2130
-
2131
- // Default to local machine time zone if none is given
2132
- timezone = (arg.timezone !== undefined) ? arg.timezone : this.timezone;
2133
- if (timezone === undefined) timezone = 0;
2134
-
2135
- // Desired case: create with milliseconds if we have them.
2136
- // If we don't, convert what we have to milliseconds and recurse.
2137
- if (!SC.none(arg.milliseconds)) {
2138
-
2139
- // quick implementation of a FIFO set for the cache
2140
- var key = 'nu' + arg.milliseconds + timezone, cache = this._dt_cache;
2141
- var ret = cache[key];
2142
- if (!ret) {
2143
- var previousKey, idx = this._dt_cache_index;
2144
- ret = cache[key] = this._super({ _ms: arg.milliseconds, timezone: timezone });
2145
- idx = this._dt_cache_index = (idx + 1) % this._DT_CACHE_MAX_LENGTH;
2146
- previousKey = cache[idx];
2147
- if (previousKey !== undefined && cache[previousKey]) delete cache[previousKey];
2148
- cache[idx] = key;
2149
- }
2150
- return ret;
2151
- }
2152
- // otherwise, convert what we have to milliseconds and try again
2153
- else {
2154
- var now = new Date();
2155
-
2156
- return this.create({ // recursive call with new arguments
2157
- milliseconds: this._toMilliseconds(arg, now.getTime(), timezone, arg.resetCascadingly),
2158
- timezone: timezone
2159
- });
2160
- }
2161
- },
2162
-
2163
- /**
2164
- @private
2165
-
2166
- Calls the `create()` method with the current internal `_date` value.
2167
-
2168
- @return {SC.DateTime} the SC.DateTime instance returned by create()
2169
- */
2170
- _createFromCurrentState: function() {
2171
- return this.create({
2172
- milliseconds: this._date.getTime(),
2173
- timezone: this._tz
2174
- });
2175
- },
2176
-
2177
- /**
2178
- Returns a `SC.DateTime` object created from a given string parsed with a given
2179
- format. Returns `null` if the parsing fails.
2180
-
2181
- @see SC.DateTime#toFormattedString for a description of the format parameter
2182
- @param {String} str the string to parse
2183
- @param {String} fmt the format to parse the string with
2184
- @returns {DateTime} the DateTime corresponding to the string parameter
2185
- */
2186
- parse: function(str, fmt) {
2187
- // Declared as an object not a literal since in some browsers the literal
2188
- // retains state across function calls
2189
- var re = new RegExp('(?:%([aAbBcdDhHiIjmMpsSUWwxXyYZ%])|(.))', "g");
2190
- var d, parts, opts = {}, check = {}, scanner = Scanner.create({string: str});
2191
-
2192
- if (SC.none(fmt)) fmt = SC.DATETIME_ISO8601;
2193
-
2194
- try {
2195
- while ((parts = re.exec(fmt)) !== null) {
2196
- switch(parts[1]) {
2197
- case 'a': check.dayOfWeek = scanner.scanArray(this.abbreviatedDayNames); break;
2198
- case 'A': check.dayOfWeek = scanner.scanArray(this.dayNames); break;
2199
- case 'b': opts.month = scanner.scanArray(this.abbreviatedMonthNames) + 1; break;
2200
- case 'B': opts.month = scanner.scanArray(this.monthNames) + 1; break;
2201
- case 'c': throw new Error("%c is not implemented");
2202
- case 'd':
2203
- case 'D': opts.day = scanner.scanInt(1, 2); break;
2204
- case 'h':
2205
- case 'H': opts.hour = scanner.scanInt(1, 2); break;
2206
- case 'i':
2207
- case 'I': opts.hour = scanner.scanInt(1, 2); break;
2208
- case 'j': throw new Error("%j is not implemented");
2209
- case 'm': opts.month = scanner.scanInt(1, 2); break;
2210
- case 'M': opts.minute = scanner.scanInt(1, 2); break;
2211
- case 'p': opts.meridian = scanner.scanArray(['AM', 'PM']); break;
2212
- case 'S': opts.second = scanner.scanInt(1, 2); break;
2213
- case 's': opts.millisecond = scanner.scanInt(1, 3); break;
2214
- case 'U': throw new Error("%U is not implemented");
2215
- case 'W': throw new Error("%W is not implemented");
2216
- case 'w': throw new Error("%w is not implemented");
2217
- case 'x': throw new Error("%x is not implemented");
2218
- case 'X': throw new Error("%X is not implemented");
2219
- case 'y': opts.year = scanner.scanInt(2); opts.year += (opts.year > 70 ? 1900 : 2000); break;
2220
- case 'Y': opts.year = scanner.scanInt(4); break;
2221
- case 'Z':
2222
- var modifier = scanner.scan(1);
2223
- if (modifier === 'Z') {
2224
- opts.timezone = 0;
2225
- } else if (modifier === '+' || modifier === '-' ) {
2226
- var h = scanner.scanInt(2);
2227
- if (scanner.scan(1) !== ':') scanner.scan(-1);
2228
- var m = scanner.scanInt(2);
2229
- opts.timezone = (modifier === '+' ? -1 : 1) * (h*60 + m);
2230
- }
2231
- break;
2232
- case '%': scanner.skipString('%'); break;
2233
- default: scanner.skipString(parts[0]); break;
2234
- }
2235
- }
2236
- } catch (e) {
2237
- SC.Logger.log('SC.DateTime.createFromString ' + e.toString());
2238
- return null;
2239
- }
2240
-
2241
- if (!SC.none(opts.meridian) && !SC.none(opts.hour)) {
2242
- if (opts.meridian === 1) opts.hour = (opts.hour + 12) % 24;
2243
- delete opts.meridian;
2244
- }
2245
-
2246
- d = SC.DateTime.create(opts);
2247
-
2248
- if (!SC.none(check.dayOfWeek) && get(d,'dayOfWeek') !== check.dayOfWeek) {
2249
- return null;
2250
- }
2251
-
2252
- return d;
2253
- },
2254
-
2255
- /**
2256
- @private
2257
-
2258
- Converts the x parameter into a string padded with 0s so that the string’s
2259
- length is at least equal to the len parameter.
2260
-
2261
- @param {Object} x the object to convert to a string
2262
- @param {Integer} the minimum length of the returned string
2263
- @returns {String} the padded string
2264
- */
2265
- _pad: function(x, len) {
2266
- var str = '' + x;
2267
- if (len === undefined) len = 2;
2268
- while (str.length < len) str = '0' + str;
2269
- return str;
2270
- },
2271
-
2272
- /**
2273
- @private
2274
- @see SC.DateTime#_toFormattedString
2275
- */
2276
- __toFormattedString: function(part, start, timezone) {
2277
- var hour, offset;
2278
-
2279
- // Note: all calls to _get() here should include only one
2280
- // argument, since _get() is built for recursion and behaves differently
2281
- // if arguments 2 and 3 are included.
2282
- //
2283
- // This method is simply a helper for this._toFormattedString() (one underscore);
2284
- // this is only called from there, and _toFormattedString() has already
2285
- // set up the appropriate internal date/time/timezone state for it.
2286
-
2287
- switch(part[1]) {
2288
- case 'a': return this.abbreviatedDayNames[this._get('dayOfWeek')];
2289
- case 'A': return this.dayNames[this._get('dayOfWeek')];
2290
- case 'b': return this.abbreviatedMonthNames[this._get('month')-1];
2291
- case 'B': return this.monthNames[this._get('month')-1];
2292
- case 'c': return this._date.toString();
2293
- case 'd': return this._pad(this._get('day'));
2294
- case 'D': return this._get('day');
2295
- case 'h': return this._get('hour');
2296
- case 'H': return this._pad(this._get('hour'));
2297
- case 'i':
2298
- hour = this._get('hour');
2299
- return (hour === 12 || hour === 0) ? 12 : (hour + 12) % 12;
2300
- case 'I':
2301
- hour = this._get('hour');
2302
- return this._pad((hour === 12 || hour === 0) ? 12 : (hour + 12) % 12);
2303
- case 'j': return this._pad(this._get('dayOfYear'), 3);
2304
- case 'm': return this._pad(this._get('month'));
2305
- case 'M': return this._pad(this._get('minute'));
2306
- case 'p': return this._get('hour') > 11 ? 'PM' : 'AM';
2307
- case 'S': return this._pad(this._get('second'));
2308
- case 's': return this._pad(this._get('millisecond'), 3);
2309
- case 'u': return this._pad(this._get('utc')); //utc
2310
- case 'U': return this._pad(this._get('week0'));
2311
- case 'W': return this._pad(this._get('week1'));
2312
- case 'w': return this._get('dayOfWeek');
2313
- case 'x': return this._date.toDateString();
2314
- case 'X': return this._date.toTimeString();
2315
- case 'y': return this._pad(this._get('year') % 100);
2316
- case 'Y': return this._get('year');
2317
- case 'Z':
2318
- offset = -1 * timezone;
2319
- return (offset >= 0 ? '+' : '-')
2320
- + this._pad(parseInt(Math.abs(offset)/60, 10))
2321
- + ':'
2322
- + this._pad(Math.abs(offset)%60);
2323
- case '%': return '%';
2324
- }
2325
- },
2326
-
2327
- /**
2328
- @private
2329
- @see SC.DateTime#toFormattedString
2330
- */
2331
- _toFormattedString: function(format, start, timezone) {
2332
- var that = this;
2333
- var tz = (timezone !== undefined) ? timezone : (this.timezone !== undefined) ? this.timezone : 0;
2334
-
2335
- // need to move into local time zone for these calculations
2336
- this._setCalcState(start - (timezone * 60000), 0); // so simulate a shifted 'UTC' time
2337
-
2338
- return format.replace(/\%([aAbBcdDhHiIjmMpsSUWwxXyYZ\%])/g, function() {
2339
- var v = that.__toFormattedString.call(that, arguments, start, timezone);
2340
- return v;
2341
- });
2342
- },
2343
-
2344
- /**
2345
- This will tell you which of the two passed `DateTime` is greater by
2346
- comparing their number of milliseconds since
2347
- January, 1st 1970 00:00:00.0 UTC.
2348
-
2349
- @param {SC.DateTime} a the first DateTime instance
2350
- @param {SC.DateTime} b the second DateTime instance
2351
- @returns {Integer} -1 if a < b,
2352
- +1 if a > b,
2353
- 0 if a == b
2354
- */
2355
- compare: function(a, b) {
2356
- var ma = get(a, 'milliseconds');
2357
- var mb = get(b, 'milliseconds');
2358
- return ma < mb ? -1 : ma === mb ? 0 : 1;
2359
- },
2360
-
2361
- /**
2362
- This will tell you which of the two passed DateTime is greater
2363
- by only comparing the date parts of the passed objects. Only dates
2364
- with the same timezone can be compared.
2365
-
2366
- @param {SC.DateTime} a the first DateTime instance
2367
- @param {SC.DateTime} b the second DateTime instance
2368
- @returns {Integer} -1 if a < b,
2369
- +1 if a > b,
2370
- 0 if a == b
2371
- @throws {SC.DATETIME_COMPAREDATE_TIMEZONE_ERROR} if the passed arguments
2372
- don't have the same timezone
2373
- */
2374
- compareDate: function(a, b) {
2375
- if (get(a, 'timezone') !== get(b,'timezone')) {
2376
- throw new Error(SC.DATETIME_COMPAREDATE_TIMEZONE_ERROR);
2377
- }
2378
-
2379
- var ma = get(a.adjust({hour: 0}), 'milliseconds');
2380
- var mb = get(b.adjust({hour: 0}), 'milliseconds');
2381
- return ma < mb ? -1 : ma === mb ? 0 : 1;
2382
- }
2383
-
2384
- });
2385
-
2386
- /**
2387
- Adds a transform to format the DateTime value to a String value according
2388
- to the passed format string.
2389
-
2390
- valueBinding: SC.Binding.dateTime('%Y-%m-%d %H:%M:%S')
2391
- .from('MyApp.myController.myDateTime');
2392
-
2393
- @param {String} format format string
2394
- @returns {SC.Binding} this
2395
- */
2396
- SC.Binding.dateTime = function(format) {
2397
- return this.transform(function(value, binding) {
2398
- return value ? value.toFormattedString(format) : null;
2399
- });
2400
- };
2401
-
2402
-
2403
- })({});
2404
-
2405
-
2406
- (function(exports) {
2407
- // ==========================================================================
2408
- // Project: SproutCore DataStore
2409
- // Copyright: ©2010 Strobe Inc. and contributors
2410
- // License: Licensed under MIT license (see license.js)
2411
- // ==========================================================================
2412
-
2413
- })({});
2414
-
2415
- (function(exports) {
2416
- // ==========================================================================
2417
- // Project: SproutCore - JavaScript Application Framework
2418
- // Copyright: ©2006-2011 Strobe Inc. and contributors.
2419
- // Portions ©2008-2011 Apple Inc. All rights reserved.
2420
- // License: Licensed under MIT license (see license.js)
2421
- // ==========================================================================
2422
-
2423
1254
 
2424
1255
  var get = SC.get, set = SC.set, getPath = SC.getPath;
2425
1256
 
@@ -6584,7 +5415,7 @@ SC.ChildArray = SC.Object.extend(SC.Enumerable, SC.Array, SC.MutableEnumerable,
6584
5415
 
6585
5416
  this.arrayContentWillChange(0, oldLen, newLen);
6586
5417
  this._prevChildren = children;
6587
- this._childrenContentDidChange(0, oldLen, newLen);
5418
+ this._childrenContentDidChange(children, 0, oldLen, newLen);
6588
5419
 
6589
5420
  return this;
6590
5421
  },
@@ -6599,7 +5430,7 @@ SC.ChildArray = SC.Object.extend(SC.Enumerable, SC.Array, SC.MutableEnumerable,
6599
5430
  @param {Number} value
6600
5431
  @param {Number} rev
6601
5432
  */
6602
- _childrenContentDidChange: function(start, removedCount, addedCount) {
5433
+ _childrenContentDidChange: function(content, start, removedCount, addedCount) {
6603
5434
  this._records = null ; // clear cache
6604
5435
  this.arrayContentDidChange(start, removedCount, addedCount);
6605
5436
  },
@@ -7020,6 +5851,15 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.MutableEnumerable, SC.MutableA
7020
5851
  return this;
7021
5852
  },
7022
5853
 
5854
+ _inverseRecordDidLoad: function(obj, key, val) {
5855
+ var store = get(this, 'store');
5856
+ var id = store.idFor(obj.get("storeKey"));
5857
+ if(id) {
5858
+ obj.removeObserver("status", this, "_inverseRecordDidLoad");
5859
+ this.addInverseRecord(obj);
5860
+ }
5861
+ },
5862
+
7023
5863
  /**
7024
5864
  Called by the `ManyAttribute` whenever a record is added on the inverse
7025
5865
  of the relationship.
@@ -7030,8 +5870,15 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.MutableEnumerable, SC.MutableA
7030
5870
  addInverseRecord: function(inverseRecord) {
7031
5871
 
7032
5872
  if (!inverseRecord) return this;
7033
- var id = get(inverseRecord, 'id'),
7034
- storeIds = get(this, 'editableStoreIds'),
5873
+ var store = get(this, 'store');
5874
+ var id = store.idFor(inverseRecord.get("storeKey"));
5875
+
5876
+ if(!id) {
5877
+ inverseRecord.addObserver("status", this, "_inverseRecordDidLoad");
5878
+ return this;
5879
+ }
5880
+
5881
+ var storeIds = get(this, 'editableStoreIds'),
7035
5882
  orderBy = get(this, 'orderBy'),
7036
5883
  len = get(storeIds, 'length'),
7037
5884
  idx, record;
@@ -7101,7 +5948,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.MutableEnumerable, SC.MutableA
7101
5948
 
7102
5949
  if (prev) {
7103
5950
  prev.removeArrayObserver(this, {
7104
- willChange: this.arrayContentWillChange,
5951
+ willChange: this.arrayWillChange,
7105
5952
  didChange: f
7106
5953
  });
7107
5954
 
@@ -7111,11 +5958,12 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.MutableEnumerable, SC.MutableA
7111
5958
  }
7112
5959
 
7113
5960
  if (storeIds) {
7114
- storeIds.addArrayObserver(this, {
7115
- willChange: this.arrayContentWillChange,
7116
- didChange: f
7117
- });
7118
-
5961
+ if(!storeIds.get("hasArrayObservers")) {
5962
+ storeIds.addArrayObserver(this, {
5963
+ willChange: this.arrayWillChange,
5964
+ didChange: f
5965
+ });
5966
+ }
7119
5967
  newLen = get(storeIds, 'length');
7120
5968
  } else {
7121
5969
  newLen = 0;
@@ -7123,7 +5971,11 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.MutableEnumerable, SC.MutableA
7123
5971
 
7124
5972
  this.arrayContentWillChange(0, oldLen, newLen);
7125
5973
  this._prevStoreIds = storeIds;
7126
- this._storeIdsContentDidChange(0, oldLen, newLen);
5974
+ this._storeIdsContentDidChange(null, 0, oldLen, newLen);
5975
+ },
5976
+
5977
+ arrayWillChange: function(item, start, removedCount, addedCount) {
5978
+ this.arrayContentWillChange(start, removedCount, addedCount);
7127
5979
  },
7128
5980
 
7129
5981
  /** @private
@@ -7131,7 +5983,7 @@ SC.ManyArray = SC.Object.extend(SC.Enumerable, SC.MutableEnumerable, SC.MutableA
7131
5983
  dump any cached record lookup and then notify that the enumerable content
7132
5984
  has changed.
7133
5985
  */
7134
- _storeIdsContentDidChange: function(start, removedCount, addedCount) {
5986
+ _storeIdsContentDidChange: function(item, start, removedCount, addedCount) {
7135
5987
  this._records = null ; // clear cache
7136
5988
  this.arrayContentDidChange(start, removedCount, addedCount);
7137
5989
  },
@@ -8289,7 +7141,10 @@ SC.FixturesDataSource = SC.DataSource.extend(
8289
7141
  }
8290
7142
 
8291
7143
  if (get(this, 'simulateRemoteResponse')) {
8292
- this.invokeLater(this._fetch, get(this, 'latency'), store, query);
7144
+ var self = this;
7145
+ setTimeout(function() {
7146
+ self._fetch(store, query);
7147
+ }, get(this, 'latency'));
8293
7148
 
8294
7149
  } else this._fetch(store, query);
8295
7150
  },
@@ -8329,7 +7184,10 @@ SC.FixturesDataSource = SC.DataSource.extend(
8329
7184
  if (!ret) return ret ;
8330
7185
 
8331
7186
  if (get(this, 'simulateRemoteResponse')) {
8332
- this.invokeLater(this._retrieveRecords, latency, store, storeKeys);
7187
+ var self = this;
7188
+ setTimeout(function() {
7189
+ self._retrieveRecords(store, storeKeys);
7190
+ }, latency);
8333
7191
  } else this._retrieveRecords(store, storeKeys);
8334
7192
 
8335
7193
  return ret ;
@@ -8360,7 +7218,10 @@ SC.FixturesDataSource = SC.DataSource.extend(
8360
7218
  if (!ret) return ret ;
8361
7219
 
8362
7220
  if (get(this, 'simulateRemoteResponse')) {
8363
- this.invokeLater(this._updateRecords, latency, store, storeKeys);
7221
+ var self = this;
7222
+ setTimeout(function() {
7223
+ self._updateRecords(store, storeKeys);
7224
+ }, latency);
8364
7225
  } else this._updateRecords(store, storeKeys);
8365
7226
 
8366
7227
  return ret ;
@@ -8386,7 +7247,10 @@ SC.FixturesDataSource = SC.DataSource.extend(
8386
7247
  var latency = get(this, 'latency');
8387
7248
 
8388
7249
  if (get(this, 'simulateRemoteResponse')) {
8389
- this.invokeLater(this._createRecords, latency, store, storeKeys);
7250
+ var self = this;
7251
+ setTimeout(function() {
7252
+ self._createRecords(store, storeKeys);
7253
+ }, latency);
8390
7254
  } else this._createRecords(store, storeKeys);
8391
7255
 
8392
7256
  return YES ;
@@ -8420,7 +7284,10 @@ SC.FixturesDataSource = SC.DataSource.extend(
8420
7284
  if (!ret) return ret ;
8421
7285
 
8422
7286
  if (get(this, 'simulateRemoteResponse')) {
8423
- this.invokeLater(this._destroyRecords, latency, store, storeKeys);
7287
+ var self;
7288
+ setTimeout(function() {
7289
+ self._destroyRecords(store, storeKeys);
7290
+ }, latency);
8424
7291
  } else this._destroyRecords(store, storeKeys);
8425
7292
 
8426
7293
  return ret ;