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.
- data/lib/rasputin/handlebars/handlebars.js +75 -24
- data/lib/rasputin/handlebars/template.rb +1 -1
- data/lib/rasputin/version.rb +1 -1
- data/rasputin.gemspec +1 -2
- data/vendor/assets/javascripts/sproutcore-datastore.js +54 -1187
- data/vendor/assets/javascripts/sproutcore-datetime.js +1177 -0
- data/vendor/assets/javascripts/sproutcore.js +106 -102
- metadata +20 -8
@@ -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(){
|
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
|
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
|
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,
|
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
|
-
|
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
|
-
|
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
|
1133
|
-
|
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 + " = " +
|
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
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
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
|
data/lib/rasputin/version.rb
CHANGED
data/rasputin.gemspec
CHANGED
@@ -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
|
-
|
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
|
7034
|
-
|
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.
|
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.
|
7115
|
-
|
7116
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 ;
|