From be7d20cfb11d4ba15ba7ef572321f3afeba9063c Mon Sep 17 00:00:00 2001 From: "Adam St. John" Date: Sun, 13 May 2012 13:30:38 -0400 Subject: [PATCH] Working on animations for months on previous and next. --- examples/chronos.css | 19 +- lib/jquery.transition.js | 1683 +++++++++++++++++++++++++++++++++++ spec/panelMonth-spec.coffee | 54 +- spec/picker-spec.coffee | 137 ++- src/panelMonth.coffee | 34 +- src/picker.coffee | 175 ++-- src/scss/chronos.scss | 27 +- 7 files changed, 2016 insertions(+), 113 deletions(-) create mode 100644 lib/jquery.transition.js diff --git a/examples/chronos.css b/examples/chronos.css index 3721b6e..6b881e9 100644 --- a/examples/chronos.css +++ b/examples/chronos.css @@ -1,10 +1,11 @@ .chronos_picker { - width: 248px; + width: 238px; background-color: #3dacff; color: black; -webkit-border-radius: 10px; -moz-border-radius: 10px; border-radius: 10px; + overflow: hidden; /** * HEADER */ @@ -15,11 +16,17 @@ * COMMON */ } .chronos_picker .body { - padding: 0px 5px 5px 5px; } + width: 714px; + position: relative; + left: -238px; } + .chronos_picker .body_prev, .chronos_picker .body_curr, .chronos_picker .body_next { + width: 238px; + float: left; + position: relative; } .chronos_picker .header { font-weight: bold; } .chronos_picker .header .title { - width: 178px; + width: 168px; text-align: center; } .chronos_picker .header .title, .chronos_picker .header .previous, .chronos_picker .header .next { display: inline-block; @@ -82,14 +89,14 @@ -moz-border-radius: 5px; border-radius: 5px; cursor: pointer; } + .chronos_picker .monthBody .day.today { + color: white; + font-weight: bold; } .chronos_picker .monthBody .day:hover, .chronos_picker .monthBody .day.selected { background-color: #004070; color: white; } .chronos_picker .monthBody .day.otherMonth { color: #e1dbff; } - .chronos_picker .monthBody .day.today { - color: white; - font-weight: bold; } .chronos_picker .monthHeader { font-weight: bold; } .chronos_picker .day { diff --git a/lib/jquery.transition.js b/lib/jquery.transition.js new file mode 100644 index 0000000..5e7ffb0 --- /dev/null +++ b/lib/jquery.transition.js @@ -0,0 +1,1683 @@ + + + + + + + + + jquery.transition.js/jquery.transition.js at master · louisremi/jquery.transition.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+
+
+
+ + + + + + +

+ public + + + / + jquery.transition.js +

+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + +
+ + louisremi + + + + + +
+ +
+
+ +
+
+
+
+ + 100644 + 871 lines (724 sloc) + 25.626 kb +
+ +
+
+ + + + + +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
+120
+121
+122
+123
+124
+125
+126
+127
+128
+129
+130
+131
+132
+133
+134
+135
+136
+137
+138
+139
+140
+141
+142
+143
+144
+145
+146
+147
+148
+149
+150
+151
+152
+153
+154
+155
+156
+157
+158
+159
+160
+161
+162
+163
+164
+165
+166
+167
+168
+169
+170
+171
+172
+173
+174
+175
+176
+177
+178
+179
+180
+181
+182
+183
+184
+185
+186
+187
+188
+189
+190
+191
+192
+193
+194
+195
+196
+197
+198
+199
+200
+201
+202
+203
+204
+205
+206
+207
+208
+209
+210
+211
+212
+213
+214
+215
+216
+217
+218
+219
+220
+221
+222
+223
+224
+225
+226
+227
+228
+229
+230
+231
+232
+233
+234
+235
+236
+237
+238
+239
+240
+241
+242
+243
+244
+245
+246
+247
+248
+249
+250
+251
+252
+253
+254
+255
+256
+257
+258
+259
+260
+261
+262
+263
+264
+265
+266
+267
+268
+269
+270
+271
+272
+273
+274
+275
+276
+277
+278
+279
+280
+281
+282
+283
+284
+285
+286
+287
+288
+289
+290
+291
+292
+293
+294
+295
+296
+297
+298
+299
+300
+301
+302
+303
+304
+305
+306
+307
+308
+309
+310
+311
+312
+313
+314
+315
+316
+317
+318
+319
+320
+321
+322
+323
+324
+325
+326
+327
+328
+329
+330
+331
+332
+333
+334
+335
+336
+337
+338
+339
+340
+341
+342
+343
+344
+345
+346
+347
+348
+349
+350
+351
+352
+353
+354
+355
+356
+357
+358
+359
+360
+361
+362
+363
+364
+365
+366
+367
+368
+369
+370
+371
+372
+373
+374
+375
+376
+377
+378
+379
+380
+381
+382
+383
+384
+385
+386
+387
+388
+389
+390
+391
+392
+393
+394
+395
+396
+397
+398
+399
+400
+401
+402
+403
+404
+405
+406
+407
+408
+409
+410
+411
+412
+413
+414
+415
+416
+417
+418
+419
+420
+421
+422
+423
+424
+425
+426
+427
+428
+429
+430
+431
+432
+433
+434
+435
+436
+437
+438
+439
+440
+441
+442
+443
+444
+445
+446
+447
+448
+449
+450
+451
+452
+453
+454
+455
+456
+457
+458
+459
+460
+461
+462
+463
+464
+465
+466
+467
+468
+469
+470
+471
+472
+473
+474
+475
+476
+477
+478
+479
+480
+481
+482
+483
+484
+485
+486
+487
+488
+489
+490
+491
+492
+493
+494
+495
+496
+497
+498
+499
+500
+501
+502
+503
+504
+505
+506
+507
+508
+509
+510
+511
+512
+513
+514
+515
+516
+517
+518
+519
+520
+521
+522
+523
+524
+525
+526
+527
+528
+529
+530
+531
+532
+533
+534
+535
+536
+537
+538
+539
+540
+541
+542
+543
+544
+545
+546
+547
+548
+549
+550
+551
+552
+553
+554
+555
+556
+557
+558
+559
+560
+561
+562
+563
+564
+565
+566
+567
+568
+569
+570
+571
+572
+573
+574
+575
+576
+577
+578
+579
+580
+581
+582
+583
+584
+585
+586
+587
+588
+589
+590
+591
+592
+593
+594
+595
+596
+597
+598
+599
+600
+601
+602
+603
+604
+605
+606
+607
+608
+609
+610
+611
+612
+613
+614
+615
+616
+617
+618
+619
+620
+621
+622
+623
+624
+625
+626
+627
+628
+629
+630
+631
+632
+633
+634
+635
+636
+637
+638
+639
+640
+641
+642
+643
+644
+645
+646
+647
+648
+649
+650
+651
+652
+653
+654
+655
+656
+657
+658
+659
+660
+661
+662
+663
+664
+665
+666
+667
+668
+669
+670
+671
+672
+673
+674
+675
+676
+677
+678
+679
+680
+681
+682
+683
+684
+685
+686
+687
+688
+689
+690
+691
+692
+693
+694
+695
+696
+697
+698
+699
+700
+701
+702
+703
+704
+705
+706
+707
+708
+709
+710
+711
+712
+713
+714
+715
+716
+717
+718
+719
+720
+721
+722
+723
+724
+725
+726
+727
+728
+729
+730
+731
+732
+733
+734
+735
+736
+737
+738
+739
+740
+741
+742
+743
+744
+745
+746
+747
+748
+749
+750
+751
+752
+753
+754
+755
+756
+757
+758
+759
+760
+761
+762
+763
+764
+765
+766
+767
+768
+769
+770
+771
+772
+773
+774
+775
+776
+777
+778
+779
+780
+781
+782
+783
+784
+785
+786
+787
+788
+789
+790
+791
+792
+793
+794
+795
+796
+797
+798
+799
+800
+801
+802
+803
+804
+805
+806
+807
+808
+809
+810
+811
+812
+813
+814
+815
+816
+817
+818
+819
+820
+821
+822
+823
+824
+825
+826
+827
+828
+829
+830
+831
+832
+833
+834
+835
+836
+837
+838
+839
+840
+841
+842
+843
+844
+845
+846
+847
+848
+849
+850
+851
+852
+853
+854
+855
+856
+857
+858
+859
+860
+861
+862
+863
+864
+865
+866
+867
+868
+869
+870
+
+
+
/* Cases where transition is disabled:
* - in incompatible browsers (Opera 11 included)
* - when the animated object is not an element
* - when there is a special easing
* - when there is a step function
* - when jQuery.fx.off is true (should work out of the box)
*
* jQuery.fx.stop() will stop animations instead of pausing them (undocumented method and behavior anyway).
*/
(function( jQuery ) {

var elemdisplay = {},
iframe, iframeDoc,
rfxtypes = /^(?:toggle|show|hide)$/,
rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
//rMarginProp = /^margin/,
timerId,
/*fxAttrs = [
// height animations
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
// width animations
[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
// opacity animations
[ "opacity" ]
],*/
fxNow;

// ++TRANSITION++
// Following feature test code should be moved to support.js
var div = document.createElement( "div" ),
divStyle = div.style,
trans = "Transition",
_cubicBezier = "cubic-bezier(",
easingTable;
// Only test for transition support in Firefox and Webkit
// as we know for sure that Opera has too much bugs (see http://csstransition.net)
// and there's no guarantee that first IE implementation will be bug-free
jQuery.support.transition =
"Moz" + trans in divStyle ? "Moz" + trans :
"Webkit" + trans in divStyle ? "Webkit" + trans :
false;
// Following declarations should be moved to css.js
jQuery.cssNumber.color = jQuery.cssNumber.backgroundColor = true;
// Translation table from "jQuery easings" to "transition timing functions"
easingTable = {
linear: "linear",
swing: "ease-out",
bounce: _cubicBezier + "0,.35,.5,1.3)",
// Penner equation approximations from Matthew Lein's Ceaser: http://matthewlein.com/ceaser/
easeInQuad: _cubicBezier + ".55,.085,.68,.53)",
easeInCubic: _cubicBezier + ".55,.055,.675,.19)",
easeInQuart: _cubicBezier + ".895,.03,.685,.22)",
easeInQuint: _cubicBezier + ".755,.05,.855,.06)",
easeInSine: _cubicBezier + ".47,0,.745,.715)",
easeInExpo: _cubicBezier + ".95,.05,.795,.035)",
easeInCirc: _cubicBezier + ".6,.04,.98,.335)",
easeOutQuad: _cubicBezier + ".25,.46,.45,.94)",
easeOutCubic: _cubicBezier + ".215,.61,.355,1)",
easeOutQuart: _cubicBezier + ".165,.84,.44,1)",
easeOutQuint: _cubicBezier + ".23,1,.32,1)",
easeOutSine: _cubicBezier + ".39,.575,.565,1)",
easeOutExpo: _cubicBezier + ".19,1,.22,1)",
easeOutCirc: _cubicBezier + ".075,.82,.165,1)",
easeInOutQuad: _cubicBezier + ".455,.03,.515,.955)",
easeInOutCubic: _cubicBezier + ".645,.045,.355,1)",
easeInOutQuart: _cubicBezier + ".77,0,.175,1)",
easeInOutQuint: _cubicBezier + ".86,0,.07,1)",
easeInOutSine: _cubicBezier + ".445,.05,.55,.95)",
easeInOutExpo: _cubicBezier + "1,0,0,1)",
easeInOutCirc: _cubicBezier + ".785,.135,.15,.86)"
};

jQuery.fn.extend({
/*show: function( speed, easing, callback ) {
var elem, display;

if ( speed || speed === 0 ) {
return this.animate( genFx("show", 3), speed, easing, callback );

} else {
for ( var i = 0, j = this.length; i < j; i++ ) {
elem = this[ i ];

if ( elem.style ) {
display = elem.style.display;

// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
display = elem.style.display = "";
}

// Set elements which have been overridden with display: none
// in a stylesheet to whatever the default browser style is
// for such an element
if ( (display === "" && jQuery.css(elem, "display") === "none") ||
!jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {
jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
}
}
}

// Set the display of most of the elements in a second loop
// to avoid the constant reflow
for ( i = 0; i < j; i++ ) {
elem = this[ i ];

if ( elem.style ) {
display = elem.style.display;

if ( display === "" || display === "none" ) {
elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
}
}
}

return this;
}
},

hide: function( speed, easing, callback ) {
if ( speed || speed === 0 ) {
return this.animate( genFx("hide", 3), speed, easing, callback);

} else {
var elem, display,
i = 0,
j = this.length;

for ( ; i < j; i++ ) {
elem = this[i];
if ( elem.style ) {
display = jQuery.css( elem, "display" );

if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) {
jQuery._data( elem, "olddisplay", display );
}
}
}

// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( i = 0; i < j; i++ ) {
if ( this[i].style ) {
this[i].style.display = "none";
}
}

return this;
}
},

// Save the old toggle function
_toggle: jQuery.fn.toggle,

toggle: function( fn, fn2, callback ) {
var bool = typeof fn === "boolean";

if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
this._toggle.apply( this, arguments );

} else if ( fn == null || bool ) {
this.each(function() {
var state = bool ? fn : jQuery(this).is(":hidden");
jQuery(this)[ state ? "show" : "hide" ]();
});

} else {
this.animate(genFx("toggle", 3), fn, fn2, callback);
}

return this;
},

fadeTo: function( speed, to, easing, callback ) {
return this.filter(":hidden").css("opacity", 0).show().end()
.animate({opacity: to}, speed, easing, callback);
},*/

animate: function( prop, speed, easing, callback ) {
var optall = jQuery.speed( speed, easing, callback );

if ( jQuery.isEmptyObject( prop ) ) {
return this.each( optall.complete, [ false ] );
}

// Do not change referenced properties as per-property easing will be lost
prop = jQuery.extend( {}, prop );

function doAnimation() {
// XXX 'this' does not always have a nodeName when running the
// test suite

if ( optall.queue === false ) {
jQuery._mark( this );
}

var opt = jQuery.extend( {}, optall ),
isElement = this.nodeType === 1,
hidden = isElement && jQuery(this).is(":hidden"),
name, val, p, e, hooks, replace,
parts, start, end, unit,
method,
// ++TRANSITION++
cssProps = jQuery.cssProps,
// disable transition if a step option is supplied
supportTransition = !opt.step && jQuery.support.transition,
transition,
transitions = [],
easing, real, lower,
computedStyle;

// will store per property easing and be used to determine when an animation is complete
opt.animatedProperties = {};
// ++TRANSITION++
// transition is enabled per property, when:
// - there is no step function for the animation
// - there is no special easing for the property
opt.transition = {};

// first pass over propertys to expand / normalize
for ( p in prop ) {
name = jQuery.camelCase( p );
if ( p !== name ) {
prop[ name ] = prop[ p ];
delete prop[ p ];
}

if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) {
replace = hooks.expand( prop[ name ] );
delete prop[ name ];

// not quite $.extend, this wont overwrite keys already present.
// also - reusing 'p' from above because we have the correct "name"
for ( p in replace ) {
if ( ! ( p in prop ) ) {
prop[ p ] = replace[ p ];
}
}
}
}

for ( name in prop ) {
val = prop[ name ];
// easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default)
if ( jQuery.isArray( val ) ) {
/* ++TRANSITION++ */ easing = opt.animatedProperties[ name ] = val[ 1 ];
val = prop[ name ] = val[ 0 ];
} else {
/* ++TRANSITION++ */ easing = opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || "swing";
}

// ++TRANSITION++
// check that CSS Transitions can be used
transition = supportTransition && isElement && opt.duration > 0 && name.indexOf( "scroll" ) && easingTable[ easing ];

// collect the properties to be added to elem.style.transition...
if ( transition ) {
real = cssProps[ name ] || name;

lower = real.replace(/([A-Z])/g, '-$1').toLowerCase();

transition =
lower +" "+
opt.duration +"ms "+
transition;

opt.transition[ name ] = {
lower: lower,
real: real
};

transitions.push(transition);
}

if ( val === "hide" && hidden || val === "show" && !hidden ) {
return opt.complete.call( this );
}

if ( isElement && ( name === "height" || name === "width" ) ) {
// Make sure that nothing sneaks out
// Record all 3 overflow attributes because IE does not
// change the overflow attribute when overflowX and
// overflowY are set to the same value
opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];

// Set display property to inline-block for height/width
// animations on inline elements that are having width/height animated
if ( jQuery.css( this, "display" ) === "inline" &&
jQuery.css( this, "float" ) === "none" ) {

// inline-level elements accept inline-block;
// block-level elements need to be inline with layout
if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) {
this.style.display = "inline-block";

} else {
this.style.zoom = 1;
}
}
}
}

if ( opt.overflow != null ) {
this.style.overflow = "hidden";
}

for ( p in prop ) {
e = new jQuery.fx( this, opt, p );
val = prop[ p ];

if ( rfxtypes.test( val ) ) {

// Tracks whether to show or hide based on private
// data attached to the element
method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 );
if ( method ) {
jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" );
e[ method ]();
} else {
e[ val ]();
}

} else {
parts = rfxnum.exec( val );
start = e.cur();

if ( parts ) {
end = parseFloat( parts[2] );
unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );

// We need to compute starting value
if ( unit !== "px" && p !== "opacity" ) {
jQuery.style( this, p, (end || 1) + unit);
start = ( (end || 1) / e.cur() ) * start;
jQuery.style( this, p, start + unit);
}

// If a +=/-= token was provided, we're doing a relative animation
if ( parts[1] ) {
end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
}

e.custom( start, end, unit );

} else {
e.custom( start, val, "" );
}
}
}

// ++TRANSITION++
if ( supportTransition && transitions.length ) {
transition = this.style[supportTransition];
computedStyle = window.getComputedStyle( this );

this.style[ supportTransition ] =
transitions.join() + ( transition && transition.indexOf( "none" ) ? "," + transition : "" );
// Once the transition property has been set, it's time to set all animated style properties
for ( p in opt.transition ) {
// access the computed style to make sure it has been taken into account by the browser
computedStyle[ p ];
// ...before setting the target style
jQuery.style.apply( null, opt.transition[ p ].styleToSet );
}
}

// For JS strict compliance
return true;
}

return optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
},

stop: function( type, clearQueue, gotoEnd ) {
if ( typeof type !== "string" ) {
gotoEnd = clearQueue;
clearQueue = type;
type = undefined;
}
if ( clearQueue && type !== false ) {
this.queue( type || "fx", [] );
}

return this.each(function() {
var index,
hadTimers = false,
timers = jQuery.timers,
data = jQuery._data( this ),
// ++TRANSITION++
supportTransition = jQuery.support.transition;

// clear marker counters if we know they won't be
if ( !gotoEnd ) {
jQuery._unmark( true, this );
}

function stopQueue( elem, data, index ) {
var hooks = data[ index ];
jQuery.removeData( elem, index, true );
hooks.stop( gotoEnd );
}

if ( type == null ) {
for ( index in data ) {
if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) {
stopQueue( this, data, index );
}
}
} else if ( data[ index = type + ".run" ] && data[ index ].stop ){
stopQueue( this, data, index );
}

for ( index = timers.length; index--; ) {
// ++TRANSITION++
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
if ( gotoEnd || supportTransition ) {

// force the next step to be the last
timers[ index ]( /* ++TRANSITION++*/ gotoEnd );
}
if ( !gotoEnd ) {
timers[ index ].saveState();
}
hadTimers = true;
timers.splice( index, 1 );
}
}

// start the next in the queue if the last step wasn't forced
// timers currently will call their complete callbacks, which will dequeue
// but only if they were gotoEnd
if ( !( gotoEnd && hadTimers ) ) {
jQuery.dequeue( this, type );
}
});
}

});

// Animations created synchronously will run synchronously
function createFxNow() {
setTimeout( clearFxNow, 0 );
return ( fxNow = jQuery.now() );
}

function clearFxNow() {
fxNow = undefined;
}

/*// Generate parameters to create a standard animation
function genFx( type, num ) {
var obj = {};

jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
obj[ this ] = type;
});

return obj;
}

// Generate shortcuts for custom animations
jQuery.each({
slideDown: genFx( "show", 1 ),
slideUp: genFx( "hide", 1 ),
slideToggle: genFx( "toggle", 1 ),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
});

jQuery.extend({
speed: function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
};

opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;

// normalize opt.queue - true/undefined/null -> "fx"
if ( opt.queue == null || opt.queue === true ) {
opt.queue = "fx";
}

// Queueing
opt.old = opt.complete;

opt.complete = function( noUnmark ) {
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
}

if ( opt.queue ) {
jQuery.dequeue( this, opt.queue );
} else if ( noUnmark !== false ) {
jQuery._unmark( this );
}
};

return opt;
},

easing: {
linear: function( p ) {
return p;
},
swing: function( p ) {
return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5;
}
},

timers: [],

fx: function( elem, options, prop ) {
this.options = options;
this.elem = elem;
this.prop = prop;

options.orig = options.orig || {};
}

});*/

jQuery.extend( jQuery.fx.prototype, {
/*jQuery.fx.prototype = {
// Simple function for setting a style value
update: function() {
if ( this.options.step ) {
this.options.step.call( this.elem, this.now, this );
}

( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
},*/

// Get the current size
cur: function() {
if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) {
return this.elem[ this.prop ];
}

var parsed,
r = jQuery.css( this.elem, this.prop );
// Empty strings, null, undefined and "auto" are converted to 0,
// complex values such as "rotate(1rad)" are returned as is,
// simple values such as "10px" are parsed to Float.
return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed;
},

// Start an animation from one number to another
custom: function( from, to, unit ) {
var self = this,
fx = jQuery.fx,
// ++TRANSITION++
transition = self.options.transition,
// prop has to be saved, or it'll be undefined in the setTimeout closure
prop = this.prop;

this.startTime = fxNow || createFxNow();
this.end = to;
this.now = this.start = from;
this.pos = this.state = 0;
this.unit = unit || this.unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );

function t( gotoEnd ) {
return self.step( gotoEnd );
}

t.queue = this.options.queue;
t.elem = this.elem;
t.saveState = function() {
if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) {
if ( self.options.hide ) {
jQuery._data( self.elem, "fxshow" + self.prop, self.start );
} else if ( self.options.show ) {
jQuery._data( self.elem, "fxshow" + self.prop, self.end );
}
}
};

// ++TRANSITION++
if ( ( t.transition = transition[ prop ] ) ) {
jQuery.timers.push( t );

// explicitly set the property to it's initial value to workaround bugzil.la/571344
// transform shouldn't cause any problem in this case, as it is covered by the spec.
if ( prop != "transform" ) {
self.elem.style[ transition[ prop ].real ] = from + self.unit;
}

// prevent negative values on certain properties
jQuery.fx.step[ prop ] && ( to = Math.max(0, to) );

transition[ prop ].styleToSet = [ self.elem, prop, to + self.unit ];

// use a setTimeout to detect the end of a transition
// the transitionend event is unreliable
transition[ prop ].timeout = setTimeout(function() {
jQuery.timers.splice( jQuery.timers.indexOf( t ), 1 );
self.step( true );
// add an unperceptible delay to help some tests pass in Firefox
}, self.options.duration + 30);

} else if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval( fx.tick, fx.interval );
}
},

/*// Simple 'show' function
show: function() {
var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );

// Remember where we started, so that we can go back to it later
this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
this.options.show = true;

// Begin the animation
// Make sure that we start at a small width/height to avoid any flash of content
if ( dataShow !== undefined ) {
// This show is picking up where a previous hide or show left off
this.custom( this.cur(), dataShow );
} else {
this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() );
}

// Start by showing the element
jQuery( this.elem ).show();
},

// Simple 'hide' function
hide: function() {
// Remember where we started, so that we can go back to it later
this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop );
this.options.hide = true;

// Begin the animation
this.custom( this.cur(), 0 );
},*/

// Each step of an animation
step: function( gotoEnd ) {
var p, n, complete,
t = fxNow || createFxNow(),
done = true,
elem = this.elem,
options = this.options,
// ++TRANSITION++
transition = options.transition[ this.prop ],
naturalEnd = t >= options.duration + this.startTime,
supportTransition = jQuery.support.transition;

if ( transition || gotoEnd || naturalEnd ) {
if ( !transition ) {
this.now = this.end;
this.pos = this.state = 1;
this.update();
// ++TRANSITION++
} else {
clearTimeout( transition.timeout );
// Stop a transition halfway through
if ( !gotoEnd && !naturalEnd ) {
// yes, stopping a transition halfway through should be as simple as setting a property to its current value.
// Try to call window.getComputedStyle() only once per element (in tick()?)
this.elem.style[ transition.real ] = jQuery.css( this.elem, transition.real );
}
}

options.animatedProperties[ this.prop ] = true;

for ( p in options.animatedProperties ) {
if ( options.animatedProperties[ p ] !== true ) {
done = false;
}
}

if ( done ) {
// Reset the overflow
if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {

jQuery.each( [ "", "X", "Y" ], function( index, value ) {
elem.style[ "overflow" + value ] = options.overflow[ index ];
});
}

// Hide the element if the "hide" operation was done
if ( options.hide ) {
jQuery( elem ).hide();
}

// ++TRANSITION++
// cleanup the transition property
if ( transition ) {
transition = "," + elem.style[ supportTransition ];
for ( p in options.transition ) {
transition = transition.split( options.transition[ p ].lower ).join("_");
}
transition = transition.replace( /, ?_[^,]*/g, "" ).substr(1);
// if the resulting string is empty, replace it with "none" to achieve gotoEnd
elem.style[ supportTransition ] = transition || "none";
// finish on an empty string if "none" has been used
// this way transitions set with CSS aren't overridden.
!transition && ( elem.style[ supportTransition ] = transition );
}

// Reset the properties, if the item has been hidden or shown
if ( options.hide || options.show ) {
for ( p in options.animatedProperties ) {
( gotoEnd || naturalEnd ) && jQuery.style( elem, p, options.orig[ p ] );
jQuery.removeData( elem, "fxshow" + p, true );
// Toggle data is no longer needed
jQuery.removeData( elem, "toggle" + p, true );
}
}

// Execute the complete function
// in the event that the complete function throws an exception
// we must ensure it won't be called twice. #5684

complete = options.complete;
if ( complete && ( gotoEnd || naturalEnd ) ) {

options.complete = false;
complete.call( elem );
}
}

return false;

} else {
// classical easing cannot be used with an Infinity duration
if ( options.duration == Infinity ) {
this.now = t;
} else {
n = t - this.startTime;
this.state = n / options.duration;

// Perform the easing function, defaults to swing
this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration );
this.now = this.start + ( (this.end - this.start) * this.pos );
}
// Perform the next step of the animation
this.update();
}

return true;
}
});//};

jQuery.extend( jQuery.fx, {
tick: function() {
var timer,
timers = jQuery.timers,
i = 0;

for ( ; i < timers.length; i++ ) {
timer = timers[ i ];
// Checks the timer has not already been removed
// ++TRANSITION++
if ( !timer.transition && !timer() && timers[ i ] === timer ) {
timers.splice( i--, 1 );
}
}

if ( !timers.length ) {
jQuery.fx.stop();
}
}/*,

interval: 13,

stop: function() {
clearInterval( timerId );
timerId = null;
},

speeds: {
slow: 600,
fast: 200,
// Default speed
_default: 400
},

step: {
opacity: function( fx ) {
jQuery.style( fx.elem, "opacity", fx.now );
},

_default: function( fx ) {
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
fx.elem.style[ fx.prop ] = fx.now + fx.unit;
} else {
fx.elem[ fx.prop ] = fx.now;
}
}
}*/
});

/*// Ensure props that can't be negative don't go there on undershoot easing
jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) {
// Exclude marginTop, marginLeft, marginBottom and marginRight from this list
if ( !rMarginProp.test( prop ) ) {
jQuery.fx.step[ prop ] = function( fx ) {
jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
};
}
});

if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
}).length;
};
}*/

// Try to restore the default display value of an element
function defaultDisplay( nodeName ) {

if ( !elemdisplay[ nodeName ] ) {

var body = document.body,
elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
display = elem.css( "display" );
elem.remove();

// If the simple way fails,
// get element's real default display by attaching it to a temp iframe
if ( display === "none" || display === "" ) {
// No iframe to use yet, so create it
if ( !iframe ) {
iframe = document.createElement( "iframe" );
iframe.frameBorder = iframe.width = iframe.height = 0;
}

body.appendChild( iframe );

// Create a cacheable copy of the iframe document on first call.
// IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
// document to it; WebKit & Firefox won't allow reusing the iframe document.
if ( !iframeDoc || !iframe.createElement ) {
iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" );
iframeDoc.close();
}

elem = iframeDoc.createElement( nodeName );

iframeDoc.body.appendChild( elem );

display = jQuery.css( elem, "display" );
body.removeChild( iframe );
}

// Store the correct default display
elemdisplay[ nodeName ] = display;
}

return elemdisplay[ nodeName ];
}

})( jQuery );
+
+
+ +
+
+
+
+ +
+ + + +
+
+
+ + +
+ + + + + + + + +
+

Markdown Cheat Sheet

+ +
+ +
+
+

Format Text

+

Headers

+
+# This is an <h1> tag
+## This is an <h2> tag
+###### This is an <h6> tag
+

Text styles

+
+*This text will be italic*
+_This will also be italic_
+**This text will be bold**
+__This will also be bold__
+
+*You **can** combine them*
+
+
+
+

Lists

+

Unordered

+
+* Item 1
+* Item 2
+  * Item 2a
+  * Item 2b
+

Ordered

+
+1. Item 1
+2. Item 2
+3. Item 3
+   * Item 3a
+   * Item 3b
+
+
+

Miscellaneous

+

Images

+
+![GitHub Logo](/images/logo.png)
+Format: ![Alt Text](url)
+
+

Links

+
+http://github.com - automatic!
+[GitHub](http://github.com)
+

Blockquotes

+
+As Kanye West said:
+
+> We're living the future so
+> the present is our past.
+
+
+
+
+ +

Code Examples in Markdown

+
+

Syntax highlighting with GFM

+
+```javascript
+function fancyAlert(arg) {
+  if(arg) {
+    $.facebox({div:'#foo'})
+  }
+}
+```
+
+
+

Or, indent your code 4 spaces

+
+Here is a Python code example
+without syntax highlighting:
+
+    def foo:
+      if not bar:
+        return true
+
+
+

Inline code for comments

+
+I think you should use an
+`<addr>` element here instead.
+
+
+ +
+ + + +
+

Something went wrong with that request. Please try again. Dismiss

+
+ +
+

Looking for the GitHub logo?

+ +
+ + + + + + + + diff --git a/spec/panelMonth-spec.coffee b/spec/panelMonth-spec.coffee index 7ffd43a..855bac9 100644 --- a/spec/panelMonth-spec.coffee +++ b/spec/panelMonth-spec.coffee @@ -1,10 +1,13 @@ -describe "Picker", -> +describe "PanelMonth", -> givenDate = new Date("2012-05-12") options = month: givenDate.getMonth() givenDate: givenDate startDay: 0 # Sunday dayNamesAbbr: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] + monthNames: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec", "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" ] p = {} beforeEach -> @@ -12,8 +15,6 @@ describe "Picker", -> describe "public methods", -> describe "#contructor:", -> - it "sets @month", -> - expect(p.month).toEqual(options.month) it "sets @givenDate", -> expect(p.givenDate.valueOf()).toEqual(options.givenDate.valueOf()) @@ -27,6 +28,9 @@ describe "Picker", -> it "sets @dayNamesAbbr", -> expect(p.dayNamesAbbr).toEqual(options.dayNamesAbbr) + it "sets @monthNames", -> + expect(p.monthNames).toEqual(options.monthNames) + describe "#render", -> it "returns a jquery element", -> @@ -212,6 +216,13 @@ describe "Picker", -> expect(p._isAvailable(d)).toEqual(false) + describe "#_getMonthTitle", -> + it "returns a string representing the givenDate's month and year", -> + # "#{@monthNames[@givenDate.getMonth()+12]} #{@givenDate.getFullYear()}" + test = "May 2012" + expect(p._getMonthTitle()).toEqual(test) + + describe "#_getMonthDays", -> it "returns a jquery element", -> expect(p._getMonthDays().hide()).toBeTruthy() # duck type @@ -226,16 +237,39 @@ describe "Picker", -> days = p._getMonthDays().find(".week").children() expect(days.length).toEqual(42) + it "adds a data-date element so external sources know what + the panel represents", -> + # givenDate = new Date("2012-05-12") + # $("
") + $panel = p._getMonthDays() + match = String(p.givenDate.valueOf()) + expect($panel.attr('data-date')).toEqual(match) + + it "adds the month's title for external sources to handle", -> + $panel = p._getMonthDays() + month = p.givenDate.getMonth() + year = p.givenDate.getFullYear() + match = "#{p.monthNames[month+12]} #{year}" + expect($panel.attr('data-date_title')).toEqual(match) + + it "should handle day click events", -> + spyOn(p, '_onDaySelect') + $panel = p._getMonthDays() + $day = $panel.find(".week0").find(".day0") + $day.click() + expect(p._onDaySelect).toHaveBeenCalled() + describe "events", -> describe "#_onDaySelect", -> - p = new chronos.PanelMonth(options) - p.container = {} - p.container.trigger = jasmine.createSpy("container") - mock_event = {target: "mock target element"} - p._onDaySelect(mock_event, "date") - expect(p.container.trigger).toHaveBeenCalledWith('daySelect', - ['mock target element', 'date']) + it "triggers the 'daySelect' event", -> + p = new chronos.PanelMonth(options) + p.container = {} + p.container.trigger = jasmine.createSpy("container") + mock_event = {target: "mock target element"} + p._onDaySelect(mock_event, "date") + expect(p.container.trigger).toHaveBeenCalledWith('daySelect', + ['mock target element', 'date']) diff --git a/spec/picker-spec.coffee b/spec/picker-spec.coffee index f74c38e..4205bb9 100644 --- a/spec/picker-spec.coffee +++ b/spec/picker-spec.coffee @@ -45,7 +45,7 @@ describe "Picker", -> expect(c.hasClass('blue')).toBeTruthy - describe "#_createHeader", -> + describe "#_createBody", -> it "should create a jquery container div", -> c = p._createBody() expect(c.hide()).toBeTruthy() # duck type @@ -56,7 +56,7 @@ describe "Picker", -> expect(c.find(".#{klass}").length).toBeGreaterThan(0) - describe "#_createBody", -> + describe "#_createHeader", -> it "should create a jquery container div", -> c = p._createHeader() expect(c.hide()).toBeTruthy() # duck type @@ -66,6 +66,24 @@ describe "Picker", -> c = p._createHeader() expect(c.find(".#{klass}").length).toBeGreaterThan(0) + it "should handle previous click events", -> + spyOn(p, '_onPrevious') + c = p._createHeader() + c.find(".previous").click() + expect(p._onPrevious).toHaveBeenCalled() + + it "should handle next click events", -> + spyOn(p, '_onNext') + c = p._createHeader() + c.find(".next").click() + expect(p._onNext).toHaveBeenCalled() + + it "should handle zoom (title bar) click events", -> + spyOn(p, '_onZoomOut') + c = p._createHeader() + c.find(".title").click() + expect(p._onZoomOut).toHaveBeenCalled() + describe "#_initialize", -> @@ -74,10 +92,10 @@ describe "Picker", -> p.constructor(current) expect(p._initializeContainer).toHaveBeenCalled() - it "should call _setWorkingDate", -> - p._setWorkingDate = jasmine.createSpy("_setWorkingDate") + it "should call _setStartingDate", -> + p._setStartingDate = jasmine.createSpy("_setStartingDate") p.constructor(current) - expect(p._setWorkingDate).toHaveBeenCalled() + expect(p._setStartingDate).toHaveBeenCalled() it "should set the container", -> expect(p.container.find(".body").length).toBeGreaterThan(0) # duck type @@ -95,48 +113,48 @@ describe "Picker", -> p._renderTitle("HI") expect(p.container.find(".titleText").html()).toEqual("HI") - describe "#_setWorkingDate", -> - console.log "SWD" + + describe "#_setStartingDate", -> beforeEach -> p.current.options.minDate = null p.current.options.maxDate = null - p.workingDate = undefined + p.startingDate = undefined describe "when no maxDate or minDate", -> - it "sets the workingDate to the @dateToday value", -> - p._setWorkingDate() - expect(p.workingDate.valueOf()).toEqual(p.todayDate.valueOf()) + it "sets the startingDate to the @dateToday value", -> + p._setStartingDate() + expect(p.startingDate.valueOf()).toEqual(p.todayDate.valueOf()) describe "when given a minDate", -> - it "sets the workingDate to the minDate if @todayDate is before the minDate", -> + it "sets the startingDate to the minDate if @todayDate is before the minDate", -> min = new Date("2012-06-10") p.todayDate = new Date("2012-06-5") p.current.options.minDate = min - p._setWorkingDate() - expect(p.workingDate.valueOf()).toEqual(min.valueOf()) + p._setStartingDate() + expect(p.startingDate.valueOf()).toEqual(min.valueOf()) - it "does not set the workingDate if @todayDate is after the minDate", -> + it "does not set the startingDate if @todayDate is after the minDate", -> today = new Date("2012-06-15") p.current.options.minDate = new Date("2012-06-10") p.todayDate = today - p._setWorkingDate() - expect(p.workingDate.valueOf()).toEqual(today.valueOf()) + p._setStartingDate() + expect(p.startingDate.valueOf()).toEqual(today.valueOf()) describe "when given a maxDate", -> - it "sets the workingDate to the maxDate if @todayDate is after the maxDate", -> + it "sets the startingDate to the maxDate if @todayDate is after the maxDate", -> max = new Date("2012-06-10") p.todayDate = new Date("2012-06-15") p.current.options.maxDate = max - p._setWorkingDate() - expect(p.workingDate.valueOf()).toEqual(max.valueOf()) + p._setStartingDate() + expect(p.startingDate.valueOf()).toEqual(max.valueOf()) - it "does not set the workingDate if @todayDate is before the maxDate", -> + it "does not set the startingDate if @todayDate is before the maxDate", -> today = new Date("2012-06-15") p.current.options.maxDate = new Date("2012-06-20") p.todayDate = today - p._setWorkingDate() - expect(p.workingDate.valueOf()).toEqual(today.valueOf()) + p._setStartingDate() + expect(p.startingDate.valueOf()).toEqual(today.valueOf()) describe "when given both a minDate and a maxDate", -> it "will default to the minDate", -> @@ -144,8 +162,8 @@ describe "Picker", -> p.todayDate = new Date("2012-06-5") p.current.options.minDate = min p.current.options.maxDate = new Date("2012-06-20") - p._setWorkingDate() - expect(p.workingDate.valueOf()).toEqual(min.valueOf()) + p._setStartingDate() + expect(p.startingDate.valueOf()).toEqual(min.valueOf()) describe "#_renderYears", -> @@ -158,5 +176,72 @@ describe "Picker", -> describe "#_renderMonths", -> - pending + d = {} + $container = {} + + beforeEach -> + d = new Date("2012-05-10") + p.startingDate = d + + it "should set the title for @startingDate", -> + p._renderMonths() + month = d.getMonth() + text = "#{p.current.options.monthNames[month+12]} #{d.getFullYear()}" + title = p.container.find(".titleText").html() + expect(title).toEqual(text) + + it "should set the .body_curr div for @startingDate", -> + p._renderMonths() + test = p.container.find(".body_curr").find(".monthBody").attr('data-date') + given = String(d.valueOf()) + expect(test).toEqual(given) + + it "should set the .body_prev div for the previous month", -> + p._renderMonths() + test = p.container.find(".body_prev").find(".monthBody").attr('data-date') + previous = new Date(d.setMonth(d.getMonth() - 1)) + given = String(previous.valueOf()) + expect(test).toEqual(given) + + it "should set the .body_next div for the next month", -> + p._renderMonths() + test = p.container.find(".body_next").find(".monthBody").attr('data-date') + previous = new Date(d.setMonth(d.getMonth() + 1)) + given = String(previous.valueOf()) + expect(test).toEqual(given) + + + describe "#_buildMonth", -> + d = new Date() + $container = $("
") + + it "should add a month panel to the given container", -> + p._buildMonth(d, $container) + expect($container.find(".monthPanel").length).toEqual(1) + + it "should handle day click events", -> + spyOn(p, '_onDaySelect') + $test = p._buildMonth(d, $container) + $day = $test.find(".monthBody").find(".week0").find(".day0") + $day.click() + expect(p._onDaySelect).toHaveBeenCalled() + + describe "#_changeMonthBy", -> + d = {} + beforeEach -> + d = new Date() + + it "returns a date object", -> + expect(p._changeMonthBy(d, 0).valueOf()).toBeTruthy() # duck type + + it "can decrease the given date by given months", -> + test = new Date(d.valueOf()) + test.setMonth(d.getMonth() - 2) + expect(p._changeMonthBy(d, -2).valueOf()).toEqual(test.valueOf()) + + it "can increase the given date by given months", -> + test = new Date(d.valueOf()) + test.setMonth(d.getMonth() + 5) + expect(p._changeMonthBy(d, 5).valueOf()).toEqual(test.valueOf()) + diff --git a/src/panelMonth.coffee b/src/panelMonth.coffee index 7ecbf97..af8137b 100644 --- a/src/panelMonth.coffee +++ b/src/panelMonth.coffee @@ -6,9 +6,9 @@ class chronos.PanelMonth @choice = new Date(options.choice) if options.choice @maxDate = new Date(options.maxDate) if options.maxDate @minDate = new Date(options.minDate) if options.minDate - @month = options.month @startDay = options.startDay @dayNamesAbbr = options.dayNamesAbbr + @monthNames = options.monthNames @today = new Date() @container = {} @@ -18,6 +18,7 @@ class chronos.PanelMonth @container.append(@_getMonthDays.call(@)) @container + ### Private Methods ### @@ -57,9 +58,9 @@ class chronos.PanelMonth else false - # Return true if given date matches @month + # Return true if given date matches the given month _isMonth: (d) -> - @month == d.getMonth() + @givenDate.getMonth() == d.getMonth() # Return true if date is available according to maxDate and minDate # maxDate and minDate are inclusive and have already had their time portions zeroed @@ -80,15 +81,23 @@ class chronos.PanelMonth d.setMilliseconds(0) d + _getMonthTitle: -> + "#{@monthNames[@givenDate.getMonth()+12]} #{@givenDate.getFullYear()}" + # build the month days _getMonthDays: -> - days = $("
") - workingDate = @_getMonthStart() - # zero out time portion of dates before doing any comparisons - for item in [workingDate, @choice, @today, @maxDate, @minDate] + for item in [@givenDate, @choice, @today, @maxDate, @minDate] @_clearTimePortion(item) if item + workingDate = @_getMonthStart() + + # here we store the month's workingDate so that we can figure out what month + # the panel represents from an external source + days = $("
") + for d in [0..41] classes = ['day', 'day' + workingDate.getDay()] classes.push('today') if @_isToday(workingDate) @@ -115,10 +124,13 @@ class chronos.PanelMonth days - # Fire daySelect - _onDaySelect: (event, date) -> - @container.trigger('daySelect', [event.target, date]) - + ### + Events + ### + # Fire daySelect + _onDaySelect: (event, date) -> + $target = $(event.target) + @container.trigger('daySelect', [event.target, date]) unless $target.hasClass("unavailable") diff --git a/src/picker.coffee b/src/picker.coffee index 0ec2c91..6e6c555 100644 --- a/src/picker.coffee +++ b/src/picker.coffee @@ -1,11 +1,11 @@ class chronos.Picker constructor: (current) -> - @current = current - @container = undefined - @workingDate = undefined - @todayDate = new Date() - #@pickedDate = new Date() + @current = current # to reference and save picker's settings + @container = undefined # to hold the picker + @startingDate = undefined # the start date to show + @todayDate = new Date() # for today + @pickedDateTime = undefined @_initialize() @@ -25,7 +25,7 @@ class chronos.Picker _initialize: -> @container ||= @_initializeContainer() - @_setWorkingDate() + @_setStartingDate() _initializeContainer: -> @container = @_createContainer() @@ -95,75 +95,56 @@ class chronos.Picker # fill the picker's body with a range of months to select from _renderMonths: -> - month = @workingDate.getMonth() + month = @startingDate.getMonth() # add 12 to month names to get full name - @_renderTitle("#{@current.options.monthNames[month+12]} #{@workingDate.getFullYear()}") + @_buildMonth(@startingDate, @container.find(".body_curr")) - currentMonthPanel = new chronos.PanelMonth( - givenDate: @workingDate - month: @workingDate.getMonth() + # set title to current month + @_renderTitle(@container.find(".body_curr").find(".monthBody").attr('data-date_title')) + + @_buildMonth(@_changeMonthBy(@startingDate, -1), @container.find(".body_prev")) + + @_buildMonth(@_changeMonthBy(@startingDate, 1), @container.find(".body_next")) + + # create a month panel according to given date and append it to given container + _buildMonth: (showDate, $container) -> + monthPanel = new chronos.PanelMonth( + givenDate: showDate startDay: @current.options.startDay dayNamesAbbr: @current.options.dayNamesAbbr - choice: new Date("2012-05-16") # TODO + monthNames: @current.options.monthNames + choice: @pickedDateTime #new Date("2012-05-16") # TODO maxDate: undefined # TODO minDate: undefined # TODO ) - - currentMonth = currentMonthPanel.render() - + month = monthPanel.render() # handle day selection - currentMonth.bind 'daySelect', (event, dayElement, date) => + month.bind 'daySelect', (event, dayElement, date) => @_onDaySelect(event, dayElement, date) + $container.append(month) - @container.find(".body_curr").append(currentMonth) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Increment or decrement given date by given value in months + _changeMonthBy: (date, value) -> + d = new Date(date.valueOf()) + d.setMonth(d.getMonth() + value) + d # sets the picker's working date - _setWorkingDate: -> + _setStartingDate: -> if @current.options.maxDate != null || @current.options.minDate != null - # if today is past the max date, then set the working date to the max if @current.options.maxDate && (@todayDate.valueOf() > @current.options.maxDate.valueOf()) - @workingDate = new Date(@current.options.maxDate.valueOf()) + @startingDate = new Date(@current.options.maxDate.valueOf()) # if today is before the min date, then set the working date to the min if @current.options.minDate && (@todayDate.valueOf() < @current.options.minDate.valueOf()) - @workingDate = new Date(@current.options.minDate.valueOf()) + @startingDate = new Date(@current.options.minDate.valueOf()) # set the working date to today (but a separate object) if still undefined - if @workingDate == undefined - @workingDate = new Date(@todayDate.valueOf()) - @workingDate + if @startingDate == undefined + @startingDate = new Date(@todayDate.valueOf()) + @startingDate ### @@ -174,13 +155,99 @@ class chronos.Picker console.log ("ZOOM!") _onPrevious: (event) -> + console.log ("previous!") + unless @animating # prevent spastic clicks + @animating = true + $container = $(event.target).parent().parent(".chronos_picker") + $body = $container.find(".body") + + $next = $body.find(".body_next") + $curr = $body.find(".body_curr") + $prev = $body.find(".body_prev") + width = $curr.outerWidth() + + # set title to new month + @_renderTitle($prev.find(".monthBody").attr('data-date_title')) + + # TODO: abstract animation options + + $curr.animate { + left: "+=#{width}" + }, 500 + $prev.animate { + left: "+=#{width}" + }, 500, => + # when finished: + # - remove next + # - set current to next + # - set prev to current + # - build new MonthPanel for prev + $next.remove() + $curr.removeClass("body_curr").addClass("body_next") + $prev.removeClass("body_prev").addClass("body_curr") + $new_prev = $("
") + $body.prepend($new_prev) + + $curr.removeAttr('style') + $prev.removeAttr('style') + $next.removeAttr('style') + + newCurrentDate = new Date(parseInt($prev.find(".monthBody").attr('data-date'))) + @_buildMonth(@_changeMonthBy(newCurrentDate, -1), $new_prev) + @animating = false + + + _onNext: (event) -> + console.log ("next!") + unless @animating # prevent spastic clicks + @animating = true + $container = $(event.target).parent().parent(".chronos_picker") + $body = $container.find(".body") + + $next = $body.find(".body_next") + $curr = $body.find(".body_curr") + $prev = $body.find(".body_prev") + width = $curr.outerWidth() + + # set title to new month + @_renderTitle($next.find(".monthBody").attr('data-date_title')) + + # TODO: abstract animation options + + $curr.animate { + left: "-=#{width}" + }, 500 + $next.animate { + left: "-=#{width}" + }, 500, => + # when finished: + # - remove prev + # - set current to prev + # - set next to current + # - build new MonthPanel for next + $prev.remove() + $curr.removeClass("body_curr").addClass("body_prev") + $next.removeClass("body_next").addClass("body_curr") + $new_next = $("
") + $body.append($new_next) + + $curr.removeAttr('style') + $prev.removeAttr('style') + $next.removeAttr('style') + + newCurrentDate = new Date(parseInt($next.find(".monthBody").attr('data-date'))) + @_buildMonth(@_changeMonthBy(newCurrentDate, 1), $new_next) + @animating = false _onClose: (event) -> + console.log ("close!") + _onDaySelect: (event, dayElement, date) -> console.log "SELECTED", event, dayElement, date + #@pickedDateTime = date diff --git a/src/scss/chronos.scss b/src/scss/chronos.scss index 6a0bcbf..66ff813 100644 --- a/src/scss/chronos.scss +++ b/src/scss/chronos.scss @@ -10,7 +10,8 @@ $day_padding: 2px; $border_radius: 10px; $border_radius_day: 5px; -$calendar_width: 7*$day_width + 2*7*$day_padding + 10px; // 10px = 5px *2 from body_padding +$calendar_width: 7*$day_width + 2*7*$day_padding; + // padding on left and right for previous and next $header_title_width: $calendar_width - 2*$selector_width - 2*$header_padding_sides; @@ -26,9 +27,23 @@ $colorOtherMonth: #E1DBFF; background-color: $colorBase; color: $colorText; @include border-radius($border_radius); + overflow: hidden; // needed to body_prev and body_next .body { - padding: $body_padding; + width: 3*$calendar_width; + position: relative; + left: -$calendar_width; // shift body panels to left so that .body_curr is showing first + } + .body_prev, .body_curr, .body_next { + width: $calendar_width; + float: left; + position: relative; // needed for animation + } + .body_prev { + } + .body_curr { + } + .body_next { } /** @@ -96,6 +111,10 @@ $colorOtherMonth: #E1DBFF; .day { @include border-radius($border_radius_day); cursor: pointer; + &.today { + color: white; + font-weight: bold; + } &:hover, &.selected { background-color: darken($colorBase, 40%); color: $colorWhite; @@ -103,10 +122,6 @@ $colorOtherMonth: #E1DBFF; &.otherMonth { color: $colorOtherMonth; } - &.today { - color: white; - font-weight: bold; - } } } .monthHeader {