Editorial Calendar - Version 0.9

Version Description

  • The calendar now uses the QUnit unit test framework to make sure the calendar maintains high quality in every release. You can see the tests run on our integration blog or run them on your own blog by adding &qunit=true to the end of the URL for the calendar.
  • Made it easier to see the first day of the month using a special header and background color.
  • Changed to a lighter gradient for the calendar header to match the new theme of WordPress 3.0.
  • We now support pending review posts in the calendar.
  • The calendar supports a feedback mechanism where we collect data about your use of the calendar. This anonymous data helps us improve the calendar and know which areas to focus on. Everyone can view the Editorial Calendar Statistics.
  • Auto-drafts are now hidden from the calendar.
Download this release

Release Info

Developer zgrossbart
Plugin Icon wp plugin Editorial Calendar
Version 0.9
Comparing to
See all releases

Code changes from version 0.8 to 0.9

Files changed (11) hide show
  1. closure.sh +2 -0
  2. edcal.css +126 -177
  3. edcal.js +364 -266
  4. edcal.php +198 -66
  5. edcal_test.js +354 -0
  6. images/tip_close.gif +0 -0
  7. images/tip_close.png +0 -0
  8. lib/lib.txt +6 -0
  9. lib/qunit.css +119 -0
  10. lib/qunit.js +1069 -0
  11. readme.txt +46 -17
closure.sh CHANGED
@@ -1 +1,3 @@
1
  java -jar closure/compiler.jar --js_output_file lib/edcallib.min.js --js lib/humanmsg.js --js lib/jquery.cookie.js --js lib/jquery.bgiframe.js --js lib/jquery.delegate.js --js lib/jquery.tools.min.js --js lib/jquery.timepicker.js --js lib/json2.js --js lib/tools.scrollable-1.1.2.js --js lib/tools.scrollable.mousewheel-1.0.1.js --js lib/ui.core.js --js lib/ui.draggable.js --js lib/ui.droppable.js --js lib/date.extras.js
 
 
1
  java -jar closure/compiler.jar --js_output_file lib/edcallib.min.js --js lib/humanmsg.js --js lib/jquery.cookie.js --js lib/jquery.bgiframe.js --js lib/jquery.delegate.js --js lib/jquery.tools.min.js --js lib/jquery.timepicker.js --js lib/json2.js --js lib/tools.scrollable-1.1.2.js --js lib/tools.scrollable.mousewheel-1.0.1.js --js lib/ui.core.js --js lib/ui.draggable.js --js lib/ui.droppable.js --js lib/date.extras.js
2
+
3
+ java -jar closure/compiler.jar --js_output_file edcal.min.js --js edcal.js
edcal.css CHANGED
@@ -22,8 +22,8 @@
22
  /* This height will be reset with JavaScript */
23
  height: 100%;
24
  margin-top: 5px;
25
- margin-left: 2.5px;
26
- margin-right: 2.5px;
27
  }
28
 
29
  #cal_cont, #edcal_scrollable {
@@ -47,7 +47,7 @@
47
  }
48
 
49
  .day {
50
- border: thin solid rgb(164, 164, 164);
51
  -moz-border-radius-topright:3px;
52
  -moz-border-radius-topleft:3px;
53
  -webkit-border-top-left-radius:3px;
@@ -81,6 +81,10 @@
81
  height: 100%;
82
  }
83
 
 
 
 
 
84
  .daylabel {
85
  position: absolute;
86
  top: 0px;
@@ -148,55 +152,35 @@
148
  font-weight: bold;
149
  text-align: left;
150
  text-indent: 5px;
151
- color: #FFFFFF;
152
- text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.4);
153
- border-style-top:solid solid none;
154
- border-width:1px 1px 0;
155
- line-height: 23px;
156
  min-width: 10em;
157
- -x-system-font:none;
158
- font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif;
159
- font-size:13px;
160
- font-size-adjust:none;
161
- font-stretch:normal;
162
- font-style:normal;
163
- font-variant:normal;
164
- font-weight:normal;
165
  }
166
 
167
  .dayheadcont {
168
  position: relative;
169
  height: 28px;
170
  top: 0px;
171
-
172
- -webkit-border-top-left-radius: 6px;
173
- -webkit-border-top-right-radius: 6px;
174
- -moz-border-radius-topleft: 6px;
175
- -moz-border-radius-topright: 6px;
176
  }
177
 
178
  .dayhead.firstday {
179
  margin-left: 5px;
180
-
181
  }
182
 
183
  .dayhead.lastday {
184
-
185
  }
186
 
187
  #cal_cont {
188
- border-left: thin solid lightgray;
189
- border-right: thin solid lightgray;
190
- border-bottom: thin solid lightgray;
191
- padding-bottom: 5px;
192
-
193
- -moz-border-radius: 12px;
194
- -webkit-border-radius: 12px;
195
  }
196
 
197
  #topbar {
198
  margin-bottom: 1em;
199
- width: 98%;
200
  margin-top: 1em;
201
  position: relative;
202
  height: 2em;
@@ -204,14 +188,14 @@
204
 
205
  #topleft {
206
  position: absolute;
207
- left: 10px;
208
  top: -14px;
209
  width: auto;
210
  }
211
 
212
  #topright {
213
  position: absolute;
214
- right: 2em;
215
  top: 0px;
216
  width: auto;
217
  text-align: right;
@@ -219,6 +203,7 @@
219
 
220
  #moveToToday {
221
  margin-left: 2em;
 
222
  text-decoration: none;
223
  }
224
 
@@ -251,8 +236,8 @@
251
 
252
  .postlist {
253
  position: relative;
254
- top: 1.5em;
255
- height: 90%;
256
  overflow: auto;
257
  }
258
 
@@ -264,211 +249,175 @@
264
  color: #21759B;
265
  }*/
266
 
267
- #edit-slug-buttons {
268
- position: relative;
269
- }
270
 
271
- #newPostScheduleButton {
272
- position: absolute;
273
- right: 20px;
274
- top: -5px;
275
- }
276
-
277
- #newPostScheduleButton.disabled {
278
- color: #AAAAAA;
279
- }
280
 
281
  #tooltip {
282
- min-width: 35em;
 
 
 
 
 
 
 
283
  }
284
 
285
- #tooltip .mceEditor {
286
- height: 15em;
287
- margin-left: -6px;
288
  }
289
 
290
  #tooltiptitle {
291
  font-weight:bold;
292
- left: 0.5em;
293
- position:absolute;
294
- top: -8px;
 
 
295
  text-shadow: #fff 0px 1px 0px;
296
  }
297
 
298
- .tipauthor {
299
- font-weight: normal;
300
- font-style: italic;
301
- position: relative;
302
- left: 2em;
 
303
  }
304
 
305
- #tooltip p {
306
- margin-left: 1.5em;
307
  }
308
 
309
- #tooltip h3 {
310
- max-width: 20em;
311
- margin-bottom: -10px;
312
- padding-right: 15px;
313
- margin-top: 1em;
314
- font-size: 13px;
315
- font-weight: bold;
316
  }
317
 
318
- #tooltip h4 {
319
- margin: 0 0 3px 0;
320
- font-family:Georgia,"Times New Roman","Bitstream Charter",Times,serif;
321
- font-style: italic;
322
- font-weight: normal;
323
  }
324
 
325
- #tooltip #edcal-time-status-section h4 {
326
- display: inline;
 
327
  }
328
 
 
 
 
 
329
  #tooltip #edcal-time {
330
- width: 8em;
331
  background-color: white;
332
  }
333
 
334
- #tooltip #edcal-time-section h4, #tooltip #edcal-status-section h4 {
335
- top: 3px;
 
336
  }
337
 
338
- #edcal-time-status-section {
339
  position: relative;
340
- height: 5em;
 
341
  }
342
 
343
- #edcal-time-section {
344
- position: absolute;
345
- top: 0px;
346
- left: 0px;
347
- width: 48%;
348
- }
349
-
350
- #edcal-status-section {
351
  position: absolute;
352
- top: 0px;
353
- left: 50%;
354
- width: 48%;
355
- }
356
-
357
- #tooltip .edcal-form-row {
358
- margin-bottom: 5px;
359
- margin-top: 1em;
360
- position: relative;
361
- }
362
-
363
- #edcal-title-row, #edcal-time-section, #edcal-status-section {
364
- height: 2em;
365
- }
366
-
367
- #edcal-status-section {
368
- top: -3em;
369
- left: 16em;
370
  }
371
 
372
- #tooltip .edcal-form-label {
373
- position: absolute;
374
- left: 0px;
375
- top: 0px;
376
- width: 5.5em;
377
- text-align: right;
378
  }
379
 
380
- #tooltip .edcal-form-control {
381
- position: absolute;
382
- left: 7em;
383
- top: 0px;
384
- width: 75%;
385
- }
386
 
387
- #tooltip #edcal-content-row .edcal-form-label {
388
- position: static;
389
- float: left;
390
- }
391
 
392
- #tooltip #edcal-content-row .edcal-form-control {
393
- position: static;
394
- margin-left: 7em;
395
  }
396
 
397
- #tooltip #edcal-status-section h4 {
398
  width: 4em;
399
  }
400
 
401
- #edcal-status-section .edcal-form-control {
402
- left: 7em;
403
- width: auto;
404
- }
405
-
406
- #tooltip #cal_mediabar {
407
- font-size: 11px;
408
  }
409
 
410
- #tooltip #media-buttons {
411
- padding: 5px 0;
 
 
412
  }
413
 
414
- #tooltip {
415
- background: white;
416
- padding: 1em;
417
- padding-bottom: 0em;
418
- border: thin solid lightgray;
419
- -moz-border-radius:6px;
420
- -webkit-border-radius:6px;
421
- -moz-box-shadow:0 4px 8px #C8C8C8;
422
- position: relative;
423
- z-index: 25;
424
  }
425
 
426
- .newposttip {
427
- padding: 1em 1em 1em 0.5em;
428
  position: relative;
 
429
  }
430
 
431
- #edcal-title {
432
- padding-top: 0.5em;
433
- padding-bottom: 0.5em;
 
 
 
 
 
 
 
 
434
  }
435
 
436
- #edcal-title-edit-field {
437
- padding: 0.25em;
438
  }
439
 
440
- #edcal-title-new-field {
441
- padding: 0.25em;
442
- width: 100%;
443
- }
444
 
445
- #edcal-title-new-section {
446
- padding-top: 1em;
447
- padding-bottom: 0.5em;
448
  }
449
 
450
- #tipclose {
451
- width: 16px;
452
- height: 15px;
453
- position: absolute;
454
- top: -5px;
455
- right: -4px;
456
  }
457
 
458
- #edcal-title-box {
459
- display: none;
460
- padding-right: 15px;
461
- }
462
-
463
- #edcal-title-edit {
464
- font-size: 11px;
 
 
 
465
  }
466
 
467
- #nextmonth, #prevmonth {
468
- /*padding: 0.5em;*/
469
- font-size:12px;
 
 
 
 
 
 
470
  }
471
 
472
- #edcal_weeks_pref {
473
- width: 4em;
474
  }
22
  /* This height will be reset with JavaScript */
23
  height: 100%;
24
  margin-top: 5px;
25
+ margin-left: 4px;
26
+ margin-right: 2px;
27
  }
28
 
29
  #cal_cont, #edcal_scrollable {
47
  }
48
 
49
  .day {
50
+ border: 1px solid rgb(164, 164, 164); /* this will add to total height in IE. So, 100% + 2px */
51
  -moz-border-radius-topright:3px;
52
  -moz-border-radius-topleft:3px;
53
  -webkit-border-top-left-radius:3px;
81
  height: 100%;
82
  }
83
 
84
+ .firstOfMonth .dayobj {
85
+ background-color: #eaf2fb;
86
+ }
87
+
88
  .daylabel {
89
  position: absolute;
90
  top: 0px;
152
  font-weight: bold;
153
  text-align: left;
154
  text-indent: 5px;
155
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
156
+ color: #000;
157
+ line-height: 2.4em;
 
 
158
  min-width: 10em;
 
 
 
 
 
 
 
 
159
  }
160
 
161
  .dayheadcont {
162
  position: relative;
163
  height: 28px;
164
  top: 0px;
 
 
 
 
 
165
  }
166
 
167
  .dayhead.firstday {
168
  margin-left: 5px;
 
169
  }
170
 
171
  .dayhead.lastday {
 
172
  }
173
 
174
  #cal_cont {
175
+ border:1px solid #dfdfdf;
176
+ padding-bottom:1px;
177
+ -moz-border-radius:4px;
178
+ -webkit-border-radius:4px;
 
 
 
179
  }
180
 
181
  #topbar {
182
  margin-bottom: 1em;
183
+ width: 100%;
184
  margin-top: 1em;
185
  position: relative;
186
  height: 2em;
188
 
189
  #topleft {
190
  position: absolute;
191
+ left: 0px;
192
  top: -14px;
193
  width: auto;
194
  }
195
 
196
  #topright {
197
  position: absolute;
198
+ right: 0px;
199
  top: 0px;
200
  width: auto;
201
  text-align: right;
203
 
204
  #moveToToday {
205
  margin-left: 2em;
206
+ margin-right: 6px;
207
  text-decoration: none;
208
  }
209
 
236
 
237
  .postlist {
238
  position: relative;
239
+ top:24px; /* 1.5em; */
240
+ height: 88%;
241
  overflow: auto;
242
  }
243
 
249
  color: #21759B;
250
  }*/
251
 
 
 
 
252
 
253
+ /* Quick Add/Edit Tooltip */
 
 
 
 
 
 
 
 
254
 
255
  #tooltip {
256
+ background: #f9f9f9;
257
+ width:500px;
258
+ border:1px solid #D3D3D3;
259
+ -moz-border-radius:6px;
260
+ -webkit-border-radius:6px;
261
+ -moz-box-shadow:0 4px 8px #C8C8C8;
262
+ position: relative;
263
+ z-index: 25;
264
  }
265
 
266
+ .tooltip {
267
+ padding: 1em;
268
+ position: relative;
269
  }
270
 
271
  #tooltiptitle {
272
  font-weight:bold;
273
+ padding-left:1em;
274
+ padding-right:1em;
275
+ height:30px;
276
+ line-height:30px;
277
+ position:relative;
278
  text-shadow: #fff 0px 1px 0px;
279
  }
280
 
281
+ #tipclose {
282
+ width: 15px;
283
+ height: 15px;
284
+ position: absolute;
285
+ top:8px;
286
+ right:1em;
287
  }
288
 
289
+ #tooltip .inline-edit-row fieldset {
290
+ padding-bottom:1.2em;
291
  }
292
 
293
+ #edcal-title {
294
+ padding-top: 0.5em;
295
+ padding-bottom: 0.5em;
 
 
 
 
296
  }
297
 
298
+ /*
299
+ TinyMCE Rich Editor and Media buttons are not currently used
300
+ #tooltip .mceEditor,*/
301
+ #tooltip textarea {
302
+ height: 15em;
303
  }
304
 
305
+ /*
306
+ #tooltip #cal_mediabar {
307
+ font-size: 11px;
308
  }
309
 
310
+ #tooltip #media-buttons {
311
+ padding: 5px 0;
312
+ }*/
313
+
314
  #tooltip #edcal-time {
315
+ width: 9em;
316
  background-color: white;
317
  }
318
 
319
+ #edcal-author-p {
320
+ line-height:1.8em;
321
+ padding-left:0.2em;
322
  }
323
 
324
+ #edit-slug-buttons {
325
  position: relative;
326
+ margin:0;
327
+ padding:0 0 0.5em 0;
328
  }
329
 
330
+ #newPostScheduleButton {
 
 
 
 
 
 
 
331
  position: absolute;
332
+ right: 0;
333
+ top: -5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
 
336
+ #newPostScheduleButton.disabled {
337
+ color: #AAAAAA;
 
 
 
 
338
  }
339
 
340
+ /* End Add/Edit Tooltip */
 
 
 
 
 
341
 
 
 
 
 
342
 
343
+ #nextmonth, #prevmonth {
344
+ /*padding: 0.5em;*/
345
+ font-size:12px;
346
  }
347
 
348
+ #edcal_weeks_pref {
349
  width: 4em;
350
  }
351
 
352
+ #calendar-fields-prefs {
353
+ margin-bottom: 1em;
 
 
 
 
 
354
  }
355
 
356
+ #calendar-fields-prefs label {
357
+ margin-left: 1em;
358
+ line-height: normal;
359
+ vertical-align: baseline;
360
  }
361
 
362
+ #edcal-qunit {
363
+ margin-top: 2em;
 
 
 
 
 
 
 
 
364
  }
365
 
366
+ .optionscolorrow {
 
367
  position: relative;
368
+ height: 20px;
369
  }
370
 
371
+ .optionscolor {
372
+ width: 16px;
373
+ height: 16px;
374
+ margin-left: 1em;
375
+ display: block;
376
+ position: absolute;
377
+ top: 2px;
378
+ -moz-border-radius: 3px;
379
+ -webkit-border-radius: 3px;
380
+ opacity: 0.5;
381
+
382
  }
383
 
384
+ .optionscolor:hover {
385
+ opacity: 1;
386
  }
387
 
 
 
 
 
388
 
389
+ .colorselected {
390
+ opacity: 1;
 
391
  }
392
 
393
+ .colorlabel {
394
+ -moz-border-radius: 4px;
395
+ -webkit-border-radius: 4px;
 
 
 
396
  }
397
 
398
+ #feedbacksection {
399
+ margin-top: 15px;
400
+ padding: 1em;
401
+ -moz-border-radius: 3px;
402
+ -webkit-border-radius: 3px;
403
+ width: 55%;
404
+ min-width: 500px;
405
+ background-color: #eaf2fb;
406
+ font-family:"Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
407
+ font-size:11px;
408
  }
409
 
410
+ #feedbacksection h2 {
411
+ font-family:"Lucida Grande",Verdana,Arial,"Bitstream Vera Sans",sans-serif;
412
+ font-size: 14.3px;
413
+ font-style: normal;
414
+ font-weight: bold;
415
+ color: #333333;
416
+ padding: 0px;
417
+ margin: 0px;
418
+ margin-top: -10px;
419
  }
420
 
421
+ #nofeedbacklink {
422
+ margin-left: 1em;
423
  }
edcal.js CHANGED
@@ -68,6 +68,27 @@ var edcal = {
68
  in the calendar.
69
  */
70
  weeksPref: 3,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /*
72
  * True if the calendar is in the process of moving
73
  */
@@ -83,10 +104,17 @@ var edcal = {
83
  during a drag.
84
  */
85
  isDragScrolling: false,
 
 
 
 
 
 
 
86
 
87
  /*
88
- This is the position of the calendar. It is an array with
89
- two fields: top and bottom.
90
  */
91
  position: null,
92
 
@@ -307,6 +335,15 @@ var edcal = {
307
  */
308
  monthstyle = "month-past";
309
  }
 
 
 
 
 
 
 
 
 
310
  return monthstyle+' '+daystyle;
311
  },
312
 
@@ -340,37 +377,38 @@ var edcal = {
340
  createRow: function(/*jQuery*/ parent, /*bool*/ append) {
341
  var _date = edcal._wDate.clone();
342
 
343
- var newrow = '<div class="rowcont" id="' + 'row' + edcal._wDate.toString("ddMMyyyy") + '">' +
344
- '<div id="' + 'row' + edcal._wDate.toString("ddMMyyyy") + 'row" class="row">';
345
  for (var i = 0; i < 7; i++) {
346
  /*
347
  * Adding all of these calls in the string is kind of messy. We
348
  * could do this with the JQuery live function, but there are a lot
349
  * of days in the calendar and the live function gets a little slow.
350
  */
351
- newrow += '<div onmouseover="edcal.showAddPostLink(\'' + _date.toString("ddMMyyyy") + '\');" ' +
352
- 'onmouseout="edcal.hideAddPostLink(\'' + _date.toString("ddMMyyyy") + '\');" ' +
353
- 'id="' + _date.toString("ddMMyyyy") + '" class="day ' +
354
  edcal.getDateClass(_date) + ' ' +
355
  _date.toString("dddd").toLowerCase() + ' month-' +
356
  _date.toString("MM").toLowerCase() + '">';
357
 
358
  newrow += '<div class="dayobj">';
359
-
360
  newrow += '<a href="#" adddate="' + _date.toString("MMMM d") + '" class="daynewlink" title="' + edcal.str_newpost + _date.toString("MMMM d") + '" ' +
361
  'onclick="return false;">' + edcal.str_addPostLink + '</a>';
362
 
363
- newrow += '<div class="daylabel">';
364
  if (_date.toString("dd") == "01") {
365
- newrow += _date.toString("MMM d");
366
  } else {
367
- newrow += _date.toString("d");
368
  }
 
 
369
  newrow += '</div>';
370
 
371
  newrow += '<ul class="postlist">';
372
 
373
- newrow += edcal.getPostItems(_date.toString("ddMMyyyy"));
374
 
375
  newrow += '</ul>';
376
 
@@ -391,11 +429,11 @@ var edcal = {
391
  /*
392
  * This is the horizontal alignment of an individual week
393
  */
394
- edcal.alignGrid("#row" + edcal._wDate.toString("ddMMyyyy") + "row", 7, 13.9, 100, 0.5);
395
 
396
- edcal.draggablePost('#row' + edcal._wDate.toString("ddMMyyyy") + ' li.post');
397
 
398
- jQuery('#row' + edcal._wDate.toString("ddMMyyyy") + ' > div > div.day').droppable({
399
  hoverClass: 'day-active',
400
  accept: function(ui) {
401
  /*
@@ -423,36 +461,43 @@ var edcal = {
423
  //output('ui.draggable.html(): ' + ui.draggable.html());
424
 
425
  var dayId = ui.draggable.parent().parent().parent().attr("id");
426
-
427
- // Step 0. Get the post object from the map
428
- var post = edcal.findPostForId(ui.draggable.parent().parent().parent().attr("id"),
429
- ui.draggable.attr("id"));
430
-
431
- // Step 1. Remove the post from the posts map
432
- edcal.removePostFromMap(ui.draggable.parent().parent().parent().attr("id"),
433
- ui.draggable.attr("id"));
434
-
435
- // Step 2. Remove the old element from the old parent.
436
- jQuery('#' + ui.draggable.attr("id")).remove();
437
-
438
- // Step 3. Add the item to the new DOM parent
439
- jQuery('#' + jQuery(this).attr("id") + ' .postlist').append(edcal.createPostItem(post,
440
- jQuery(this).attr("id")));
441
-
442
- if (dayId == jQuery(this).attr("id")) {
443
- /*
444
- If they dropped back on to the day they started with we
445
- don't want to go back to the server.
446
- */
447
- edcal.draggablePost('#' + jQuery(this).attr("id") + ' .post');
448
- } else {
449
- // Step6. Update the date on the server
450
- edcal.changeDate(jQuery(this).attr("id"), post);
451
- }
452
  }
453
  });
454
 
455
- return jQuery('row' + edcal._wDate.toString("ddMMyyyy"));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  },
457
 
458
  /*
@@ -566,7 +611,7 @@ var edcal = {
566
  /*
567
  Deletes the post specified. Will only be executed once the user clicks the confirm link to proceed.
568
  */
569
- deletePost: function(/*Post ID*/ postId) {
570
 
571
  var url = edcal.ajax_url() + "&action=edcal_deletepost&postid=" + postId;
572
 
@@ -589,11 +634,15 @@ var edcal = {
589
  } else {
590
  edcal.output('Finished deleting the post: "' + res.post.title + '"');
591
  }
 
 
 
 
592
  },
593
  error: function(xhr) {
594
  edcal.showError(edcal.general_error);
595
  if (xhr.responseText) {
596
- edcal.output("xhr.responseText: " + xhr.responseText);
597
  }
598
  }
599
  });
@@ -622,79 +671,6 @@ var edcal = {
622
  return ajaxurl + "?_wpnonce=" + edcal.wp_nonce;
623
  },
624
 
625
- /*
626
- This is an AJAX function to save the past title when
627
- the user presses the save button on the tooltip.
628
- */
629
- saveTitle: function(/*string*/ postId) {
630
- edcal.output("Saving the new title " + jQuery("#edcal-title-edit-field").val() + " for post " + postId);
631
- var url = edcal.ajax_url() + "&action=edcal_changetitle&postid=" + postId +
632
- "&title=" + encodeURIComponent(jQuery("#edcal-title-edit-field").val());
633
-
634
- jQuery("#post-" + postId).addClass("loadingclass");
635
-
636
- jQuery("#tooltip").hide();
637
-
638
- jQuery.ajax( {
639
- url: url,
640
- type: "POST",
641
- processData: false,
642
- timeout: 100000,
643
- dataType: "json",
644
- success: function(res) {
645
- edcal.removePostItem(res.post.date, "post-" + res.post.id);
646
- if (res.error) {
647
- /*
648
- * If there was an error we need to remove the dropped
649
- * post item.
650
- */
651
- if (res.error === edcal.NONCE_ERROR) {
652
- edcal.showError(edcal.checksum_error);
653
- }
654
- }
655
- edcal.addPostItem(res.post, res.post.date);
656
- edcal.addPostItemDragAndToolltip(res.post.date);
657
- },
658
- error: function(xhr) {
659
- edcal.showError(edcal.general_error);
660
- if (xhr.responseText) {
661
- edcal.output("xhr.responseText: " + xhr.responseText);
662
- }
663
- }
664
- });
665
- },
666
-
667
- /*
668
- * Cancels the edit title action in the tooltip.
669
- */
670
- cancelEditTitle: function(/*string*/ postTitle) {
671
- jQuery("#edcal-title-edit-field").val(postTitle);
672
-
673
- jQuery("#edcal-title-box").hide();
674
- jQuery("#edcal-title").show();
675
-
676
- },
677
-
678
- /*
679
- * Shows the edit title UI in the tooltip.
680
- */
681
- editTitle: function() {
682
- jQuery("#edcal-title").hide();
683
- jQuery("#edcal-title-box").show();
684
-
685
- jQuery("#edcal-title-edit-field").focus();
686
- jQuery("#edcal-title-edit-field").select();
687
- },
688
-
689
- /*
690
- Switches back to the normal tooltip title view
691
- and closes the tooltip. (NOT USED)
692
- */
693
- closeTooltip: function() {
694
- edcal.cancelEditTitle();
695
- jQuery("#tooltip").hide();
696
- },
697
-
698
  /*
699
  NOT USED
700
  */
@@ -719,7 +695,7 @@ var edcal = {
719
  var post = {
720
  id: 0,
721
  date: date,
722
- formatteddate: edcal.getDayFromDayId(date).toString("MMMM d"),
723
  time: formattedtime
724
  };
725
  edcal.showForm(post);
@@ -750,7 +726,7 @@ var edcal = {
750
  * doEdit - should we edit the post immediately? if true we send the user
751
  * to the edit screen for their new post.
752
  */
753
- savePost: function(/*object*/ post, /*boolean*/ doEdit, /*boolean*/ doPublish) {
754
  if(typeof(post) === 'undefined' || post === null) {
755
  post = edcal.serializePost();
756
  }
@@ -769,7 +745,6 @@ var edcal = {
769
  but we still need to work aorund the issue. Hackito
770
  ergo sum.
771
  */
772
- edcal.output("post.time: " + post.time);
773
  if (post.time.toUpperCase() === "12:00 PM") {
774
  post.time = "12:00";
775
  } else if (post.time.toUpperCase() === "12:30 PM") {
@@ -780,8 +755,6 @@ var edcal = {
780
  post.time = "00:30";
781
  }
782
 
783
- edcal.output("post.time after: " + post.time);
784
-
785
  var time;
786
  if(post.time != '') {
787
  time = Date.parse(post.time);
@@ -843,13 +816,17 @@ var edcal = {
843
  edcal.addPostItemDragAndToolltip(res.post.date);
844
  }
845
  }
 
 
 
 
846
  },
847
  error: function(xhr) {
848
  jQuery("#edit-slug-buttons").removeClass("tiploading");
849
  jQuery('#tooltip').hide();
850
  edcal.showError(edcal.general_error);
851
  if (xhr.responseText) {
852
- edcal.output("xhr.responseText: " + xhr.responseText);
853
  }
854
  }
855
  });
@@ -880,10 +857,11 @@ var edcal = {
880
  if(!post.id) {
881
  jQuery('#tooltiptitle').text(edcal.str_newpost_title + post.formatteddate);
882
  } else {
883
- jQuery('#tooltiptitle').text(edcal.str_edit_post_title);
884
- jQuery('#tooltip h3').html('<span class="tiptitle">' + post.title + '</span><br />' +
885
- '<span class="tipauthor">' + edcal.str_by + ' ' + post.author + '</span>');
886
-
 
887
  // add post info to form
888
  jQuery('#edcal-title-new-field').val(post.title);
889
  jQuery('#content').val(post.content);
@@ -909,7 +887,7 @@ var edcal = {
909
  If you have a status that isn't draft or future we
910
  just make it read only.
911
  */
912
- if (post.status && post.status !== 'draft' && post.status !== 'future') {
913
  jQuery('#edcal-status').attr('disabled', 'true');
914
  jQuery('#edcal-status').append('<option class="temp" value="' + post.status + '">' + post.status + '</option>');
915
  jQuery('#edcal-status').val(post.status);
@@ -938,6 +916,7 @@ var edcal = {
938
  jQuery("#edcal-title-new-field").focus();
939
  jQuery("#edcal-title-new-field").select();
940
 
 
941
  tb_init('a.thickbox, area.thickbox, input.thickbox');
942
 
943
  edCanvas = document.getElementById('content');
@@ -949,6 +928,7 @@ var edcal = {
949
  tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
950
  }
951
  });
 
952
  },
953
 
954
  /*
@@ -956,7 +936,6 @@ var edcal = {
956
  */
957
  hideForm: function( ) {
958
  jQuery('#tooltip').hide();
959
- edcal.cancelEditTitle();
960
  edcal.resetForm();
961
  },
962
 
@@ -973,7 +952,7 @@ var edcal = {
973
  jQuery('#newPostScheduleButton').text(edcal.str_publish);
974
 
975
  jQuery('#tooltiptitle').text('');
976
- jQuery('#tooltip h3').html('');
977
 
978
  jQuery('#edcal-status').removeAttr('disabled');
979
 
@@ -1101,30 +1080,44 @@ var edcal = {
1101
  posttitle = "[No Title]";
1102
  }
1103
 
1104
- if ((post.status === "draft" ||
1105
- post.status === "pending") &&
1106
- post.sticky === '1') {
1107
- /*
1108
- * Then this post is a sticky draft
1109
- */
1110
- posttitle += edcal.str_draft_sticky;
1111
- } else if (post.sticky === '1') {
1112
- posttitle += edcal.str_sticky;
1113
- } else if (post.status === "draft" ||
1114
- post.status === "pending") {
1115
- posttitle += edcal.str_draft;
1116
- } else if (post.status !== "publish" &&
1117
- post.status !== "future" &&
1118
- post.status !== "pending") {
1119
- /*
1120
- There are some WordPress plugins that let you specify
1121
- custom post status. In that case we just want to show
1122
- you the status.
1123
- */
1124
- posttitle += ' [' + post.status + ']';
 
 
 
 
 
 
 
 
1125
  }
1126
 
1127
- posttitle = '<span class="posttime">' + post.formattedtime + '</span> ' + posttitle;
 
 
 
 
 
 
1128
 
1129
  var classString = '';
1130
 
@@ -1158,7 +1151,7 @@ var edcal = {
1158
  We want to set a class for the cell that represents the current day so we can
1159
  give it a background color.
1160
  */
1161
- jQuery('#' + Date.today().toString("ddMMyyyy")).addClass("today");
1162
  },
1163
 
1164
  /*
@@ -1167,13 +1160,13 @@ var edcal = {
1167
  calendar based on the window height.
1168
  */
1169
  getCalHeight: function() {
1170
- var myHeight = jQuery(window).height() - 225;
1171
 
1172
  /*
1173
  We don't want to make the calendar too short even if the
1174
  user's screen is super short.
1175
  */
1176
- return Math.max(myHeight, 600);
1177
  },
1178
 
1179
  /*
@@ -1181,7 +1174,7 @@ var edcal = {
1181
  True moves the calendar down into the future and false moves the calendar
1182
  up into the past.
1183
  */
1184
- move: function(steps, direction) {
1185
  /*
1186
  * If the add/edit post form is visible, don't go anywhere.
1187
  */
@@ -1252,10 +1245,12 @@ var edcal = {
1252
  */
1253
  if (!direction) {
1254
  edcal.getPosts(edcal._wDate.clone(),
1255
- edcal._wDate.clone().add(7 * (edcal.steps + 1)).days());
 
1256
  } else {
1257
  edcal.getPosts(edcal._wDate.clone().add(-7 * (edcal.steps + 1)).days(),
1258
- edcal._wDate.clone());
 
1259
  }
1260
 
1261
  edcal.steps = 0;
@@ -1497,6 +1492,61 @@ var edcal = {
1497
  'width: ' + (jQuery(".rowcont:eq(2) .day:first").width() - 5) + 'px;' +
1498
  '</style>}');
1499
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1500
 
1501
  /*
1502
  * Initializes the calendar
@@ -1510,6 +1560,8 @@ var edcal = {
1510
  return;
1511
  }
1512
 
 
 
1513
  jQuery("#loading").hide();
1514
 
1515
  jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
@@ -1590,7 +1642,7 @@ var edcal = {
1590
  edcal.move(edcal.weeksPref, false);
1591
  return false;
1592
  } else if (evt.keyCode === 27) { //escape key
1593
- edcal.closeTooltip();
1594
  return false;
1595
  }
1596
  });
@@ -1660,15 +1712,6 @@ var edcal = {
1660
  }
1661
  });
1662
 
1663
- jQuery("#edcal-title-edit-field").live("keyup", function(evt) {
1664
- if (evt.keyCode == 13) { // enter key
1665
- /*
1666
- * If the user presses enter we want to save the post title.
1667
- */
1668
- edcal.saveTitle(jQuery(this).attr("postid"));
1669
- }
1670
- });
1671
-
1672
  jQuery("#edcal_weeks_pref").live("keyup", function(evt) {
1673
  if (jQuery("#edcal_weeks_pref").val().length > 0) {
1674
  jQuery("#edcal_applyoptions").removeClass("disabled");
@@ -1693,85 +1736,11 @@ var edcal = {
1693
  });
1694
  },
1695
 
1696
- /*
1697
- * When the user presses the new post link on each calendar cell they get
1698
- * a tooltip which prompts them to edit the title of the new post. Once
1699
- * they provide a title we call this function.
1700
- *
1701
- * date - the date for the new post
1702
- * title - the title for the new post
1703
- * doEdit - should we edit the post immediately? if true we send the user
1704
- * to the edit screen for their new post.
1705
- */
1706
- createNewDraft: function(/*string*/ date, /*string*/ title, /*string*/ content, /*boolean*/ doEdit) {
1707
- if (!title || title === "") {
1708
- return;
1709
- }
1710
- edcal.output("createNewDraft(" + date + ", " + title + ")");
1711
-
1712
- jQuery("#edit-slug-buttons").addClass("tiploading");
1713
- /*
1714
- * We don't really let them set a time in the calendar, so we
1715
- * put a default post time of 10:00 AM.
1716
- */
1717
- var formattedDate = encodeURIComponent(edcal.getDayFromDayId(date).toString(edcal.wp_dateFormat) + " 10:00:00");
1718
- var url = edcal.ajax_url() + "&action=edcal_newdraft&";
1719
- var postData = "date=" + formattedDate +
1720
- "&title=" + encodeURIComponent(title) +
1721
- "&content=" + encodeURIComponent(content);
1722
-
1723
- jQuery.ajax( {
1724
- url: url,
1725
- type: "POST",
1726
- processData: false,
1727
- data: postData,
1728
- timeout: 100000,
1729
- dataType: "json",
1730
- success: function(res) {
1731
- jQuery("#edit-slug-buttons").removeClass("tiploading");
1732
- jQuery('#tooltip').hide();
1733
- if (res.error) {
1734
- /*
1735
- * If there was an error we need to remove the dropped
1736
- * post item.
1737
- */
1738
- if (res.error === edcal.NONCE_ERROR) {
1739
- edcal.showError(edcal.checksum_error);
1740
- }
1741
- return;
1742
- }
1743
-
1744
- if (!res.post) {
1745
- edcal.showError("There was an error creating a new post for your blog.");
1746
- } else {
1747
- if (doEdit) {
1748
- /*
1749
- * If the user wanted to edit the post then we redirect
1750
- * them to the edit page.
1751
- */
1752
- window.location = res.post.editlink.replace("&amp;", "&");
1753
- } else {
1754
- edcal.addPostItem(res.post, res.post.date);
1755
- edcal.addPostItemDragAndToolltip(res.post.date);
1756
- }
1757
- }
1758
- },
1759
- error: function(xhr) {
1760
- jQuery("#edit-slug-buttons").removeClass("tiploading");
1761
- jQuery('#tooltip').hide();
1762
- edcal.showError(edcal.general_error);
1763
- if (xhr.responseText) {
1764
- edcal.output("xhr.responseText: " + xhr.responseText);
1765
- }
1766
- }
1767
- });
1768
- },
1769
-
1770
  /*
1771
  This function makes an AJAX call and changes the date of
1772
  the specified post on the server.
1773
  */
1774
- changeDate: function(/*string*/ newdate, /*Post*/ post) {
1775
  edcal.output('Changing the date of "' + post.title + '" to ' + newdate);
1776
  var newdateFormatted = edcal.getDayFromDayId(newdate).toString(edcal.wp_dateFormat);
1777
 
@@ -1793,25 +1762,31 @@ var edcal = {
1793
  * If there was an error we need to remove the dropped
1794
  * post item.
1795
  */
1796
- edcal.output('removePostItem(' + newdate + ', "post-" + ' + res.post.id + ');');
1797
  edcal.removePostItem(newdate, "post-" + res.post.id);
1798
  if (res.error === edcal.CONCURRENCY_ERROR) {
1799
- edcal.showError(edcal.concurrency_error + '<br />' + res.post.title);
1800
  } else if (res.error === edcal.PERMISSION_ERROR) {
1801
- edcal.showError(edcal.permission_error);
1802
  } else if (res.error === edcal.NONCE_ERROR) {
1803
- edcal.showError(edcal.checksum_error);
1804
  }
1805
- }
1806
-
1807
  edcal.removePostItem(res.post.date, "post-" + res.post.id);
1808
  edcal.addPostItem(res.post, res.post.date);
1809
  edcal.addPostItemDragAndToolltip(res.post.date);
 
 
 
 
1810
  },
1811
- error: function(xhr) {
1812
  edcal.showError(edcal.general_error);
 
 
 
1813
  if (xhr.responseText) {
1814
- edcal.output("xhr.responseText: " + xhr.responseText);
1815
  }
1816
  }
1817
  });
@@ -1822,7 +1797,7 @@ var edcal = {
1822
  Makes an AJAX call to get the posts from the server within the
1823
  specified dates.
1824
  */
1825
- getPosts: function(/*Date*/ from, /*Date*/ to) {
1826
  edcal.output("Getting posts from " + from + " to " + to);
1827
 
1828
  var shouldGet = edcal.cacheDates[from];
@@ -1834,13 +1809,15 @@ var edcal = {
1834
  * it somewhat, but we could get much better about this.
1835
  */
1836
  edcal.output("Using cached results for posts from " + from.toString("dd-MMM-yyyy") + " to " + to.toString("dd-MMM-yyyy"));
 
 
 
 
1837
  return;
1838
  }
1839
 
1840
  edcal.cacheDates[from] = true;
1841
 
1842
- edcal.output("edcal.ajax_url(): " + edcal.ajax_url());
1843
-
1844
  var url = edcal.ajax_url() + "&action=edcal_posts&from=" + from.toString("yyyy-MM-dd") + "&to=" + to.toString("yyyy-MM-dd");
1845
 
1846
  jQuery("#loading").show();
@@ -1914,11 +1891,16 @@ var edcal = {
1914
  edcal.addPostItemDragAndToolltip(postDate);
1915
  });
1916
  }, 300);
 
 
 
 
 
1917
  },
1918
  error: function(xhr) {
1919
  edcal.showError(edcal.general_error);
1920
  if (xhr.responseText) {
1921
- edcal.output("xhr.responseText: " + xhr.responseText);
1922
  }
1923
  }
1924
  });
@@ -1969,7 +1951,7 @@ var edcal = {
1969
 
1970
  edcal.showError(edcal.general_error);
1971
  if (xhr.responseText) {
1972
- edcal.output("xhr.responseText: " + xhr.responseText);
1973
  }
1974
  return false;
1975
  }
@@ -1988,7 +1970,7 @@ var edcal = {
1988
  '<a class="show-settings" ' +
1989
  'id="show-edcal-settings-link" ' +
1990
  'onclick="edcal.toggleOptions(); return false;" ' +
1991
- 'href="#screen-options" ' +
1992
  'style="background-image: url(images/screen-options-right.gif);">' + edcal.str_screenoptions + '</a>' +
1993
  '</div>';
1994
 
@@ -2006,9 +1988,31 @@ var edcal = {
2006
  */
2007
  edcal.helpMeta = jQuery("#contextual-help-wrap").html();
2008
 
2009
- var optionsHtml = '<h5>' + edcal.str_optionsheader + '</h5>' +
2010
- '<div class="metabox-prefs">' +
2011
- edcal.str_show + '<select id="edcal_weeks_pref" ' + 'class="screen-per-page" title="' + edcal.str_weekstt + '"> ';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2012
 
2013
  var weeks = parseInt(edcal.weeksPref, 10);
2014
  for (i = 1; i < 6; i++) {
@@ -2020,13 +2024,23 @@ var edcal = {
2020
  }
2021
 
2022
  optionsHtml += '</select>' +
2023
- edcal.str_show2 + '<br /><br />' +
2024
- '<button id="edcal_applyoptions" onclick="edcal.saveOptions(); return false;" class="save button">' + edcal.str_apply + '</button>' +
2025
  '</div>';
2026
 
 
 
 
 
 
 
 
 
 
 
 
2027
  jQuery("#contextual-help-wrap").html(optionsHtml);
2028
 
2029
- jQuery("#contextual-help-link-wrap").hide();
2030
 
2031
  jQuery('#contextual-help-wrap').slideDown('normal');
2032
 
@@ -2042,7 +2056,52 @@ var edcal = {
2042
  edcal.helpMeta = null;
2043
 
2044
  jQuery("#show-edcal-settings-link").css("background-image", "url(images/screen-options-right.gif)");
2045
- jQuery("#contextual-help-link-wrap").show();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2046
  }
2047
  },
2048
 
@@ -2067,6 +2126,10 @@ var edcal = {
2067
  var url = edcal.ajax_url() + "&action=edcal_saveoptions&weeks=" +
2068
  encodeURIComponent(jQuery("#edcal_weeks_pref").val());
2069
 
 
 
 
 
2070
  jQuery.ajax( {
2071
  url: url,
2072
  type: "POST",
@@ -2083,7 +2146,7 @@ var edcal = {
2083
  error: function(xhr) {
2084
  edcal.showError(edcal.general_error);
2085
  if (xhr.responseText) {
2086
- edcal.output("xhr.responseText: " + xhr.responseText);
2087
  }
2088
  }
2089
  });
@@ -2108,7 +2171,32 @@ var edcal = {
2108
  if (window.console) {
2109
  console.error(msg);
2110
  }
2111
- humanMsg.displayMsg(msg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2112
  }
2113
  };
2114
 
@@ -2125,4 +2213,14 @@ jQuery.fn.center = function () {
2125
 
2126
  jQuery(document).ready(function(){
2127
  edcal.init();
 
 
 
 
 
 
 
 
 
 
2128
  });
68
  in the calendar.
69
  */
70
  weeksPref: 3,
71
+
72
+ /*
73
+ This is a preference value indicating if you see the post status
74
+ */
75
+ statusPref: true,
76
+
77
+ /*
78
+ This is a preference value indicating if you see the post author
79
+ */
80
+ authorPref: false,
81
+
82
+ /*
83
+ This is a preference value indicating if you see the post time
84
+ */
85
+ timePref: true,
86
+
87
+ /*
88
+ This is a preference value indicating if we should prompt for feeback
89
+ */
90
+ doFeedbackPref: true,
91
+
92
  /*
93
  * True if the calendar is in the process of moving
94
  */
104
  during a drag.
105
  */
106
  isDragScrolling: false,
107
+
108
+ /*
109
+ * This is the format we use to dates that we use as IDs in the
110
+ * calendar. It is independant of the visible date which is
111
+ * formatted based on the user's locale.
112
+ */
113
+ internalDateFormat: 'ddMMyyyy',
114
 
115
  /*
116
+ This is the position of the calendar on the screen in pixels.
117
+ It is an array with two fields: top and bottom.
118
  */
119
  position: null,
120
 
335
  */
336
  monthstyle = "month-past";
337
  }
338
+
339
+ if (date.toString("dd") == "01") {
340
+ /*
341
+ * This this date is the first day of the month
342
+ */
343
+ daystyle += ' firstOfMonth';
344
+ }
345
+
346
+
347
  return monthstyle+' '+daystyle;
348
  },
349
 
377
  createRow: function(/*jQuery*/ parent, /*bool*/ append) {
378
  var _date = edcal._wDate.clone();
379
 
380
+ var newrow = '<div class="rowcont" id="' + 'row' + edcal._wDate.toString(edcal.internalDateFormat) + '">' +
381
+ '<div id="' + 'row' + edcal._wDate.toString(edcal.internalDateFormat) + 'row" class="row">';
382
  for (var i = 0; i < 7; i++) {
383
  /*
384
  * Adding all of these calls in the string is kind of messy. We
385
  * could do this with the JQuery live function, but there are a lot
386
  * of days in the calendar and the live function gets a little slow.
387
  */
388
+ newrow += '<div onmouseover="edcal.showAddPostLink(\'' + _date.toString(edcal.internalDateFormat) + '\');" ' +
389
+ 'onmouseout="edcal.hideAddPostLink(\'' + _date.toString(edcal.internalDateFormat) + '\');" ' +
390
+ 'id="' + _date.toString(edcal.internalDateFormat) + '" class="day ' +
391
  edcal.getDateClass(_date) + ' ' +
392
  _date.toString("dddd").toLowerCase() + ' month-' +
393
  _date.toString("MM").toLowerCase() + '">';
394
 
395
  newrow += '<div class="dayobj">';
396
+
397
  newrow += '<a href="#" adddate="' + _date.toString("MMMM d") + '" class="daynewlink" title="' + edcal.str_newpost + _date.toString("MMMM d") + '" ' +
398
  'onclick="return false;">' + edcal.str_addPostLink + '</a>';
399
 
 
400
  if (_date.toString("dd") == "01") {
401
+ newrow += '<div class="daylabel">' + _date.toString("MMM d");
402
  } else {
403
+ newrow += '<div class="daylabel">' + _date.toString("d");
404
  }
405
+
406
+
407
  newrow += '</div>';
408
 
409
  newrow += '<ul class="postlist">';
410
 
411
+ newrow += edcal.getPostItems(_date.toString(edcal.internalDateFormat));
412
 
413
  newrow += '</ul>';
414
 
429
  /*
430
  * This is the horizontal alignment of an individual week
431
  */
432
+ edcal.alignGrid("#row" + edcal._wDate.toString(edcal.internalDateFormat) + "row", 7, 13.9, 100, 0.5);
433
 
434
+ edcal.draggablePost('#row' + edcal._wDate.toString(edcal.internalDateFormat) + ' li.post');
435
 
436
+ jQuery('#row' + edcal._wDate.toString(edcal.internalDateFormat) + ' > div > div.day').droppable({
437
  hoverClass: 'day-active',
438
  accept: function(ui) {
439
  /*
461
  //output('ui.draggable.html(): ' + ui.draggable.html());
462
 
463
  var dayId = ui.draggable.parent().parent().parent().attr("id");
464
+
465
+ edcal.doDrop(dayId, ui.draggable.attr("id"), jQuery(this).attr("id"));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  }
467
  });
468
 
469
+ return jQuery('row' + edcal._wDate.toString(edcal.internalDateFormat));
470
+ },
471
+
472
+ /*
473
+ * Handle the drop when a user drags and drops a post.
474
+ */
475
+ doDrop: function(/*string*/ parentId, /*string*/ postId, /*string*/ newDate, /*function*/ callback) {
476
+ var dayId = parentId;
477
+
478
+
479
+ // Step 0. Get the post object from the map
480
+ var post = edcal.findPostForId(parentId, postId);
481
+
482
+ // Step 1. Remove the post from the posts map
483
+ edcal.removePostFromMap(parentId, postId);
484
+
485
+ // Step 2. Remove the old element from the old parent.
486
+ jQuery('#' + postId).remove();
487
+
488
+ // Step 3. Add the item to the new DOM parent
489
+ jQuery('#' + newDate + ' .postlist').append(edcal.createPostItem(post, newDate));
490
+
491
+ if (dayId == newDate) {
492
+ /*
493
+ If they dropped back on to the day they started with we
494
+ don't want to go back to the server.
495
+ */
496
+ edcal.draggablePost('#' + newDate + ' .post');
497
+ } else {
498
+ // Step6. Update the date on the server
499
+ edcal.changeDate(newDate, post, callback);
500
+ }
501
  },
502
 
503
  /*
611
  /*
612
  Deletes the post specified. Will only be executed once the user clicks the confirm link to proceed.
613
  */
614
+ deletePost: function(/*Post ID*/ postId, /*function*/ callback) {
615
 
616
  var url = edcal.ajax_url() + "&action=edcal_deletepost&postid=" + postId;
617
 
634
  } else {
635
  edcal.output('Finished deleting the post: "' + res.post.title + '"');
636
  }
637
+
638
+ if (callback) {
639
+ callback(res);
640
+ }
641
  },
642
  error: function(xhr) {
643
  edcal.showError(edcal.general_error);
644
  if (xhr.responseText) {
645
+ edcal.output("deletePost xhr.responseText: " + xhr.responseText);
646
  }
647
  }
648
  });
671
  return ajaxurl + "?_wpnonce=" + edcal.wp_nonce;
672
  },
673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  /*
675
  NOT USED
676
  */
695
  var post = {
696
  id: 0,
697
  date: date,
698
+ formatteddate: edcal.getDayFromDayId(date).toString(edcal.previewDateFormat),
699
  time: formattedtime
700
  };
701
  edcal.showForm(post);
726
  * doEdit - should we edit the post immediately? if true we send the user
727
  * to the edit screen for their new post.
728
  */
729
+ savePost: function(/*object*/ post, /*boolean*/ doEdit, /*boolean*/ doPublish, /*function*/ callback) {
730
  if(typeof(post) === 'undefined' || post === null) {
731
  post = edcal.serializePost();
732
  }
745
  but we still need to work aorund the issue. Hackito
746
  ergo sum.
747
  */
 
748
  if (post.time.toUpperCase() === "12:00 PM") {
749
  post.time = "12:00";
750
  } else if (post.time.toUpperCase() === "12:30 PM") {
755
  post.time = "00:30";
756
  }
757
 
 
 
758
  var time;
759
  if(post.time != '') {
760
  time = Date.parse(post.time);
816
  edcal.addPostItemDragAndToolltip(res.post.date);
817
  }
818
  }
819
+
820
+ if (callback) {
821
+ callback(res);
822
+ }
823
  },
824
  error: function(xhr) {
825
  jQuery("#edit-slug-buttons").removeClass("tiploading");
826
  jQuery('#tooltip').hide();
827
  edcal.showError(edcal.general_error);
828
  if (xhr.responseText) {
829
+ edcal.output("savePost xhr.responseText: " + xhr.responseText);
830
  }
831
  }
832
  });
857
  if(!post.id) {
858
  jQuery('#tooltiptitle').text(edcal.str_newpost_title + post.formatteddate);
859
  } else {
860
+ jQuery('#tooltiptitle').text(edcal.str_edit_post_title + edcal.getDayFromDayId(post.date).toString(edcal.previewDateFormat));
861
+
862
+ // sets the read-only author field
863
+ //jQuery('#edcal-author-p').html(post.author);
864
+
865
  // add post info to form
866
  jQuery('#edcal-title-new-field').val(post.title);
867
  jQuery('#content').val(post.content);
887
  If you have a status that isn't draft or future we
888
  just make it read only.
889
  */
890
+ if (post.status && post.status !== 'draft' && post.status !== 'future' && post.status !== 'pending') {
891
  jQuery('#edcal-status').attr('disabled', 'true');
892
  jQuery('#edcal-status').append('<option class="temp" value="' + post.status + '">' + post.status + '</option>');
893
  jQuery('#edcal-status').val(post.status);
916
  jQuery("#edcal-title-new-field").focus();
917
  jQuery("#edcal-title-new-field").select();
918
 
919
+ /*
920
  tb_init('a.thickbox, area.thickbox, input.thickbox');
921
 
922
  edCanvas = document.getElementById('content');
928
  tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
929
  }
930
  });
931
+ */
932
  },
933
 
934
  /*
936
  */
937
  hideForm: function( ) {
938
  jQuery('#tooltip').hide();
 
939
  edcal.resetForm();
940
  },
941
 
952
  jQuery('#newPostScheduleButton').text(edcal.str_publish);
953
 
954
  jQuery('#tooltiptitle').text('');
955
+ //jQuery('#edcal-author-p').html('');
956
 
957
  jQuery('#edcal-status').removeAttr('disabled');
958
 
1080
  posttitle = "[No Title]";
1081
  }
1082
 
1083
+ if (edcal.statusPref) {
1084
+ if (post.status === "draft" &&
1085
+ post.sticky === '1') {
1086
+ /*
1087
+ * Then this post is a sticky draft
1088
+ */
1089
+ posttitle += edcal.str_draft_sticky;
1090
+ } else if (post.status === "pending" &&
1091
+ post.sticky === '1') {
1092
+ /*
1093
+ * Then this post is a sticky pending post
1094
+ */
1095
+ posttitle += edcal.str_pending_sticky;
1096
+ } else if (post.sticky === '1') {
1097
+ posttitle += edcal.str_sticky;
1098
+ } else if (post.status === "pending") {
1099
+ posttitle += edcal.str_pending;
1100
+ } else if (post.status === "draft") {
1101
+ posttitle += edcal.str_draft;
1102
+ } else if (post.status !== "publish" &&
1103
+ post.status !== "future" &&
1104
+ post.status !== "pending") {
1105
+ /*
1106
+ There are some WordPress plugins that let you specify
1107
+ custom post status. In that case we just want to show
1108
+ you the status.
1109
+ */
1110
+ posttitle += ' [' + post.status + ']';
1111
+ }
1112
  }
1113
 
1114
+ if (edcal.timePref) {
1115
+ posttitle = '<span class="posttime">' + post.formattedtime + '</span> ' + posttitle;
1116
+ }
1117
+
1118
+ if (edcal.authorPref) {
1119
+ posttitle = posttitle + ' ' + edcal.str_by + ' ' + '<span class="postauthor">' + post.author + '</span>';
1120
+ }
1121
 
1122
  var classString = '';
1123
 
1151
  We want to set a class for the cell that represents the current day so we can
1152
  give it a background color.
1153
  */
1154
+ jQuery('#' + Date.today().toString(edcal.internalDateFormat)).addClass("today");
1155
  },
1156
 
1157
  /*
1160
  calendar based on the window height.
1161
  */
1162
  getCalHeight: function() {
1163
+ var myHeight = jQuery(window).height() - jQuery('#footer').height() - jQuery('#wphead').height() - 150;
1164
 
1165
  /*
1166
  We don't want to make the calendar too short even if the
1167
  user's screen is super short.
1168
  */
1169
+ return Math.max(myHeight, 500);
1170
  },
1171
 
1172
  /*
1174
  True moves the calendar down into the future and false moves the calendar
1175
  up into the past.
1176
  */
1177
+ move: function(/*int*/ steps, /*boolean*/ direction, /*function*/ callback) {
1178
  /*
1179
  * If the add/edit post form is visible, don't go anywhere.
1180
  */
1245
  */
1246
  if (!direction) {
1247
  edcal.getPosts(edcal._wDate.clone(),
1248
+ edcal._wDate.clone().add(7 * (edcal.steps + 1)).days(),
1249
+ callback);
1250
  } else {
1251
  edcal.getPosts(edcal._wDate.clone().add(-7 * (edcal.steps + 1)).days(),
1252
+ edcal._wDate.clone(),
1253
+ callback);
1254
  }
1255
 
1256
  edcal.steps = 0;
1492
  'width: ' + (jQuery(".rowcont:eq(2) .day:first").width() - 5) + 'px;' +
1493
  '</style>}');
1494
  },
1495
+
1496
+ /*
1497
+ * Adds the feedback section
1498
+ */
1499
+ addFeedbackSection: function() {
1500
+ if (edcal.visitCount > 3 && edcal.doFeedbackPref) {
1501
+ jQuery('#edcal_main_title').after(edcal.str_feedbackmsg);
1502
+ }
1503
+ },
1504
+
1505
+ /*
1506
+ * Does the data collection. This uses Mint to collect data about the way
1507
+ * the calendar is being used.
1508
+ */
1509
+ doFeedback: function() {
1510
+ jQuery.getScript('http://www.zackgrossbart.com/edcal/mint/?js', function() {
1511
+ edcal.saveFeedbackPref();
1512
+ });
1513
+ },
1514
+
1515
+ /*
1516
+ * Sends no feedback and hides the section
1517
+ */
1518
+ noFeedback: function() {
1519
+ jQuery('#feedbacksection').hide("fast");
1520
+ edcal.saveFeedbackPref();
1521
+ },
1522
+
1523
+ /*
1524
+ * Saves the feedback preference to the server
1525
+ */
1526
+ saveFeedbackPref: function() {
1527
+ var url = edcal.ajax_url() + "&action=edcal_saveoptions&dofeedback=" + encodeURIComponent('done');
1528
+
1529
+ jQuery.ajax( {
1530
+ url: url,
1531
+ type: "POST",
1532
+ processData: false,
1533
+ timeout: 100000,
1534
+ dataType: "text",
1535
+ success: function(res) {
1536
+ jQuery('#feedbacksection').html(edcal.str_feedbackdone);
1537
+ setTimeout(function() {
1538
+ jQuery('#feedbacksection').hide("slow");
1539
+ }, 5000);
1540
+ },
1541
+ error: function(xhr) {
1542
+ edcal.showError(edcal.general_error);
1543
+ if (xhr.responseText) {
1544
+ edcal.output("saveOptions xhr.responseText: " + xhr.responseText);
1545
+ }
1546
+ }
1547
+ });
1548
+
1549
+ },
1550
 
1551
  /*
1552
  * Initializes the calendar
1560
  return;
1561
  }
1562
 
1563
+ edcal.addFeedbackSection();
1564
+
1565
  jQuery("#loading").hide();
1566
 
1567
  jQuery("#edcal_scrollable").css("height", edcal.getCalHeight() + "px");
1642
  edcal.move(edcal.weeksPref, false);
1643
  return false;
1644
  } else if (evt.keyCode === 27) { //escape key
1645
+ edcal.hideForm();
1646
  return false;
1647
  }
1648
  });
1712
  }
1713
  });
1714
 
 
 
 
 
 
 
 
 
 
1715
  jQuery("#edcal_weeks_pref").live("keyup", function(evt) {
1716
  if (jQuery("#edcal_weeks_pref").val().length > 0) {
1717
  jQuery("#edcal_applyoptions").removeClass("disabled");
1736
  });
1737
  },
1738
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1739
  /*
1740
  This function makes an AJAX call and changes the date of
1741
  the specified post on the server.
1742
  */
1743
+ changeDate: function(/*string*/ newdate, /*Post*/ post, /*function*/ callback) {
1744
  edcal.output('Changing the date of "' + post.title + '" to ' + newdate);
1745
  var newdateFormatted = edcal.getDayFromDayId(newdate).toString(edcal.wp_dateFormat);
1746
 
1762
  * If there was an error we need to remove the dropped
1763
  * post item.
1764
  */
 
1765
  edcal.removePostItem(newdate, "post-" + res.post.id);
1766
  if (res.error === edcal.CONCURRENCY_ERROR) {
1767
+ edcal.displayMessage(edcal.concurrency_error + '<br />' + res.post.title);
1768
  } else if (res.error === edcal.PERMISSION_ERROR) {
1769
+ edcal.displayMessage(edcal.permission_error);
1770
  } else if (res.error === edcal.NONCE_ERROR) {
1771
+ edcal.displayMessage(edcal.checksum_error);
1772
  }
1773
+ }
1774
+
1775
  edcal.removePostItem(res.post.date, "post-" + res.post.id);
1776
  edcal.addPostItem(res.post, res.post.date);
1777
  edcal.addPostItemDragAndToolltip(res.post.date);
1778
+
1779
+ if (callback) {
1780
+ callback(res);
1781
+ }
1782
  },
1783
+ error: function(xhr, textStatus, error) {
1784
  edcal.showError(edcal.general_error);
1785
+
1786
+ edcal.output("textStatus: " + textStatus);
1787
+ edcal.output("error: " + error);
1788
  if (xhr.responseText) {
1789
+ edcal.output("changeDate xhr.responseText: " + xhr.responseText);
1790
  }
1791
  }
1792
  });
1797
  Makes an AJAX call to get the posts from the server within the
1798
  specified dates.
1799
  */
1800
+ getPosts: function(/*Date*/ from, /*Date*/ to, /*function*/ callback) {
1801
  edcal.output("Getting posts from " + from + " to " + to);
1802
 
1803
  var shouldGet = edcal.cacheDates[from];
1809
  * it somewhat, but we could get much better about this.
1810
  */
1811
  edcal.output("Using cached results for posts from " + from.toString("dd-MMM-yyyy") + " to " + to.toString("dd-MMM-yyyy"));
1812
+
1813
+ if (callback) {
1814
+ callback();
1815
+ }
1816
  return;
1817
  }
1818
 
1819
  edcal.cacheDates[from] = true;
1820
 
 
 
1821
  var url = edcal.ajax_url() + "&action=edcal_posts&from=" + from.toString("yyyy-MM-dd") + "&to=" + to.toString("yyyy-MM-dd");
1822
 
1823
  jQuery("#loading").show();
1891
  edcal.addPostItemDragAndToolltip(postDate);
1892
  });
1893
  }, 300);
1894
+
1895
+ if (callback) {
1896
+ callback(res);
1897
+ }
1898
+
1899
  },
1900
  error: function(xhr) {
1901
  edcal.showError(edcal.general_error);
1902
  if (xhr.responseText) {
1903
+ edcal.output("getPosts xhr.responseText: " + xhr.responseText);
1904
  }
1905
  }
1906
  });
1951
 
1952
  edcal.showError(edcal.general_error);
1953
  if (xhr.responseText) {
1954
+ edcal.output("getPost xhr.responseText: " + xhr.responseText);
1955
  }
1956
  return false;
1957
  }
1970
  '<a class="show-settings" ' +
1971
  'id="show-edcal-settings-link" ' +
1972
  'onclick="edcal.toggleOptions(); return false;" ' +
1973
+ 'href="#" ' +
1974
  'style="background-image: url(images/screen-options-right.gif);">' + edcal.str_screenoptions + '</a>' +
1975
  '</div>';
1976
 
1988
  */
1989
  edcal.helpMeta = jQuery("#contextual-help-wrap").html();
1990
 
1991
+ /*
1992
+ * Set up the visible fields option
1993
+ */
1994
+ var optionsHtml = '<div class="metabox-prefs" id="calendar-fields-prefs">' +
1995
+ '<h5>' + edcal.str_show_opts + '</h5>' +
1996
+ '<label for="author-hide">' +
1997
+ '<input type="checkbox" ' + edcal.isPrefChecked(edcal.authorPref) + 'value="true" id="author-hide" ' +
1998
+ 'name="author-hide" class="hide-column-tog" />' + edcal.str_opt_author +
1999
+ '</label>' +
2000
+ '<label for="status-hide">' +
2001
+ '<input type="checkbox" ' + edcal.isPrefChecked(edcal.statusPref) + 'value="true" id="status-hide" ' +
2002
+ 'name="status-hide" class="hide-column-tog" />' + edcal.str_opt_status +
2003
+ '</label>' +
2004
+ '<label for="time-hide">' +
2005
+ '<input type="checkbox" ' + edcal.isPrefChecked(edcal.timePref) + 'value="true" id="time-hide" ' +
2006
+ 'name="time-hide" class="hide-column-tog" />' + edcal.str_opt_time +
2007
+ '</label>' +
2008
+ '</div>';
2009
+
2010
+ /*
2011
+ * Set up the number of posts option
2012
+ */
2013
+ optionsHtml += '<div class="metabox-prefs">' +
2014
+ '<h5>' + edcal.str_show_title + '</h5>' +
2015
+ '<select id="edcal_weeks_pref" ' + 'class="screen-per-page" title="' + edcal.str_weekstt + '"> ';
2016
 
2017
  var weeks = parseInt(edcal.weeksPref, 10);
2018
  for (i = 1; i < 6; i++) {
2024
  }
2025
 
2026
  optionsHtml += '</select>' +
2027
+ edcal.str_opt_weeks +
 
2028
  '</div>';
2029
 
2030
+ /*
2031
+ I started work on adding a color picker so you could choose the color for
2032
+ drafts, published posts, and scheduled posts. However, that makes the settings
2033
+ a lot more complicated and I'm not sure it is worth it.
2034
+ */
2035
+ //optionsHtml += '<h5>' + edcal.str_optionscolors + '</h5>';
2036
+ //optionsHtml += edcal.generateColorPicker(edcal.str_optionsdraftcolor, 'draft-color', 'lightgreen');
2037
+
2038
+
2039
+ optionsHtml += '<br /><button id="edcal_applyoptions" onclick="edcal.saveOptions(); return false;" class="save button">' + edcal.str_apply + '</button>';
2040
+
2041
  jQuery("#contextual-help-wrap").html(optionsHtml);
2042
 
2043
+ jQuery("#contextual-help-link-wrap").css('visibility', 'hidden');
2044
 
2045
  jQuery('#contextual-help-wrap').slideDown('normal');
2046
 
2056
  edcal.helpMeta = null;
2057
 
2058
  jQuery("#show-edcal-settings-link").css("background-image", "url(images/screen-options-right.gif)");
2059
+ jQuery("#contextual-help-link-wrap").css('visibility', '');
2060
+ }
2061
+ },
2062
+
2063
+ generateColorPicker: function(/*String*/ title, /*string*/ id, /*string*/ value) {
2064
+ var html = '<div id="' + id + '" class="optionscolorrow">';
2065
+
2066
+ html += '<span style="background-color: ' + value + ';" class="colorlabel"> ' + title + '</span> ';
2067
+
2068
+ var colors = [ "lightred", "orange", "yellow", "lightgreen", "lightblue", "purple", "lightgray" ];
2069
+
2070
+ edcal.output("colors.length: " + colors.length);
2071
+ for (var i = 0; i < colors.length; i++) {
2072
+ html += '<a href="#" class="optionscolor ';
2073
+
2074
+ if (colors[i] === value) {
2075
+ html += 'colorselected';
2076
+
2077
+ }
2078
+
2079
+ html += '" class=' + id + colors[i] + '" style="background-color: ' + colors[i] + '; left: ' + ((i * 20) + 50) + 'px" ' +
2080
+ 'onclick="edcal.selectColor(\'' + id + '\', \'' + colors[i] + '\'); return false;"></a>';
2081
+
2082
+ }
2083
+
2084
+ html += '</div>';
2085
+
2086
+ return html;
2087
+
2088
+ },
2089
+
2090
+ selectColor: function(/*string*/ id, /*string*/ value) {
2091
+ edcal.output("selectColor(" + id + ", " + value + ")");
2092
+ jQuery('#' + id + ' .colorlabel').css('background-color', value);
2093
+
2094
+ jQuery('#' + id + ' .colorselected').removeClass('colorselected');
2095
+
2096
+ jQuery('#' + id + 'value').addClass('colorselected');
2097
+
2098
+ },
2099
+
2100
+ isPrefChecked: function(/*boolean*/ prefVal) {
2101
+ if (prefVal) {
2102
+ return ' checked="checked" ';
2103
+ } else {
2104
+ return '';
2105
  }
2106
  },
2107
 
2126
  var url = edcal.ajax_url() + "&action=edcal_saveoptions&weeks=" +
2127
  encodeURIComponent(jQuery("#edcal_weeks_pref").val());
2128
 
2129
+ jQuery('#calendar-fields-prefs').find('input, textarea, select').each(function() {
2130
+ url += '&' + encodeURIComponent(this.name) + '=' + encodeURIComponent(this.checked);
2131
+ });
2132
+
2133
  jQuery.ajax( {
2134
  url: url,
2135
  type: "POST",
2146
  error: function(xhr) {
2147
  edcal.showError(edcal.general_error);
2148
  if (xhr.responseText) {
2149
+ edcal.output("saveOptions xhr.responseText: " + xhr.responseText);
2150
  }
2151
  }
2152
  });
2171
  if (window.console) {
2172
  console.error(msg);
2173
  }
2174
+
2175
+ edcal.displayMessage(msg);
2176
+
2177
+ },
2178
+
2179
+ /*
2180
+ * Display an error message to the user
2181
+ */
2182
+ displayMessage: function(/*string*/ msg) {
2183
+ humanMsg.displayMsg(msg);
2184
+ },
2185
+
2186
+ /*
2187
+ * A helper function to get the parameters from the
2188
+ * current URL.
2189
+ */
2190
+ getUrlVars: function() {
2191
+ var vars = [], hash;
2192
+ var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
2193
+ for (var i = 0; i < hashes.length; i++) {
2194
+ hash = hashes[i].split('=');
2195
+ vars.push(hash[0]);
2196
+ vars[hash[0]] = hash[1];
2197
+ }
2198
+
2199
+ return vars;
2200
  }
2201
  };
2202
 
2213
 
2214
  jQuery(document).ready(function(){
2215
  edcal.init();
2216
+
2217
+ /*
2218
+ * The calendar supports unit tests through the QUnit framework,
2219
+ * but we don't want to load the extra files when we aren't running
2220
+ * tests so we load them dynamically. Add the qunit=true parameter
2221
+ * to run the tests.
2222
+ */
2223
+ if (edcal.getUrlVars().qunit) {
2224
+ edcal_test.runTests();
2225
+ }
2226
  });
edcal.php CHANGED
@@ -18,7 +18,7 @@
18
  /*
19
  Plugin Name: WordPress Editorial Calendar
20
  Description: The Editorial Calendar makes it possible to see all your posts and drag and drop them to manage your blog.
21
- Version: 0.8
22
  Author: Colin Vernon, Justin Evans, Mary Vogt, and Zack Grossbart
23
  Author URI: http://www.zackgrossbart.com
24
  Plugin URI: http://www.zackgrossbart.com
@@ -42,7 +42,7 @@ add_action("init", 'edcal_load_language');
42
  $EDCAL_CONCURRENCY_ERROR = "4";
43
 
44
  /*
45
- * This error code matches NONCE_ERROR from edcal.js
46
  */
47
  $EDCAL_PERMISSION_ERROR = "5";
48
 
@@ -85,6 +85,22 @@ function echoEdCalFile($myFile) {
85
  function edcal_list_admin() {
86
  include_once('edcal.php');
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  /*
90
  * This section of code embeds certain CSS and
@@ -111,6 +127,7 @@ function edcal_list_admin() {
111
  echo '</style>';
112
 
113
  ?>
 
114
  <!-- This is just a little script so we can pass the AJAX URL and some localized strings -->
115
  <script type="text/javascript">
116
  jQuery(document).ready(function(){
@@ -122,9 +139,43 @@ function edcal_list_admin() {
122
  <?php
123
  }
124
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
  edcal.startOfWeek = <?php echo(get_option("start_of_week")); ?>;
127
  edcal.timeFormat = "<?php echo(get_option("time_format")); ?>";
 
128
 
129
  /*
130
  * We want to show the day of the first day of the week to match the user's
@@ -150,8 +201,10 @@ function edcal_list_admin() {
150
  edcal.str_addPostLink = <?php echo(edcal_json_encode(__('New Post', 'editorial-calendar'))) ?>;
151
 
152
  edcal.str_draft = <?php echo(edcal_json_encode(__(' [DRAFT]', 'editorial-calendar'))) ?>;
153
- edcal.str_stick = <?php echo(edcal_json_encode(__(' [STICKY]', 'editorial-calendar'))) ?>;
 
154
  edcal.str_draft_sticky = <?php echo(edcal_json_encode(__(' [DRAFT, STICKY]', 'editorial-calendar'))) ?>;
 
155
  edcal.str_edit = <?php echo(edcal_json_encode(__('Edit', 'editorial-calendar'))) ?>;
156
  edcal.str_quick_edit = <?php echo(edcal_json_encode(__('Quick Edit', 'editorial-calendar'))) ?>;
157
  edcal.str_del = <?php echo(edcal_json_encode(__('Delete', 'editorial-calendar'))) ?>;
@@ -166,7 +219,7 @@ function edcal_list_admin() {
166
  edcal.str_update = <?php echo(edcal_json_encode(__('Update', 'editorial-calendar'))) ?>;
167
  edcal.str_publish = <?php echo(edcal_json_encode(__('Schedule', 'editorial-calendar'))) ?>;
168
  edcal.str_save = <?php echo(edcal_json_encode(__('Save', 'editorial-calendar'))) ?>;
169
- edcal.str_edit_post_title = <?php echo(edcal_json_encode(__('Edit Post', 'editorial-calendar'))) ?>;
170
  edcal.str_scheduled = <?php echo(edcal_json_encode(__('Scheduled', 'editorial-calendar'))) ?>;
171
 
172
  edcal.str_del_msg1 = <?php echo(edcal_json_encode(__('You are about to delete the post "', 'editorial-calendar'))) ?>;
@@ -178,12 +231,26 @@ function edcal_list_admin() {
178
  edcal.general_error = <?php echo(edcal_json_encode(__('There was an error contacting your blog.', 'editorial-calendar'))) ?>;
179
 
180
  edcal.str_screenoptions = <?php echo(edcal_json_encode(__('Screen Options', 'editorial-calendar'))) ?>;
181
- edcal.str_optionsheader = <?php echo(edcal_json_encode(__('Calendar Options', 'editorial-calendar'))) ?>;
 
182
  edcal.str_apply = <?php echo(edcal_json_encode(__('Apply', 'editorial-calendar'))) ?>;
183
- edcal.str_show = <?php echo(edcal_json_encode(__('Show ', 'editorial-calendar'))) ?>;
184
- edcal.str_show2 = <?php echo(edcal_json_encode(__('weeks at a time', 'editorial-calendar'))) ?>;
 
 
 
 
 
185
  edcal.str_weekserror = <?php echo(edcal_json_encode(__('The calendar can only show between 1 and 5 weeks at a time.', 'editorial-calendar'))) ?>;
186
  edcal.str_weekstt = <?php echo(edcal_json_encode(__('Select the number of weeks for the calendar to show.', 'editorial-calendar'))) ?>;
 
 
 
 
 
 
 
 
187
  });
188
  </script>
189
 
@@ -197,24 +264,21 @@ function edcal_list_admin() {
197
  }
198
 
199
  #tipclose {
200
- background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/tip_close.gif")); ?>');
201
  }
202
 
203
- .month-present .daylabel {
204
- background: #F5F5F5 url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/month-present_bk.gif")); ?>') repeat-x left top;
205
- }
206
-
207
  #tooltip {
208
  background: white url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/../../../wp-admin/images/gray-grad.png")); ?>') repeat-x left top;
209
  }
210
 
211
- .today .daylabel {
212
- background: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/today_bk.gif")); ?>') repeat-x left top;
213
  }
214
 
215
- .dayheadcont {
216
- background: #6D6D6D url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/../../../wp-admin/images/menu-bits.gif")); ?>') repeat-x scroll left top;
217
  }
 
218
  </style>
219
 
220
  <?php
@@ -227,7 +291,7 @@ function edcal_list_admin() {
227
 
228
  <div class="wrap">
229
  <div class="icon32" id="icon-edit"><br/></div>
230
- <h2><?php echo(__('Posts Calendar', 'editorial-calendar')) ?></h2>
231
 
232
  <div id="loadingcont">
233
  <div id="loading"> </div>
@@ -243,7 +307,7 @@ function edcal_list_admin() {
243
  </div>
244
 
245
  <div id="topright">
246
- <a href="#" title="<?php echo(__('Jump to today', 'editorial-calendar')) ?>" id="moveToToday"><?php echo(__('Today', 'editorial-calendar')) ?></a>
247
  </div>
248
  </div>
249
 
@@ -254,22 +318,23 @@ function edcal_list_admin() {
254
  </div>
255
 
256
  <div id="tooltip" style="display:none;">
257
- <div class="tooltip newposttip">
258
- <div id="tooltiptitle">
259
- <?php _e('Edit Post', 'editorial-calendar') ?>
260
- </div>
261
- <a href="#" id="tipclose" onclick="edcal.hideForm(); return false;" title="close"> </a>
262
- <div id="tooltipimage"><!-- Placeholder for the form image; added dynamically --></div>
263
- <h3><!-- Placeholder for the form title; added dynamically --></h3>
264
- <div id="edcal-title-new-section">
265
- <div id="edcal-title-row" class="edcal-form-row">
266
- <h4 class="edcal-form-label"><?php _e('Title', 'editorial-calendar') ?></h4>
267
- <input type="text" class="text_input edcal-form-control" id="edcal-title-new-field" name="title" /><br />
268
- </div>
269
-
270
- <div id="edcal-content-row" class="edcal-form-row">
271
- <h4 class="edcal-form-label"><?php _e('Content', 'editorial-calendar') ?></h4>
272
- <!--
 
273
  <div id="cal_mediabar">
274
  <?php if ( current_user_can( 'upload_files' ) ) : ?>
275
  <div id="media-buttons" class="hide-if-no-js">
@@ -277,35 +342,47 @@ function edcal_list_admin() {
277
  </div>
278
  <?php endif; ?>
279
  </div>
280
- -->
281
- <div class="textarea-wrap edcal-form-control">
282
- <textarea cols="15" rows="3" class="mceEditor" id="content" name="content"></textarea>
283
- </div>
284
- </div>
285
- <div id="edcal-time-section" class="edcal-form-row">
286
- <h4 class="edcal-form-label"><?php _e('Time', 'editorial-calendar') ?></h4>
287
- <input type="text" class="edcal-form-control" id="edcal-time" name="time" value="" size="8" readonly="true" maxlength="8" autocomplete="off" />
288
- </div>
289
- <div id="edcal-status-section" class="edcal-form-row">
290
- <h4 class="edcal-form-label"><?php _e('Status', 'editorial-calendar') ?></h4>
291
- <select name="status" id="edcal-status" class="edcal-form-control">
292
- <option id="futureoption" value="future"><?php _e('Scheduled', 'editorial-calendar') ?></option>
293
- <!--<option value="pending">Pending Review</option>-->
294
  <option value="draft"><?php _e('Draft', 'editorial-calendar') ?></option>
 
 
295
  </select>
296
- </div>
297
- </div>
298
- <div id="edit-slug-buttons" class="edcal-form-row">
299
- <a class="button-primary disabled" id="newPostScheduleButton" href="#"><?php _e('Schedule', 'editorial-calendar') ?></a>
300
- <a href="#" onclick="edcal.hideForm(); return false;" class="button-secondary cancel"><?php _e('Cancel', 'editorial-calendar') ?></a>
301
- </div>
302
- <input type="hidden" id="edcal-date" name="date" value="" />
303
- <input type="hidden" id="edcal-id" name="id" value="" />
304
- </div>
305
- </div>
306
-
307
- </div>
308
-
 
 
 
 
 
 
 
 
 
 
309
  <?php
310
  }
311
 
@@ -356,6 +433,12 @@ function edcal_scripts() {
356
  //wp_enqueue_script( "date-extras", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/date.extras.js"), array( 'jquery' ) );
357
 
358
  wp_enqueue_script( "edcal-lib", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/edcallib.min.js"), array( 'jquery' ) );
 
 
 
 
 
 
359
  return;
360
 
361
  /*
@@ -490,6 +573,15 @@ function edcal_postJSON($post, $addComma = true, $fullPost = false) {
490
  }
491
 
492
  setup_postdata($post);
 
 
 
 
 
 
 
 
 
493
  ?>
494
  {
495
  "date" : "<?php the_time('d') ?><?php the_time('m') ?><?php the_time('Y') ?>",
@@ -803,7 +895,7 @@ function edcal_changedate() {
803
  ?>
804
  "post" :
805
  <?php
806
- edcal_postJSON($post);
807
  ?> }
808
 
809
  <?php
@@ -836,7 +928,7 @@ function edcal_changedate() {
836
  ?>
837
  "post" :
838
  <?php
839
- edcal_postJSON($post);
840
  ?> }
841
 
842
  <?php
@@ -910,7 +1002,7 @@ function edcal_changedate() {
910
  "post" :
911
 
912
  <?php
913
- edcal_postJSON($post);
914
  ?>}
915
  <?php
916
 
@@ -928,10 +1020,50 @@ function edcal_saveoptions() {
928
  header("Content-Type: application/json");
929
  edcal_addNoCacheHeaders();
930
 
 
 
 
931
  $edcal_weeks = isset($_GET['weeks'])?$_GET['weeks']:null;
 
 
 
 
932
 
933
- add_option("edcal_weeks_pref", $edcal_weeks, "", "yes");
934
- update_option("edcal_weeks_pref", $edcal_weeks);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
935
 
936
 
937
  /*
18
  /*
19
  Plugin Name: WordPress Editorial Calendar
20
  Description: The Editorial Calendar makes it possible to see all your posts and drag and drop them to manage your blog.
21
+ Version: 0.9
22
  Author: Colin Vernon, Justin Evans, Mary Vogt, and Zack Grossbart
23
  Author URI: http://www.zackgrossbart.com
24
  Plugin URI: http://www.zackgrossbart.com
42
  $EDCAL_CONCURRENCY_ERROR = "4";
43
 
44
  /*
45
+ * This error code matches PERMISSION_ERROR from edcal.js
46
  */
47
  $EDCAL_PERMISSION_ERROR = "5";
48
 
85
  function edcal_list_admin() {
86
  include_once('edcal.php');
87
 
88
+ /*
89
+ * We want to count the number of times they load the calendar
90
+ * so we only show the feedback after they have been using it
91
+ * for a little while.
92
+ */
93
+ $edcal_count = get_option("edcal_count");
94
+ if ($edcal_count == '') {
95
+ $edcal_count = 0;
96
+ add_option("edcal_count", $edcal_count, "", "yes");
97
+ }
98
+
99
+ if (get_option("edcal_do_feedback") != "done") {
100
+ $edcal_count++;
101
+ update_option("edcal_count", $edcal_count);
102
+ }
103
+
104
 
105
  /*
106
  * This section of code embeds certain CSS and
127
  echo '</style>';
128
 
129
  ?>
130
+
131
  <!-- This is just a little script so we can pass the AJAX URL and some localized strings -->
132
  <script type="text/javascript">
133
  jQuery(document).ready(function(){
139
  <?php
140
  }
141
  ?>
142
+
143
+ <?php
144
+ if (get_option("edcal_author_pref") != "") {
145
+ ?>
146
+ edcal.authorPref = <?php echo(get_option("edcal_author_pref")); ?>;
147
+ <?php
148
+ }
149
+ ?>
150
+
151
+ <?php
152
+ if (get_option("edcal_time_pref") != "") {
153
+ ?>
154
+ edcal.timePref = <?php echo(get_option("edcal_time_pref")); ?>;
155
+ <?php
156
+ }
157
+ ?>
158
+
159
+ <?php
160
+ if (get_option("edcal_status_pref") != "") {
161
+ ?>
162
+ edcal.statusPref = <?php echo(get_option("edcal_status_pref")); ?>;
163
+ <?php
164
+ }
165
+ ?>
166
+
167
+ <?php
168
+ if (get_option("edcal_do_feedback") != "done") {
169
+ ?>
170
+ edcal.doFeedbackPref = true;
171
+ edcal.visitCount = <?php echo(get_option("edcal_count")); ?>;
172
+ <?php
173
+ }
174
+ ?>
175
 
176
  edcal.startOfWeek = <?php echo(get_option("start_of_week")); ?>;
177
  edcal.timeFormat = "<?php echo(get_option("time_format")); ?>";
178
+ edcal.previewDateFormat = "MMMM d";
179
 
180
  /*
181
  * We want to show the day of the first day of the week to match the user's
201
  edcal.str_addPostLink = <?php echo(edcal_json_encode(__('New Post', 'editorial-calendar'))) ?>;
202
 
203
  edcal.str_draft = <?php echo(edcal_json_encode(__(' [DRAFT]', 'editorial-calendar'))) ?>;
204
+ edcal.str_pending = <?php echo(edcal_json_encode(__(' [PENDING]', 'editorial-calendar'))) ?>;
205
+ edcal.str_sticky = <?php echo(edcal_json_encode(__(' [STICKY]', 'editorial-calendar'))) ?>;
206
  edcal.str_draft_sticky = <?php echo(edcal_json_encode(__(' [DRAFT, STICKY]', 'editorial-calendar'))) ?>;
207
+ edcal.str_pending_sticky = <?php echo(edcal_json_encode(__(' [PENDING, STICKY]', 'editorial-calendar'))) ?>;
208
  edcal.str_edit = <?php echo(edcal_json_encode(__('Edit', 'editorial-calendar'))) ?>;
209
  edcal.str_quick_edit = <?php echo(edcal_json_encode(__('Quick Edit', 'editorial-calendar'))) ?>;
210
  edcal.str_del = <?php echo(edcal_json_encode(__('Delete', 'editorial-calendar'))) ?>;
219
  edcal.str_update = <?php echo(edcal_json_encode(__('Update', 'editorial-calendar'))) ?>;
220
  edcal.str_publish = <?php echo(edcal_json_encode(__('Schedule', 'editorial-calendar'))) ?>;
221
  edcal.str_save = <?php echo(edcal_json_encode(__('Save', 'editorial-calendar'))) ?>;
222
+ edcal.str_edit_post_title = <?php echo(edcal_json_encode(__('Edit Post - ', 'editorial-calendar'))) ?>;
223
  edcal.str_scheduled = <?php echo(edcal_json_encode(__('Scheduled', 'editorial-calendar'))) ?>;
224
 
225
  edcal.str_del_msg1 = <?php echo(edcal_json_encode(__('You are about to delete the post "', 'editorial-calendar'))) ?>;
231
  edcal.general_error = <?php echo(edcal_json_encode(__('There was an error contacting your blog.', 'editorial-calendar'))) ?>;
232
 
233
  edcal.str_screenoptions = <?php echo(edcal_json_encode(__('Screen Options', 'editorial-calendar'))) ?>;
234
+ edcal.str_optionscolors = <?php echo(edcal_json_encode(__('Colors', 'editorial-calendar'))) ?>;
235
+ edcal.str_optionsdraftcolor = <?php echo(edcal_json_encode(__('Drafts: ', 'editorial-calendar'))) ?>;
236
  edcal.str_apply = <?php echo(edcal_json_encode(__('Apply', 'editorial-calendar'))) ?>;
237
+ edcal.str_show_title = <?php echo(edcal_json_encode(__('Show on screen', 'editorial-calendar'))) ?>;
238
+ edcal.str_opt_weeks = <?php echo(edcal_json_encode(__(' weeks at a time', 'editorial-calendar'))) ?>;
239
+ edcal.str_show_opts = <?php echo(edcal_json_encode(__('Show in Calendar Cell', 'editorial-calendar'))) ?>;
240
+ edcal.str_opt_author = <?php echo(edcal_json_encode(__('Author', 'editorial-calendar'))) ?>;
241
+ edcal.str_opt_status = <?php echo(edcal_json_encode(__('Status', 'editorial-calendar'))) ?>;
242
+ edcal.str_opt_time = <?php echo(edcal_json_encode(__('Time of day', 'editorial-calendar'))) ?>;
243
+
244
  edcal.str_weekserror = <?php echo(edcal_json_encode(__('The calendar can only show between 1 and 5 weeks at a time.', 'editorial-calendar'))) ?>;
245
  edcal.str_weekstt = <?php echo(edcal_json_encode(__('Select the number of weeks for the calendar to show.', 'editorial-calendar'))) ?>;
246
+
247
+ edcal.str_feedbackmsg = <?php echo(edcal_json_encode(__('<div id="feedbacksection">' .
248
+ '<h2>Help us Make the Editorial Calendar Better</h2>' .
249
+ 'We are always trying to improve the Editorial Calendar and you can help. May we collect some anonymous data about your blog and browser settings to help us improve this plugin? We\'ll only do it once and you can <a target="_blank" href="http://www.zackgrossbart.com/edcal/mint/">see the data we collect</a>.<br /><br />' .
250
+ '<button class="button-secondary" onclick="edcal.doFeedback();">Collect Anonymous Data</button> ' .
251
+ '<a href="#" id="nofeedbacklink" onclick="edcal.noFeedback(); return false;">No thank you</a></div>', 'editorial-calendar'))) ?>;
252
+
253
+ edcal.str_feedbackdone = <?php echo(edcal_json_encode(__('<h2>We\'re done</h2>We\'ve finished collecting data. Thank you for helping us make the calendar better.', 'editorial-calendar'))) ?>;
254
  });
255
  </script>
256
 
264
  }
265
 
266
  #tipclose {
267
+ background-image: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/images/tip_close.png")); ?>');
268
  }
269
 
 
 
 
 
270
  #tooltip {
271
  background: white url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/../../../wp-admin/images/gray-grad.png")); ?>') repeat-x left top;
272
  }
273
 
274
+ .month-present .daylabel, .firstOfMonth .daylabel, .dayheadcont {
275
+ background: #6D6D6D url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/../../../wp-admin/images/gray-grad.png")); ?>') repeat-x scroll left top;
276
  }
277
 
278
+ .today .daylabel {
279
+ background: url('<?php echo(path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/../../../wp-admin/images/button-grad.png")); ?>') repeat-x left top;
280
  }
281
+
282
  </style>
283
 
284
  <?php
291
 
292
  <div class="wrap">
293
  <div class="icon32" id="icon-edit"><br/></div>
294
+ <h2 id="edcal_main_title"><?php echo(__('Posts Calendar', 'editorial-calendar')) ?></h2>
295
 
296
  <div id="loadingcont">
297
  <div id="loading"> </div>
307
  </div>
308
 
309
  <div id="topright">
310
+ <button class="save button" title="<?php echo(__('Scroll the calendar to make the current day visible', 'editorial-calendar')) ?>" id="moveToToday"><?php echo(__('Show Today', 'editorial-calendar')) ?></button>
311
  </div>
312
  </div>
313
 
318
  </div>
319
 
320
  <div id="tooltip" style="display:none;">
321
+ <div id="tooltiphead">
322
+ <div id="tooltiptitle"><?php _e('Edit Post', 'editorial-calendar') ?></div>
323
+ <a href="#" id="tipclose" onclick="edcal.hideForm(); return false;" title="close"> </a>
324
+ </div>
325
+
326
+ <div class="tooltip inline-edit-row">
327
+
328
+ <fieldset>
329
+
330
+ <label>
331
+ <span class="title"><?php _e('Title', 'editorial-calendar') ?></span>
332
+ <span class="input-text-wrap"><input type="text" class="ptitle" id="edcal-title-new-field" name="title" /></span>
333
+ </label>
334
+
335
+ <label>
336
+ <span class="title"><?php _e('Content', 'editorial-calendar') ?></span>
337
+ <?php /*
338
  <div id="cal_mediabar">
339
  <?php if ( current_user_can( 'upload_files' ) ) : ?>
340
  <div id="media-buttons" class="hide-if-no-js">
342
  </div>
343
  <?php endif; ?>
344
  </div>
345
+ /*/ ?>
346
+ <span class="input-text-wrap"><textarea cols="15" rows="7" id="content" name="content"></textarea></span>
347
+ </label>
348
+
349
+
350
+ <label>
351
+ <span class="title"><?php _e('Time', 'editorial-calendar') ?></span>
352
+ <span class="input-text-wrap"><input type="text" class="ptitle" id="edcal-time" name="time" value="" size="8" readonly="true" maxlength="8" autocomplete="off" /></span>
353
+ </label>
354
+
355
+ <label>
356
+ <span class="title"><?php _e('Status', 'editorial-calendar') ?></span>
357
+ <span class="input-text-wrap">
358
+ <select name="status" id="edcal-status">
359
  <option value="draft"><?php _e('Draft', 'editorial-calendar') ?></option>
360
+ <option value="pending"><?php _e('Pending Review', 'editorial-calendar') ?></option>
361
+ <option id="futureoption" value="future"><?php _e('Scheduled', 'editorial-calendar') ?></option>
362
  </select>
363
+ </span>
364
+ </label>
365
+
366
+ <?php /* <label>
367
+ <span class="title"><?php _e('Author', 'editorial-calendar') ?></span>
368
+ <span id="edcal-author-p"><!-- Placeholder for the author's name, added dynamically --></span>
369
+ </label>
370
+ */ ?>
371
+ </fieldset>
372
+
373
+ <p class="submit inline-edit-save" id="edit-slug-buttons">
374
+ <a class="button-primary disabled" id="newPostScheduleButton" href="#"><?php _e('Schedule', 'editorial-calendar') ?></a>
375
+ <a href="#" onclick="edcal.hideForm(); return false;" class="button-secondary cancel"><?php _e('Cancel', 'editorial-calendar') ?></a>
376
+ </p>
377
+
378
+ <input type="hidden" id="edcal-date" name="date" value="" />
379
+ <input type="hidden" id="edcal-id" name="id" value="" />
380
+
381
+ </div><?php // end .tooltip ?>
382
+ </div><?php // end #tooltip ?>
383
+
384
+ </div><?php // end .wrap ?>
385
+
386
  <?php
387
  }
388
 
433
  //wp_enqueue_script( "date-extras", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/date.extras.js"), array( 'jquery' ) );
434
 
435
  wp_enqueue_script( "edcal-lib", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/edcallib.min.js"), array( 'jquery' ) );
436
+
437
+ if ($_GET['qunit']) {
438
+ wp_enqueue_script( "qunit", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/lib/qunit.js"), array( 'jquery' ) );
439
+ wp_enqueue_script( "edcal-test", path_join(WP_PLUGIN_URL, basename( dirname( __FILE__ ) )."/edcal_test.js"), array( 'jquery' ) );
440
+ }
441
+
442
  return;
443
 
444
  /*
573
  }
574
 
575
  setup_postdata($post);
576
+
577
+ if (get_post_status() == 'auto-draft') {
578
+ /*
579
+ * WordPress 3 added a new post status of auto-draft so
580
+ * we want to hide them from the calendar
581
+ */
582
+ return;
583
+ }
584
+
585
  ?>
586
  {
587
  "date" : "<?php the_time('d') ?><?php the_time('m') ?><?php the_time('Y') ?>",
895
  ?>
896
  "post" :
897
  <?php
898
+ edcal_postJSON($post, false, true);
899
  ?> }
900
 
901
  <?php
928
  ?>
929
  "post" :
930
  <?php
931
+ edcal_postJSON($post, false, true);
932
  ?> }
933
 
934
  <?php
1002
  "post" :
1003
 
1004
  <?php
1005
+ edcal_postJSON($post, false, true);
1006
  ?>}
1007
  <?php
1008
 
1020
  header("Content-Type: application/json");
1021
  edcal_addNoCacheHeaders();
1022
 
1023
+ /*
1024
+ * The number of weeks preference
1025
+ */
1026
  $edcal_weeks = isset($_GET['weeks'])?$_GET['weeks']:null;
1027
+ if ($edcal_weeks != null) {
1028
+ add_option("edcal_weeks_pref", $edcal_weeks, "", "yes");
1029
+ update_option("edcal_weeks_pref", $edcal_weeks);
1030
+ }
1031
 
1032
+ /*
1033
+ * The show author preference
1034
+ */
1035
+ $edcal_author = isset($_GET['author-hide'])?$_GET['author-hide']:null;
1036
+ if ($edcal_author != null) {
1037
+ add_option("edcal_author_pref", $edcal_author, "", "yes");
1038
+ update_option("edcal_author_pref", $edcal_author);
1039
+ }
1040
+
1041
+ /*
1042
+ * The show status preference
1043
+ */
1044
+ $edcal_status = isset($_GET['status-hide'])?$_GET['status-hide']:null;
1045
+ if ($edcal_status != null) {
1046
+ add_option("edcal_status_pref", $edcal_status, "", "yes");
1047
+ update_option("edcal_status_pref", $edcal_status);
1048
+ }
1049
+
1050
+ /*
1051
+ * The show time preference
1052
+ */
1053
+ $edcal_time = isset($_GET['time-hide'])?$_GET['time-hide']:null;
1054
+ if ($edcal_time != null) {
1055
+ add_option("edcal_time_pref", $edcal_time, "", "yes");
1056
+ update_option("edcal_time_pref", $edcal_time);
1057
+ }
1058
+
1059
+ /*
1060
+ * The edcal feedback preference
1061
+ */
1062
+ $edcal_feedback = isset($_GET['dofeedback'])?$_GET['dofeedback']:null;
1063
+ if ($edcal_feedback != null) {
1064
+ add_option("edcal_do_feedback", $edcal_feedback, "", "yes");
1065
+ update_option("edcal_do_feedback", $edcal_feedback);
1066
+ }
1067
 
1068
 
1069
  /*
edcal_test.js ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*******************************************************************************
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ *
15
+ ******************************************************************************/
16
+ /*
17
+ * This file contains unit tests for the editorial calendar. It is only loaded
18
+ * if you add the qunit=true parameter to the URL for the calendar.
19
+ */
20
+
21
+ var edcal_test = {
22
+
23
+ post: {},
24
+
25
+ testContent: 'This is the content of the unit test post.',
26
+
27
+
28
+ runTests: function() {
29
+ jQuery("head").append("<link>");
30
+ css = jQuery("head").children(":last");
31
+ css.attr({
32
+ rel: "stylesheet",
33
+ type: "text/css",
34
+ href: "../wp-content/plugins/edcal/lib/qunit.css"
35
+ });
36
+
37
+ jQuery("#wpbody-content .wrap").append('<div id="edcal-qunit"></div>');
38
+
39
+ jQuery("#edcal-qunit").append('<h1 id="qunit-header">WordPress Editorial Calendar Unit Tests</h1>' +
40
+ '<h2 id="qunit-banner"></h2>' +
41
+ '<h2 id="qunit-userAgent"></h2>' +
42
+ '<ol id="qunit-tests"></ol>');
43
+
44
+
45
+ edcal_test.moveTests();
46
+ },
47
+
48
+ getFirstDate: function() {
49
+ var api = jQuery("#edcal_scrollable").scrollable();
50
+ var items = api.getVisibleItems();
51
+
52
+ return edcal.getDayFromDayId(items.eq(0).children(".row").children(".day:first").attr("id"));
53
+ },
54
+
55
+ getLastDate: function() {
56
+ var api = jQuery("#edcal_scrollable").scrollable();
57
+ var items = api.getVisibleItems();
58
+
59
+ return edcal.getDayFromDayId(items.eq(edcal.weeksPref - 1).children(".row").children(".day:last").attr("id"));
60
+ },
61
+
62
+ moveTests: function() {
63
+ var curSunday = edcal.nextStartOfWeek(Date.today()).add(-1).weeks();
64
+
65
+ edcal.moveTo(Date.today());
66
+
67
+ /*
68
+ * We'll start of with a series of tests about moving the calendar around
69
+ */
70
+ test("Move to today and check visible dates", function() {
71
+ expect(2);
72
+ ok(edcal_test.getFirstDate().equals(curSunday.clone()), "firstDate should match " + curSunday);
73
+
74
+ ok(edcal_test.getLastDate().equals(curSunday.clone().add(edcal.weeksPref).weeks().add(-1).days()),
75
+ "lastDate should match " + curSunday);
76
+ });
77
+
78
+ asyncTest("Move 1 week in the future and check visible dates", function() {
79
+ expect(2);
80
+ edcal.move(1, true, function() {
81
+ ok(edcal_test.getFirstDate().equals(curSunday.clone().add(1).weeks()), "firstDate should match " + curSunday );
82
+
83
+ ok(edcal_test.getLastDate().equals(curSunday.clone().add(edcal.weeksPref).weeks().add(-1).days().add(1).weeks()),
84
+ "lastDate should match " + curSunday );
85
+
86
+ edcal.move(1, false, function() {
87
+ start();
88
+ edcal_test.testMoveFourWeeks();
89
+ });
90
+ });
91
+ });
92
+
93
+ return;
94
+
95
+ },
96
+
97
+ testMoveFourWeeks: function() {
98
+ var curSunday = edcal.nextStartOfWeek(Date.today()).add(-1).weeks();
99
+
100
+ /*
101
+ * Now we'll move 4 weeks into the future
102
+ */
103
+ asyncTest("Move 4 week in the future and check visible dates", function() {
104
+ expect(2);
105
+
106
+ edcal.move(4, true, function() {
107
+ ok(edcal_test.getFirstDate().equals(curSunday.clone().add(4).weeks()), "firstDate should match " + curSunday );
108
+
109
+ ok(edcal_test.getLastDate().equals(curSunday.clone().add(edcal.weeksPref).weeks().add(-1).days().add(4).weeks()),
110
+ "lastDate should match " + curSunday );
111
+
112
+ edcal.move(4, false, function() {
113
+ start();
114
+ edcal_test.testMoveEightWeeks();
115
+ });
116
+ });
117
+
118
+
119
+ });
120
+ },
121
+
122
+ testMoveEightWeeks: function() {
123
+ var curSunday = edcal.nextStartOfWeek(Date.today()).add(-1).weeks();
124
+
125
+ /*
126
+ * Now 8 weeks into the past
127
+ */
128
+ asyncTest("Move 8 week in the past and check visible dates", function() {
129
+ expect(2);
130
+
131
+ edcal.move(8, false, function() {
132
+ ok(edcal_test.getFirstDate().equals(curSunday.clone().add(-8).weeks()), "firstDate should match " + curSunday );
133
+
134
+ ok(edcal_test.getLastDate().equals(curSunday.clone().add(edcal.weeksPref).weeks().add(-1).days().add(-8).weeks()),
135
+ "lastDate should match " + curSunday );
136
+
137
+ edcal.move(8, true, function() {
138
+ start();
139
+ edcal_test.testCreatePost();
140
+ });
141
+ });
142
+
143
+
144
+ });
145
+ },
146
+
147
+ testCreatePost: function() {
148
+ /*
149
+ * Now we'll do a few tests about creating, modifying, and deleting posts.
150
+ */
151
+
152
+ asyncTest("Create a new post", function() {
153
+ expect(3);
154
+
155
+ edcal_test.post.title = 'Unit Test Post';
156
+ edcal_test.post.content = edcal.testContent;
157
+ edcal_test.post.status = 'draft';
158
+ edcal_test.post.time = '10:00 AM';
159
+ edcal_test.post.date = Date.today().add(7).days().toString(edcal.internalDateFormat);
160
+ edcal_test.post.id = '0';
161
+
162
+ edcal.savePost(edcal_test.post, false, false, function(res) {
163
+ if (!res.post) {
164
+ ok(false, "There was an error creating the new post.");
165
+ start();
166
+ return;
167
+ }
168
+
169
+ equals(res.post.date, edcal_test.post.date, "The resulting post should have the same date as the request");
170
+ equals(res.post.title, edcal_test.post.title, "The resulting post should have the same title as the request");
171
+
172
+ equals(jQuery('#post-' + res.post.id).length, 1, "The post should be added in only one place in the calendar.");
173
+
174
+ edcal_test.post = res.post;
175
+
176
+ start();
177
+
178
+ edcal_test.testGetPost();
179
+ });
180
+ });
181
+
182
+ },
183
+
184
+ testGetPost: function() {
185
+ /*
186
+ * We'll start by getting data about the post we've just created
187
+ */
188
+
189
+ asyncTest("Get post information", function() {
190
+ expect(2);
191
+
192
+ edcal.getPost(edcal_test.post.id, function(post) {
193
+ equals(post.date, edcal_test.post.date, "The resulting post should have the same date as the request");
194
+ equals(post.title, edcal_test.post.title, "The resulting post should have the same title as the request");
195
+
196
+ edcal_test.post = post;
197
+
198
+ start();
199
+
200
+ edcal_test.testMovePost();
201
+ });
202
+ });
203
+
204
+ },
205
+
206
+ testMovePost: function() {
207
+
208
+ asyncTest("Change the date of an existing post", function() {
209
+ expect(2);
210
+
211
+ // We added the post one week in the future, now we will move it
212
+ // one day after that.
213
+ var newDate = Date.today().add(8).days().toString(edcal.internalDateFormat);
214
+
215
+ edcal.doDrop(edcal_test.post.date, 'post-' + edcal_test.post.id, newDate, function(res) {
216
+
217
+ if (!res.post) {
218
+ ok(false, "There was an error creating the new post.");
219
+ return;
220
+ }
221
+
222
+ equals(res.post.date, newDate, "The resulting post should have the same date as the request");
223
+
224
+ equals(jQuery('#post-' + res.post.id).length, 1, "The post should be added in only one place in the calendar.");
225
+
226
+ edcal_test.post = res.post;
227
+
228
+ start();
229
+
230
+ edcal_test.testMovePostOneWeek();
231
+ });
232
+ });
233
+
234
+ },
235
+
236
+ testMovePostOneWeek: function() {
237
+
238
+ asyncTest("Make a second change to the date of an existing post", function() {
239
+ expect(2);
240
+
241
+ // We added the post one week in the future, now we will move it
242
+ // one day after that.
243
+ var newDate = Date.today().add(22).days().toString(edcal.internalDateFormat);
244
+
245
+ edcal.doDrop(edcal_test.post.date, 'post-' + edcal_test.post.id, newDate, function(res) {
246
+
247
+ if (!res.post) {
248
+ ok(false, "There was an error creating the new post.");
249
+ return;
250
+ }
251
+
252
+ equals(res.post.date, newDate, "The resulting post should have the same date as the request");
253
+
254
+ equals(jQuery('#post-' + res.post.id).length, 1, "The post should be added in only one place in the calendar.");
255
+
256
+ edcal_test.post = res.post;
257
+
258
+ start();
259
+
260
+ edcal_test.testEditPost();
261
+ });
262
+ });
263
+
264
+ },
265
+
266
+ testEditPost: function() {
267
+
268
+ asyncTest("Edit the content of an existing post and mark it as scheduled", function() {
269
+ expect(2);
270
+
271
+ edcal_test.post.title = 'Unit Test Post &#8211 Changed';
272
+ edcal_test.post.content = 'This is the content of the unit test post. &#8211 Changed';
273
+
274
+ edcal.savePost(edcal_test.post, false, true, function(res)
275
+ {
276
+ if (!res.post) {
277
+ ok(false, "There was an error editing the post.");
278
+ start();
279
+ return;
280
+ }
281
+
282
+ equals(res.post.title, edcal_test.post.title, "The resulting post should have the same title as the request");
283
+
284
+ equals(jQuery('#post-' + res.post.id).length, 1, "The post should be added in only one place in the calendar.");
285
+
286
+ edcal_test.post = res.post;
287
+
288
+ start();
289
+
290
+ edcal_test.testDateConflict();
291
+
292
+ });
293
+ });
294
+
295
+ },
296
+
297
+ testDateConflict: function() {
298
+ asyncTest("Try to change a post date and fail because of a concurrency conflict", function() {
299
+ expect(2);
300
+
301
+ edcal_test.post.date = Date.today().add(-1).days().toString(edcal.internalDateFormat);
302
+
303
+ /*
304
+ * We added the post one week in the future, now we will move it
305
+ * one day after that.
306
+ */
307
+ var newDate = Date.today().add(8).days().toString(edcal.internalDateFormat);
308
+
309
+ edcal.changeDate(newDate, edcal_test.post, function(res)
310
+ {
311
+ if (!res.post) {
312
+ ok(false, "There was an error with the change date conflict.");
313
+ return;
314
+ }
315
+
316
+ equals(res.error, edcal.CONCURRENCY_ERROR, "This move should show an exception because it is in conflict.");
317
+
318
+ equals(jQuery('#post-' + res.post.id).length, 1, "The post should be added in only one place in the calendar.");
319
+
320
+ edcal_test.post = res.post;
321
+
322
+ start();
323
+
324
+ edcal_test.testDeletePost();
325
+
326
+ });
327
+ });
328
+
329
+ },
330
+
331
+ testDeletePost: function() {
332
+
333
+ /*
334
+ * The last step is to delete the post we made so
335
+ * the test cleans up after itself.
336
+ */
337
+ asyncTest("Delete the post created for testing", function() {
338
+ expect(1);
339
+
340
+ edcal.deletePost(edcal_test.post.id, function(res)
341
+ {
342
+ if (!res.post) {
343
+ ok(false, "There was an error creating the new post.");
344
+ start();
345
+ return;
346
+ }
347
+
348
+ equals(jQuery('#post-' + res.post.id).length, 0, "The post should now be deleted from the calendar.");
349
+ start();
350
+
351
+ });
352
+ });
353
+ }
354
+ };
images/tip_close.gif DELETED
Binary file
images/tip_close.png ADDED
Binary file
lib/lib.txt CHANGED
@@ -47,3 +47,9 @@ ui.droppable.js
47
  The draggable, droppable, and UI core support drag and drop.
48
  http://jqueryui.com/demos/draggable/
49
  MIT License
 
 
 
 
 
 
47
  The draggable, droppable, and UI core support drag and drop.
48
  http://jqueryui.com/demos/draggable/
49
  MIT License
50
+
51
+ qunit.js
52
+ qunit.css
53
+ A unit testing framework for the calendar
54
+ http://docs.jquery.com/QUnit
55
+ MIT License
lib/qunit.css ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ol#qunit-tests {
3
+ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
4
+ margin:0;
5
+ padding:0;
6
+ list-style-position:inside;
7
+
8
+ font-size: smaller;
9
+ }
10
+ ol#qunit-tests li{
11
+ padding:0.4em 0.5em 0.4em 2.5em;
12
+ border-bottom:1px solid #fff;
13
+ font-size:small;
14
+ list-style-position:inside;
15
+ }
16
+ ol#qunit-tests li ol{
17
+ box-shadow: inset 0px 2px 13px #999;
18
+ -moz-box-shadow: inset 0px 2px 13px #999;
19
+ -webkit-box-shadow: inset 0px 2px 13px #999;
20
+ margin-top:0.5em;
21
+ margin-left:0;
22
+ padding:0.5em;
23
+ background-color:#fff;
24
+ border-radius:15px;
25
+ -moz-border-radius: 15px;
26
+ -webkit-border-radius: 15px;
27
+ }
28
+ ol#qunit-tests li li{
29
+ border-bottom:none;
30
+ margin:0.5em;
31
+ background-color:#fff;
32
+ list-style-position: inside;
33
+ padding:0.4em 0.5em 0.4em 0.5em;
34
+ }
35
+
36
+ ol#qunit-tests li li.pass{
37
+ border-left:26px solid #C6E746;
38
+ background-color:#fff;
39
+ color:#5E740B;
40
+ }
41
+ ol#qunit-tests li li.fail{
42
+ border-left:26px solid #EE5757;
43
+ background-color:#fff;
44
+ color:#710909;
45
+ }
46
+ ol#qunit-tests li.pass{
47
+ background-color:#D2E0E6;
48
+ color:#528CE0;
49
+ }
50
+ ol#qunit-tests li.fail{
51
+ background-color:#EE5757;
52
+ color:#000;
53
+ }
54
+ ol#qunit-tests li strong {
55
+ cursor:pointer;
56
+ }
57
+ h1#qunit-header{
58
+ background-color:#0d3349;
59
+ margin:0;
60
+ padding:0.5em 0 0.5em 1em;
61
+ color:#fff;
62
+ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
63
+ border-top-right-radius:15px;
64
+ border-top-left-radius:15px;
65
+ -moz-border-radius-topright:15px;
66
+ -moz-border-radius-topleft:15px;
67
+ -webkit-border-top-right-radius:15px;
68
+ -webkit-border-top-left-radius:15px;
69
+ text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
70
+ }
71
+ h2#qunit-banner{
72
+ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
73
+ height:5px;
74
+ margin:0;
75
+ padding:0;
76
+ }
77
+ h2#qunit-banner.qunit-pass{
78
+ background-color:#C6E746;
79
+ }
80
+ h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
81
+ background-color:#EE5757;
82
+ }
83
+ #qunit-testrunner-toolbar {
84
+ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
85
+ padding:0;
86
+ /*width:80%;*/
87
+ padding:0em 0 0.5em 2em;
88
+ font-size: small;
89
+ }
90
+ h2#qunit-userAgent {
91
+ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
92
+ background-color:#2b81af;
93
+ margin:0;
94
+ padding:0;
95
+ color:#fff;
96
+ font-size: small;
97
+ padding:0.5em 0 0.5em 2.5em;
98
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
99
+ }
100
+ p#qunit-testresult{
101
+ font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
102
+ margin:0;
103
+ font-size: small;
104
+ color:#2b81af;
105
+ border-bottom-right-radius:15px;
106
+ border-bottom-left-radius:15px;
107
+ -moz-border-radius-bottomright:15px;
108
+ -moz-border-radius-bottomleft:15px;
109
+ -webkit-border-bottom-right-radius:15px;
110
+ -webkit-border-bottom-left-radius:15px;
111
+ background-color:#D2E0E6;
112
+ padding:0.5em 0.5em 0.5em 2.5em;
113
+ }
114
+ strong b.fail{
115
+ color:#710909;
116
+ }
117
+ strong b.pass{
118
+ color:#5E740B;
119
+ }
lib/qunit.js ADDED
@@ -0,0 +1,1069 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * QUnit - A JavaScript Unit Testing Framework
3
+ *
4
+ * http://docs.jquery.com/QUnit
5
+ *
6
+ * Copyright (c) 2009 John Resig, Jörn Zaefferer
7
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
8
+ * and GPL (GPL-LICENSE.txt) licenses.
9
+ */
10
+
11
+ (function(window) {
12
+
13
+ var QUnit = {
14
+
15
+ // Initialize the configuration options
16
+ init: function() {
17
+ config = {
18
+ stats: { all: 0, bad: 0 },
19
+ moduleStats: { all: 0, bad: 0 },
20
+ started: +new Date,
21
+ updateRate: 1000,
22
+ blocking: false,
23
+ autorun: false,
24
+ assertions: [],
25
+ filters: [],
26
+ queue: []
27
+ };
28
+
29
+ var tests = id("qunit-tests"),
30
+ banner = id("qunit-banner"),
31
+ result = id("qunit-testresult");
32
+
33
+ if ( tests ) {
34
+ tests.innerHTML = "";
35
+ }
36
+
37
+ if ( banner ) {
38
+ banner.className = "";
39
+ }
40
+
41
+ if ( result ) {
42
+ result.parentNode.removeChild( result );
43
+ }
44
+ },
45
+
46
+ // call on start of module test to prepend name to all tests
47
+ module: function(name, testEnvironment) {
48
+ config.currentModule = name;
49
+
50
+ synchronize(function() {
51
+ if ( config.currentModule ) {
52
+ QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
53
+ }
54
+
55
+ config.currentModule = name;
56
+ config.moduleTestEnvironment = testEnvironment;
57
+ config.moduleStats = { all: 0, bad: 0 };
58
+
59
+ QUnit.moduleStart( name, testEnvironment );
60
+ });
61
+ },
62
+
63
+ asyncTest: function(testName, expected, callback) {
64
+ if ( arguments.length === 2 ) {
65
+ callback = expected;
66
+ expected = 0;
67
+ }
68
+
69
+ QUnit.test(testName, expected, callback, true);
70
+ },
71
+
72
+ test: function(testName, expected, callback, async) {
73
+ var name = testName, testEnvironment, testEnvironmentArg;
74
+
75
+ if ( arguments.length === 2 ) {
76
+ callback = expected;
77
+ expected = null;
78
+ }
79
+ // is 2nd argument a testEnvironment?
80
+ if ( expected && typeof expected === 'object') {
81
+ testEnvironmentArg = expected;
82
+ expected = null;
83
+ }
84
+
85
+ if ( config.currentModule ) {
86
+ name = config.currentModule + " module: " + name;
87
+ }
88
+
89
+ if ( !validTest(name) ) {
90
+ return;
91
+ }
92
+
93
+ synchronize(function() {
94
+ QUnit.testStart( testName );
95
+
96
+ testEnvironment = extend({
97
+ setup: function() {},
98
+ teardown: function() {}
99
+ }, config.moduleTestEnvironment);
100
+ if (testEnvironmentArg) {
101
+ extend(testEnvironment,testEnvironmentArg);
102
+ }
103
+
104
+ // allow utility functions to access the current test environment
105
+ QUnit.current_testEnvironment = testEnvironment;
106
+
107
+ config.assertions = [];
108
+ config.expected = expected;
109
+
110
+ try {
111
+ if ( !config.pollution ) {
112
+ saveGlobal();
113
+ }
114
+
115
+ testEnvironment.setup.call(testEnvironment);
116
+ } catch(e) {
117
+ QUnit.ok( false, "Setup failed on " + name + ": " + e.message );
118
+ }
119
+
120
+ if ( async ) {
121
+ QUnit.stop();
122
+ }
123
+
124
+ try {
125
+ callback.call(testEnvironment);
126
+ } catch(e) {
127
+ fail("Test " + name + " died, exception and test follows", e, callback);
128
+ QUnit.ok( false, "Died on test #" + (config.assertions.length + 1) + ": " + e.message );
129
+ // else next test will carry the responsibility
130
+ saveGlobal();
131
+
132
+ // Restart the tests if they're blocking
133
+ if ( config.blocking ) {
134
+ start();
135
+ }
136
+ }
137
+ });
138
+
139
+ synchronize(function() {
140
+ try {
141
+ checkPollution();
142
+ testEnvironment.teardown.call(testEnvironment);
143
+ } catch(e) {
144
+ QUnit.ok( false, "Teardown failed on " + name + ": " + e.message );
145
+ }
146
+
147
+ try {
148
+ QUnit.reset();
149
+ } catch(e) {
150
+ fail("reset() failed, following Test " + name + ", exception and reset fn follows", e, reset);
151
+ }
152
+
153
+ if ( config.expected && config.expected != config.assertions.length ) {
154
+ QUnit.ok( false, "Expected " + config.expected + " assertions, but " + config.assertions.length + " were run" );
155
+ }
156
+
157
+ var good = 0, bad = 0,
158
+ tests = id("qunit-tests");
159
+
160
+ config.stats.all += config.assertions.length;
161
+ config.moduleStats.all += config.assertions.length;
162
+
163
+ if ( tests ) {
164
+ var ol = document.createElement("ol");
165
+ ol.style.display = "none";
166
+
167
+ for ( var i = 0; i < config.assertions.length; i++ ) {
168
+ var assertion = config.assertions[i];
169
+
170
+ var li = document.createElement("li");
171
+ li.className = assertion.result ? "pass" : "fail";
172
+ li.appendChild(document.createTextNode(assertion.message || "(no message)"));
173
+ ol.appendChild( li );
174
+
175
+ if ( assertion.result ) {
176
+ good++;
177
+ } else {
178
+ bad++;
179
+ config.stats.bad++;
180
+ config.moduleStats.bad++;
181
+ }
182
+ }
183
+
184
+ var b = document.createElement("strong");
185
+ b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + config.assertions.length + ")</b>";
186
+
187
+ addEvent(b, "click", function() {
188
+ var next = b.nextSibling, display = next.style.display;
189
+ next.style.display = display === "none" ? "block" : "none";
190
+ });
191
+
192
+ addEvent(b, "dblclick", function(e) {
193
+ var target = e && e.target ? e.target : window.event.srcElement;
194
+ if ( target.nodeName.toLowerCase() === "strong" ) {
195
+ var text = "", node = target.firstChild;
196
+
197
+ while ( node.nodeType === 3 ) {
198
+ text += node.nodeValue;
199
+ node = node.nextSibling;
200
+ }
201
+
202
+ text = text.replace(/(^\s*|\s*$)/g, "");
203
+
204
+ if ( window.location ) {
205
+ window.location.href = window.location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent(text);
206
+ }
207
+ }
208
+ });
209
+
210
+ var li = document.createElement("li");
211
+ li.className = bad ? "fail" : "pass";
212
+ li.appendChild( b );
213
+ li.appendChild( ol );
214
+ tests.appendChild( li );
215
+
216
+ if ( bad ) {
217
+ var toolbar = id("qunit-testrunner-toolbar");
218
+ if ( toolbar ) {
219
+ toolbar.style.display = "block";
220
+ id("qunit-filter-pass").disabled = null;
221
+ id("qunit-filter-missing").disabled = null;
222
+ }
223
+ }
224
+
225
+ } else {
226
+ for ( var i = 0; i < config.assertions.length; i++ ) {
227
+ if ( !config.assertions[i].result ) {
228
+ bad++;
229
+ config.stats.bad++;
230
+ config.moduleStats.bad++;
231
+ }
232
+ }
233
+ }
234
+
235
+ QUnit.testDone( testName, bad, config.assertions.length );
236
+
237
+ if ( !window.setTimeout && !config.queue.length ) {
238
+ done();
239
+ }
240
+ });
241
+
242
+ if ( window.setTimeout && !config.doneTimer ) {
243
+ config.doneTimer = window.setTimeout(function(){
244
+ if ( !config.queue.length ) {
245
+ done();
246
+ } else {
247
+ synchronize( done );
248
+ }
249
+ }, 13);
250
+ }
251
+ },
252
+
253
+ /**
254
+ * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
255
+ */
256
+ expect: function(asserts) {
257
+ config.expected = asserts;
258
+ },
259
+
260
+ /**
261
+ * Asserts true.
262
+ * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
263
+ */
264
+ ok: function(a, msg) {
265
+ QUnit.log(a, msg);
266
+
267
+ config.assertions.push({
268
+ result: !!a,
269
+ message: msg
270
+ });
271
+ },
272
+
273
+ /**
274
+ * Checks that the first two arguments are equal, with an optional message.
275
+ * Prints out both actual and expected values.
276
+ *
277
+ * Prefered to ok( actual == expected, message )
278
+ *
279
+ * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
280
+ *
281
+ * @param Object actual
282
+ * @param Object expected
283
+ * @param String message (optional)
284
+ */
285
+ equal: function(actual, expected, message) {
286
+ push(expected == actual, actual, expected, message);
287
+ },
288
+
289
+ notEqual: function(actual, expected, message) {
290
+ push(expected != actual, actual, expected, message);
291
+ },
292
+
293
+ deepEqual: function(a, b, message) {
294
+ push(QUnit.equiv(a, b), a, b, message);
295
+ },
296
+
297
+ notDeepEqual: function(a, b, message) {
298
+ push(!QUnit.equiv(a, b), a, b, message);
299
+ },
300
+
301
+ strictEqual: function(actual, expected, message) {
302
+ push(expected === actual, actual, expected, message);
303
+ },
304
+
305
+ notStrictEqual: function(actual, expected, message) {
306
+ push(expected !== actual, actual, expected, message);
307
+ },
308
+
309
+ start: function() {
310
+ // A slight delay, to avoid any current callbacks
311
+ if ( window.setTimeout ) {
312
+ window.setTimeout(function() {
313
+ if ( config.timeout ) {
314
+ clearTimeout(config.timeout);
315
+ }
316
+
317
+ config.blocking = false;
318
+ process();
319
+ }, 13);
320
+ } else {
321
+ config.blocking = false;
322
+ process();
323
+ }
324
+ },
325
+
326
+ stop: function(timeout) {
327
+ config.blocking = true;
328
+
329
+ if ( timeout && window.setTimeout ) {
330
+ config.timeout = window.setTimeout(function() {
331
+ QUnit.ok( false, "Test timed out" );
332
+ QUnit.start();
333
+ }, timeout);
334
+ }
335
+ },
336
+
337
+ /**
338
+ * Resets the test setup. Useful for tests that modify the DOM.
339
+ */
340
+ reset: function() {
341
+ if ( window.jQuery ) {
342
+ jQuery("#main").html( config.fixture );
343
+ jQuery.event.global = {};
344
+ jQuery.ajaxSettings = extend({}, config.ajaxSettings);
345
+ }
346
+ },
347
+
348
+ /**
349
+ * Trigger an event on an element.
350
+ *
351
+ * @example triggerEvent( document.body, "click" );
352
+ *
353
+ * @param DOMElement elem
354
+ * @param String type
355
+ */
356
+ triggerEvent: function( elem, type, event ) {
357
+ if ( document.createEvent ) {
358
+ event = document.createEvent("MouseEvents");
359
+ event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
360
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
361
+ elem.dispatchEvent( event );
362
+
363
+ } else if ( elem.fireEvent ) {
364
+ elem.fireEvent("on"+type);
365
+ }
366
+ },
367
+
368
+ // Safe object type checking
369
+ is: function( type, obj ) {
370
+ return Object.prototype.toString.call( obj ) === "[object "+ type +"]";
371
+ },
372
+
373
+ // Logging callbacks
374
+ done: function(failures, total) {},
375
+ log: function(result, message) {},
376
+ testStart: function(name) {},
377
+ testDone: function(name, failures, total) {},
378
+ moduleStart: function(name, testEnvironment) {},
379
+ moduleDone: function(name, failures, total) {}
380
+ };
381
+
382
+ // Backwards compatibility, deprecated
383
+ QUnit.equals = QUnit.equal;
384
+ QUnit.same = QUnit.deepEqual;
385
+
386
+ // Maintain internal state
387
+ var config = {
388
+ // The queue of tests to run
389
+ queue: [],
390
+
391
+ // block until document ready
392
+ blocking: true
393
+ };
394
+
395
+ // Load paramaters
396
+ (function() {
397
+ var location = window.location || { search: "", protocol: "file:" },
398
+ GETParams = location.search.slice(1).split('&');
399
+
400
+ for ( var i = 0; i < GETParams.length; i++ ) {
401
+ GETParams[i] = decodeURIComponent( GETParams[i] );
402
+ if ( GETParams[i] === "noglobals" ) {
403
+ GETParams.splice( i, 1 );
404
+ i--;
405
+ config.noglobals = true;
406
+ } else if ( GETParams[i].search('=') > -1 ) {
407
+ GETParams.splice( i, 1 );
408
+ i--;
409
+ }
410
+ }
411
+
412
+ // restrict modules/tests by get parameters
413
+ config.filters = GETParams;
414
+
415
+ // Figure out if we're running the tests from a server or not
416
+ QUnit.isLocal = !!(location.protocol === 'file:');
417
+ })();
418
+
419
+ // Expose the API as global variables, unless an 'exports'
420
+ // object exists, in that case we assume we're in CommonJS
421
+ if ( typeof exports === "undefined" || typeof require === "undefined" ) {
422
+ extend(window, QUnit);
423
+ window.QUnit = QUnit;
424
+ } else {
425
+ extend(exports, QUnit);
426
+ exports.QUnit = QUnit;
427
+ }
428
+
429
+ if ( typeof document === "undefined" || document.readyState === "complete" ) {
430
+ config.autorun = true;
431
+ }
432
+
433
+ addEvent(window, "load", function() {
434
+ // Initialize the config, saving the execution queue
435
+ var oldconfig = extend({}, config);
436
+ QUnit.init();
437
+ extend(config, oldconfig);
438
+
439
+ config.blocking = false;
440
+
441
+ var userAgent = id("qunit-userAgent");
442
+ if ( userAgent ) {
443
+ userAgent.innerHTML = navigator.userAgent;
444
+ }
445
+
446
+ var toolbar = id("qunit-testrunner-toolbar");
447
+ if ( toolbar ) {
448
+ toolbar.style.display = "none";
449
+
450
+ var filter = document.createElement("input");
451
+ filter.type = "checkbox";
452
+ filter.id = "qunit-filter-pass";
453
+ filter.disabled = true;
454
+ addEvent( filter, "click", function() {
455
+ var li = document.getElementsByTagName("li");
456
+ for ( var i = 0; i < li.length; i++ ) {
457
+ if ( li[i].className.indexOf("pass") > -1 ) {
458
+ li[i].style.display = filter.checked ? "none" : "";
459
+ }
460
+ }
461
+ });
462
+ toolbar.appendChild( filter );
463
+
464
+ var label = document.createElement("label");
465
+ label.setAttribute("for", "qunit-filter-pass");
466
+ label.innerHTML = "Hide passed tests";
467
+ toolbar.appendChild( label );
468
+
469
+ var missing = document.createElement("input");
470
+ missing.type = "checkbox";
471
+ missing.id = "qunit-filter-missing";
472
+ missing.disabled = true;
473
+ addEvent( missing, "click", function() {
474
+ var li = document.getElementsByTagName("li");
475
+ for ( var i = 0; i < li.length; i++ ) {
476
+ if ( li[i].className.indexOf("fail") > -1 && li[i].innerHTML.indexOf('missing test - untested code is broken code') > - 1 ) {
477
+ li[i].parentNode.parentNode.style.display = missing.checked ? "none" : "block";
478
+ }
479
+ }
480
+ });
481
+ toolbar.appendChild( missing );
482
+
483
+ label = document.createElement("label");
484
+ label.setAttribute("for", "qunit-filter-missing");
485
+ label.innerHTML = "Hide missing tests (untested code is broken code)";
486
+ toolbar.appendChild( label );
487
+ }
488
+
489
+ var main = id('main');
490
+ if ( main ) {
491
+ config.fixture = main.innerHTML;
492
+ }
493
+
494
+ if ( window.jQuery ) {
495
+ config.ajaxSettings = window.jQuery.ajaxSettings;
496
+ }
497
+
498
+ QUnit.start();
499
+ });
500
+
501
+ function done() {
502
+ if ( config.doneTimer && window.clearTimeout ) {
503
+ window.clearTimeout( config.doneTimer );
504
+ config.doneTimer = null;
505
+ }
506
+
507
+ if ( config.queue.length ) {
508
+ config.doneTimer = window.setTimeout(function(){
509
+ if ( !config.queue.length ) {
510
+ done();
511
+ } else {
512
+ synchronize( done );
513
+ }
514
+ }, 13);
515
+
516
+ return;
517
+ }
518
+
519
+ config.autorun = true;
520
+
521
+ // Log the last module results
522
+ if ( config.currentModule ) {
523
+ QUnit.moduleDone( config.currentModule, config.moduleStats.bad, config.moduleStats.all );
524
+ }
525
+
526
+ var banner = id("qunit-banner"),
527
+ tests = id("qunit-tests"),
528
+ html = ['Tests completed in ',
529
+ +new Date - config.started, ' milliseconds.<br/>',
530
+ '<span class="passed">', config.stats.all - config.stats.bad, '</span> tests of <span class="total">', config.stats.all, '</span> passed, <span class="failed">', config.stats.bad,'</span> failed.'].join('');
531
+
532
+ if ( banner ) {
533
+ banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass");
534
+ }
535
+
536
+ if ( tests ) {
537
+ var result = id("qunit-testresult");
538
+
539
+ if ( !result ) {
540
+ result = document.createElement("p");
541
+ result.id = "qunit-testresult";
542
+ result.className = "result";
543
+ tests.parentNode.insertBefore( result, tests.nextSibling );
544
+ }
545
+
546
+ result.innerHTML = html;
547
+ }
548
+
549
+ QUnit.done( config.stats.bad, config.stats.all );
550
+ }
551
+
552
+ function validTest( name ) {
553
+ var i = config.filters.length,
554
+ run = false;
555
+
556
+ if ( !i ) {
557
+ return true;
558
+ }
559
+
560
+ while ( i-- ) {
561
+ var filter = config.filters[i],
562
+ not = filter.charAt(0) == '!';
563
+
564
+ if ( not ) {
565
+ filter = filter.slice(1);
566
+ }
567
+
568
+ if ( name.indexOf(filter) !== -1 ) {
569
+ return !not;
570
+ }
571
+
572
+ if ( not ) {
573
+ run = true;
574
+ }
575
+ }
576
+
577
+ return run;
578
+ }
579
+
580
+ function push(result, actual, expected, message) {
581
+ message = message || (result ? "okay" : "failed");
582
+ QUnit.ok( result, result ? message + ": " + QUnit.jsDump.parse(expected) : message + ", expected: " + QUnit.jsDump.parse(expected) + " result: " + QUnit.jsDump.parse(actual) );
583
+ }
584
+
585
+ function synchronize( callback ) {
586
+ config.queue.push( callback );
587
+
588
+ if ( config.autorun && !config.blocking ) {
589
+ process();
590
+ }
591
+ }
592
+
593
+ function process() {
594
+ var start = (new Date()).getTime();
595
+
596
+ while ( config.queue.length && !config.blocking ) {
597
+ if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) {
598
+ config.queue.shift()();
599
+
600
+ } else {
601
+ setTimeout( process, 13 );
602
+ break;
603
+ }
604
+ }
605
+ }
606
+
607
+ function saveGlobal() {
608
+ config.pollution = [];
609
+
610
+ if ( config.noglobals ) {
611
+ for ( var key in window ) {
612
+ config.pollution.push( key );
613
+ }
614
+ }
615
+ }
616
+
617
+ function checkPollution( name ) {
618
+ var old = config.pollution;
619
+ saveGlobal();
620
+
621
+ var newGlobals = diff( old, config.pollution );
622
+ if ( newGlobals.length > 0 ) {
623
+ ok( false, "Introduced global variable(s): " + newGlobals.join(", ") );
624
+ config.expected++;
625
+ }
626
+
627
+ var deletedGlobals = diff( config.pollution, old );
628
+ if ( deletedGlobals.length > 0 ) {
629
+ ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") );
630
+ config.expected++;
631
+ }
632
+ }
633
+
634
+ // returns a new Array with the elements that are in a but not in b
635
+ function diff( a, b ) {
636
+ var result = a.slice();
637
+ for ( var i = 0; i < result.length; i++ ) {
638
+ for ( var j = 0; j < b.length; j++ ) {
639
+ if ( result[i] === b[j] ) {
640
+ result.splice(i, 1);
641
+ i--;
642
+ break;
643
+ }
644
+ }
645
+ }
646
+ return result;
647
+ }
648
+
649
+ function fail(message, exception, callback) {
650
+ if ( typeof console !== "undefined" && console.error && console.warn ) {
651
+ console.error(message);
652
+ console.error(exception);
653
+ console.warn(callback.toString());
654
+
655
+ } else if ( window.opera && opera.postError ) {
656
+ opera.postError(message, exception, callback.toString);
657
+ }
658
+ }
659
+
660
+ function extend(a, b) {
661
+ for ( var prop in b ) {
662
+ a[prop] = b[prop];
663
+ }
664
+
665
+ return a;
666
+ }
667
+
668
+ function addEvent(elem, type, fn) {
669
+ if ( elem.addEventListener ) {
670
+ elem.addEventListener( type, fn, false );
671
+ } else if ( elem.attachEvent ) {
672
+ elem.attachEvent( "on" + type, fn );
673
+ } else {
674
+ fn();
675
+ }
676
+ }
677
+
678
+ function id(name) {
679
+ return !!(typeof document !== "undefined" && document && document.getElementById) &&
680
+ document.getElementById( name );
681
+ }
682
+
683
+ // Test for equality any JavaScript type.
684
+ // Discussions and reference: http://philrathe.com/articles/equiv
685
+ // Test suites: http://philrathe.com/tests/equiv
686
+ // Author: Philippe Rathé <prathe@gmail.com>
687
+ QUnit.equiv = function () {
688
+
689
+ var innerEquiv; // the real equiv function
690
+ var callers = []; // stack to decide between skip/abort functions
691
+ var parents = []; // stack to avoiding loops from circular referencing
692
+
693
+
694
+ // Determine what is o.
695
+ function hoozit(o) {
696
+ if (QUnit.is("String", o)) {
697
+ return "string";
698
+
699
+ } else if (QUnit.is("Boolean", o)) {
700
+ return "boolean";
701
+
702
+ } else if (QUnit.is("Number", o)) {
703
+
704
+ if (isNaN(o)) {
705
+ return "nan";
706
+ } else {
707
+ return "number";
708
+ }
709
+
710
+ } else if (typeof o === "undefined") {
711
+ return "undefined";
712
+
713
+ // consider: typeof null === object
714
+ } else if (o === null) {
715
+ return "null";
716
+
717
+ // consider: typeof [] === object
718
+ } else if (QUnit.is( "Array", o)) {
719
+ return "array";
720
+
721
+ // consider: typeof new Date() === object
722
+ } else if (QUnit.is( "Date", o)) {
723
+ return "date";
724
+
725
+ // consider: /./ instanceof Object;
726
+ // /./ instanceof RegExp;
727
+ // typeof /./ === "function"; // => false in IE and Opera,
728
+ // true in FF and Safari
729
+ } else if (QUnit.is( "RegExp", o)) {
730
+ return "regexp";
731
+
732
+ } else if (typeof o === "object") {
733
+ return "object";
734
+
735
+ } else if (QUnit.is( "Function", o)) {
736
+ return "function";
737
+ } else {
738
+ return undefined;
739
+ }
740
+ }
741
+
742
+ // Call the o related callback with the given arguments.
743
+ function bindCallbacks(o, callbacks, args) {
744
+ var prop = hoozit(o);
745
+ if (prop) {
746
+ if (hoozit(callbacks[prop]) === "function") {
747
+ return callbacks[prop].apply(callbacks, args);
748
+ } else {
749
+ return callbacks[prop]; // or undefined
750
+ }
751
+ }
752
+ }
753
+
754
+ var callbacks = function () {
755
+
756
+ // for string, boolean, number and null
757
+ function useStrictEquality(b, a) {
758
+ if (b instanceof a.constructor || a instanceof b.constructor) {
759
+ // to catch short annotaion VS 'new' annotation of a declaration
760
+ // e.g. var i = 1;
761
+ // var j = new Number(1);
762
+ return a == b;
763
+ } else {
764
+ return a === b;
765
+ }
766
+ }
767
+
768
+ return {
769
+ "string": useStrictEquality,
770
+ "boolean": useStrictEquality,
771
+ "number": useStrictEquality,
772
+ "null": useStrictEquality,
773
+ "undefined": useStrictEquality,
774
+
775
+ "nan": function (b) {
776
+ return isNaN(b);
777
+ },
778
+
779
+ "date": function (b, a) {
780
+ return hoozit(b) === "date" && a.valueOf() === b.valueOf();
781
+ },
782
+
783
+ "regexp": function (b, a) {
784
+ return hoozit(b) === "regexp" &&
785
+ a.source === b.source && // the regex itself
786
+ a.global === b.global && // and its modifers (gmi) ...
787
+ a.ignoreCase === b.ignoreCase &&
788
+ a.multiline === b.multiline;
789
+ },
790
+
791
+ // - skip when the property is a method of an instance (OOP)
792
+ // - abort otherwise,
793
+ // initial === would have catch identical references anyway
794
+ "function": function () {
795
+ var caller = callers[callers.length - 1];
796
+ return caller !== Object &&
797
+ typeof caller !== "undefined";
798
+ },
799
+
800
+ "array": function (b, a) {
801
+ var i, j, loop;
802
+ var len;
803
+
804
+ // b could be an object literal here
805
+ if ( ! (hoozit(b) === "array")) {
806
+ return false;
807
+ }
808
+
809
+ len = a.length;
810
+ if (len !== b.length) { // safe and faster
811
+ return false;
812
+ }
813
+
814
+ //track reference to avoid circular references
815
+ parents.push(a);
816
+ for (i = 0; i < len; i++) {
817
+ loop = false;
818
+ for(j=0;j<parents.length;j++){
819
+ if(parents[j] === a[i]){
820
+ loop = true;//dont rewalk array
821
+ }
822
+ }
823
+ if (!loop && ! innerEquiv(a[i], b[i])) {
824
+ parents.pop();
825
+ return false;
826
+ }
827
+ }
828
+ parents.pop();
829
+ return true;
830
+ },
831
+
832
+ "object": function (b, a) {
833
+ var i, j, loop;
834
+ var eq = true; // unless we can proove it
835
+ var aProperties = [], bProperties = []; // collection of strings
836
+
837
+ // comparing constructors is more strict than using instanceof
838
+ if ( a.constructor !== b.constructor) {
839
+ return false;
840
+ }
841
+
842
+ // stack constructor before traversing properties
843
+ callers.push(a.constructor);
844
+ //track reference to avoid circular references
845
+ parents.push(a);
846
+
847
+ for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
848
+ loop = false;
849
+ for(j=0;j<parents.length;j++){
850
+ if(parents[j] === a[i])
851
+ loop = true; //don't go down the same path twice
852
+ }
853
+ aProperties.push(i); // collect a's properties
854
+
855
+ if (!loop && ! innerEquiv(a[i], b[i])) {
856
+ eq = false;
857
+ break;
858
+ }
859
+ }
860
+
861
+ callers.pop(); // unstack, we are done
862
+ parents.pop();
863
+
864
+ for (i in b) {
865
+ bProperties.push(i); // collect b's properties
866
+ }
867
+
868
+ // Ensures identical properties name
869
+ return eq && innerEquiv(aProperties.sort(), bProperties.sort());
870
+ }
871
+ };
872
+ }();
873
+
874
+ innerEquiv = function () { // can take multiple arguments
875
+ var args = Array.prototype.slice.apply(arguments);
876
+ if (args.length < 2) {
877
+ return true; // end transition
878
+ }
879
+
880
+ return (function (a, b) {
881
+ if (a === b) {
882
+ return true; // catch the most you can
883
+ } else if (a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || hoozit(a) !== hoozit(b)) {
884
+ return false; // don't lose time with error prone cases
885
+ } else {
886
+ return bindCallbacks(a, callbacks, [b, a]);
887
+ }
888
+
889
+ // apply transition with (1..n) arguments
890
+ })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
891
+ };
892
+
893
+ return innerEquiv;
894
+
895
+ }();
896
+
897
+ /**
898
+ * jsDump
899
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
900
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
901
+ * Date: 5/15/2008
902
+ * @projectDescription Advanced and extensible data dumping for Javascript.
903
+ * @version 1.0.0
904
+ * @author Ariel Flesler
905
+ * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
906
+ */
907
+ QUnit.jsDump = (function() {
908
+ function quote( str ) {
909
+ return '"' + str.toString().replace(/"/g, '\\"') + '"';
910
+ };
911
+ function literal( o ) {
912
+ return o + '';
913
+ };
914
+ function join( pre, arr, post ) {
915
+ var s = jsDump.separator(),
916
+ base = jsDump.indent(),
917
+ inner = jsDump.indent(1);
918
+ if ( arr.join )
919
+ arr = arr.join( ',' + s + inner );
920
+ if ( !arr )
921
+ return pre + post;
922
+ return [ pre, inner + arr, base + post ].join(s);
923
+ };
924
+ function array( arr ) {
925
+ var i = arr.length, ret = Array(i);
926
+ this.up();
927
+ while ( i-- )
928
+ ret[i] = this.parse( arr[i] );
929
+ this.down();
930
+ return join( '[', ret, ']' );
931
+ };
932
+
933
+ var reName = /^function (\w+)/;
934
+
935
+ var jsDump = {
936
+ parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance
937
+ var parser = this.parsers[ type || this.typeOf(obj) ];
938
+ type = typeof parser;
939
+
940
+ return type == 'function' ? parser.call( this, obj ) :
941
+ type == 'string' ? parser :
942
+ this.parsers.error;
943
+ },
944
+ typeOf:function( obj ) {
945
+ var type;
946
+ if ( obj === null ) {
947
+ type = "null";
948
+ } else if (typeof obj === "undefined") {
949
+ type = "undefined";
950
+ } else if (QUnit.is("RegExp", obj)) {
951
+ type = "regexp";
952
+ } else if (QUnit.is("Date", obj)) {
953
+ type = "date";
954
+ } else if (QUnit.is("Function", obj)) {
955
+ type = "function";
956
+ } else if (obj.setInterval && obj.document && !obj.nodeType) {
957
+ type = "window";
958
+ } else if (obj.nodeType === 9) {
959
+ type = "document";
960
+ } else if (obj.nodeType) {
961
+ type = "node";
962
+ } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) {
963
+ type = "array";
964
+ } else {
965
+ type = typeof obj;
966
+ }
967
+ return type;
968
+ },
969
+ separator:function() {
970
+ return this.multiline ? this.HTML ? '<br />' : '\n' : this.HTML ? '&nbsp;' : ' ';
971
+ },
972
+ indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing
973
+ if ( !this.multiline )
974
+ return '';
975
+ var chr = this.indentChar;
976
+ if ( this.HTML )
977
+ chr = chr.replace(/\t/g,' ').replace(/ /g,'&nbsp;');
978
+ return Array( this._depth_ + (extra||0) ).join(chr);
979
+ },
980
+ up:function( a ) {
981
+ this._depth_ += a || 1;
982
+ },
983
+ down:function( a ) {
984
+ this._depth_ -= a || 1;
985
+ },
986
+ setParser:function( name, parser ) {
987
+ this.parsers[name] = parser;
988
+ },
989
+ // The next 3 are exposed so you can use them
990
+ quote:quote,
991
+ literal:literal,
992
+ join:join,
993
+ //
994
+ _depth_: 1,
995
+ // This is the list of parsers, to modify them, use jsDump.setParser
996
+ parsers:{
997
+ window: '[Window]',
998
+ document: '[Document]',
999
+ error:'[ERROR]', //when no parser is found, shouldn't happen
1000
+ unknown: '[Unknown]',
1001
+ 'null':'null',
1002
+ undefined:'undefined',
1003
+ 'function':function( fn ) {
1004
+ var ret = 'function',
1005
+ name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE
1006
+ if ( name )
1007
+ ret += ' ' + name;
1008
+ ret += '(';
1009
+
1010
+ ret = [ ret, this.parse( fn, 'functionArgs' ), '){'].join('');
1011
+ return join( ret, this.parse(fn,'functionCode'), '}' );
1012
+ },
1013
+ array: array,
1014
+ nodelist: array,
1015
+ arguments: array,
1016
+ object:function( map ) {
1017
+ var ret = [ ];
1018
+ this.up();
1019
+ for ( var key in map )
1020
+ ret.push( this.parse(key,'key') + ': ' + this.parse(map[key]) );
1021
+ this.down();
1022
+ return join( '{', ret, '}' );
1023
+ },
1024
+ node:function( node ) {
1025
+ var open = this.HTML ? '&lt;' : '<',
1026
+ close = this.HTML ? '&gt;' : '>';
1027
+
1028
+ var tag = node.nodeName.toLowerCase(),
1029
+ ret = open + tag;
1030
+
1031
+ for ( var a in this.DOMAttrs ) {
1032
+ var val = node[this.DOMAttrs[a]];
1033
+ if ( val )
1034
+ ret += ' ' + a + '=' + this.parse( val, 'attribute' );
1035
+ }
1036
+ return ret + close + open + '/' + tag + close;
1037
+ },
1038
+ functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function
1039
+ var l = fn.length;
1040
+ if ( !l ) return '';
1041
+
1042
+ var args = Array(l);
1043
+ while ( l-- )
1044
+ args[l] = String.fromCharCode(97+l);//97 is 'a'
1045
+ return ' ' + args.join(', ') + ' ';
1046
+ },
1047
+ key:quote, //object calls it internally, the key part of an item in a map
1048
+ functionCode:'[code]', //function calls it internally, it's the content of the function
1049
+ attribute:quote, //node calls it internally, it's an html attribute value
1050
+ string:quote,
1051
+ date:quote,
1052
+ regexp:literal, //regex
1053
+ number:literal,
1054
+ 'boolean':literal
1055
+ },
1056
+ DOMAttrs:{//attributes to dump from nodes, name=>realName
1057
+ id:'id',
1058
+ name:'name',
1059
+ 'class':'className'
1060
+ },
1061
+ HTML:false,//if true, entities are escaped ( <, >, \t, space and \n )
1062
+ indentChar:' ',//indentation unit
1063
+ multiline:false //if true, items in a collection, are separated by a \n, else just a space.
1064
+ };
1065
+
1066
+ return jsDump;
1067
+ })();
1068
+
1069
+ })(this);
readme.txt CHANGED
@@ -2,30 +2,36 @@
2
  Contributors: cvernon, justinstresslimit, MaryVogt, zgrossbart
3
  Tags: posts, post, calendar, AJAX, admin, administration
4
  Requires at least: 2.8.5
5
- Tested up to: 2.9.2
6
- Stable tag: 0.8
7
 
8
  The Editorial Calendar makes it possible to see all your posts and drag and drop them to manage your blog.
9
 
10
  == Description ==
11
 
12
- Did you remember to write a post for next Tuesday? What about the Tuesday after that? WordPress doesn't make it easy to see what posts happen on what days. The editorial calendar gives you an overview of your blog and when each post will be available. You can drag and drop to move posts, quickly edit posts right in the calendar, and manage your entire blog.
 
 
 
 
13
 
14
- <a href="http://chrisg.org/drag-and-drop-post-scheduling-with-the-editorial-calendar-wordpress-plugin/">Chris Garret</a> liked the calendar so much he made a <a href="http://www.youtube.com/watch?v=F4BnQZsgtZc&feature=player_embedded" style="font-size: 1.25em">video</a> showing you how to use it. Thanks Chris!
15
 
16
  <h4>Try it out</h4>
17
- You don't have to install the calendar to give it a try. Just check out the demo installation at <a href="http://www.zackgrossbart.com/extras/sandbox/wp-admin/edit.php?page=cal">Zack's WordPress Sandbox</a>.
 
18
 
19
  <h4>Features</h4>
20
 
21
  1. See all of your posts and when they'll be posted.
22
  1. Drag and drop to change your post dates.
23
  1. Quickedit post titles, contents, and times.
 
24
  1. Easily see the status of your posts.
 
25
 
26
  <h4>Make it better</h4>
27
 
28
- The editorial calendar is getting closer and closer to version 1.0. We have a few more features to go. Thank you to everyone who has helped us improve this plugin with their feedback. If you have questions, problems, or idea for new features please let us know at <a href="mailto:wp-edcal@googlegroups.com">wp-edcal@googlegroups.com</a>.
29
 
30
  == Installation ==
31
 
@@ -36,10 +42,6 @@ The editorial calendar is getting closer and closer to version 1.0. We have a f
36
 
37
  == Frequently Asked Questions ==
38
 
39
- = Can I see a demo of the calendar? =
40
-
41
- Check out our demo installation at <a href="http://www.zackgrossbart.com/extras/sandbox/wp-admin/edit.php?page=cal">Zack's WordPress Sandbox</a>.
42
-
43
  = What are other people saying about the calendar? =
44
 
45
  From time to time people write articles about the editorial calendar. We appreciate every one.
@@ -48,13 +50,25 @@ From time to time people write articles about the editorial calendar. We apprec
48
 
49
  <a href="http://theincidentaleconomist.com/wordpress-plug-in-plug-editorial-calendar/">WordPress Plug-In Plug: Editorial Calendar</a> on <a href="http://theincidentaleconomist.com/">The Incidental Economist</a>
50
 
 
 
51
  <a href="http://wordcastnet.com/2010/01/12/visually-manage-your-post-publication-dates-with-the-editorial-calendar-wordpress-plugin/">Visually Manage Your Post Publication Dates with the Editorial Calendar WordPress Plugin</a> on <a href="http://wordcastnet.com/">WordCast</a>
52
 
 
 
53
  <a href="http://websuccessdiva.com/seo-copywriting-checklist/">SEO Copywriting Checklist for Wordpress Blogs</a> on <a href="http://websuccessdiva.com/">@WebSuccessDiva</a> is a good article about SEO and using an editorial calendar.
54
 
 
 
 
 
 
 
 
 
55
  = How do I navigate in the calendar? =
56
 
57
- You can move around in the calendar using the arrows next to the months at the top or your mouse wheel, You can also move using your keyboard:
58
 
59
  <ul>
60
  <li>Move 1 week into the past - Up Arrow</li>
@@ -65,32 +79,39 @@ You can move around in the calendar using the arrows next to the months at the t
65
 
66
  = How do I change the first day of the week? =
67
 
68
- The editorial calendar follows the Week Starts On preference on the WordPress General Settings page. Just change the setting and the calendar will use that day of the week as the first one.
69
 
70
  = What languages does the calendar support? =
71
 
72
  The calendar is available in Croatian, Czechoslovakian, English, French, and Greek.
73
 
 
 
 
 
74
  = How does the calendar work =
75
 
76
  The calendar is written in JavaScript. It contacts your blog when it scrolls to get data about your posts. Just like Google Maps. <a href="http://www.zackgrossbart.com/hackito/edcal/">Creating a JQuery Powered Posts Calendar for WordPress</a> explains a lot of the technical details.
77
 
78
- = Can I add new languages? =
79
 
80
- Yes please! We are looking for translators. It only takes about 20 minutes. If you're interested please <a href="mailto:wp-edcal@googlegroups.com">contact us</a>.
 
 
81
 
82
  = What users can use the calendar? =
83
 
84
- The editorial calendar appears differently based on your permissions. Administrators and Editors can use every feature available in the calendar. Authors can use the calendar to edit and move their own posts, but other people's posts are read-only. Contributors and Subscribers can't see the calendar.
85
 
86
  = Can I use the editorial calendar with pages? =
87
 
88
- Right now the calendar only supports posts. We're considering adding pages. If you would like to see support for pages let us know.
89
 
90
  = Does the calendar support group blogs? =
91
 
92
- If your blog has multiple authors the calendar will work just fine. We are currently working on new features to add for multi-author blogs. If you have any ideas please <a href="mailto:wp-edcal@googlegroups.com">contact us</a>.
93
 
 
94
 
95
  = Can my readers tell I'm using the editorial calendar? =
96
 
@@ -110,6 +131,14 @@ Moving published posts can cause problems with some RSS feeds and is generally n
110
 
111
  == Changelog ==
112
 
 
 
 
 
 
 
 
 
113
  = 0.8 =
114
  * We got the message and added an edit link that takes you to the WordPress edit page in addition to a link for the quick edit dialog in the calendar.
115
  * The calendar will now remember the last date and start there again the next time you access it.
2
  Contributors: cvernon, justinstresslimit, MaryVogt, zgrossbart
3
  Tags: posts, post, calendar, AJAX, admin, administration
4
  Requires at least: 2.8.5
5
+ Tested up to: 3.0.1
6
+ Stable tag: 0.9
7
 
8
  The Editorial Calendar makes it possible to see all your posts and drag and drop them to manage your blog.
9
 
10
  == Description ==
11
 
12
+ Did you remember to write a post for next Tuesday? What about the Tuesday after that? WordPress doesn't make it easy to see when your posts are scheduled. The editorial calendar gives you an overview of your blog and when each post will be published. You can drag and drop to move posts, edit posts right in the calendar, and manage your entire blog.
13
+
14
+ <h4>Watch the video</h4>
15
+
16
+ Get a quick overview of what the calendar can do for you with the <a href="http://vimeo.com/13196017" target="_blank">The WordPress Editorial Calendar Screen Cast</a>.
17
 
 
18
 
19
  <h4>Try it out</h4>
20
+ Try the calendar before you install it. Check out the demo installation at <a href="http://www.zackgrossbart.com/extras/sandbox/wp-admin/edit.php?page=cal">Zack's WordPress Sandbox</a>.
21
+
22
 
23
  <h4>Features</h4>
24
 
25
  1. See all of your posts and when they'll be posted.
26
  1. Drag and drop to change your post dates.
27
  1. Quickedit post titles, contents, and times.
28
+ 1. Publish posts or manage drafts.
29
  1. Easily see the status of your posts.
30
+ 1. Manage posts from multiple authors.
31
 
32
  <h4>Make it better</h4>
33
 
34
+ Thank you to everyone who has given us feedback and helped us improve this plugin. If you have questions, problems, or ideas for new features please let us know at <a href="mailto:wp-edcal@googlegroups.com">wp-edcal@googlegroups.com</a>.
35
 
36
  == Installation ==
37
 
42
 
43
  == Frequently Asked Questions ==
44
 
 
 
 
 
45
  = What are other people saying about the calendar? =
46
 
47
  From time to time people write articles about the editorial calendar. We appreciate every one.
50
 
51
  <a href="http://theincidentaleconomist.com/wordpress-plug-in-plug-editorial-calendar/">WordPress Plug-In Plug: Editorial Calendar</a> on <a href="http://theincidentaleconomist.com/">The Incidental Economist</a>
52
 
53
+ <a href="http://www.rezdwanhamid.com/tutorials/how-to-use-editorial-calendar-plugin-for-wordpress/">How To Use The Editorial Calendar For WordPress</a> by <a href="http://www.rezdwanhamid.com/">Rezdwan Hamid</a>
54
+
55
  <a href="http://wordcastnet.com/2010/01/12/visually-manage-your-post-publication-dates-with-the-editorial-calendar-wordpress-plugin/">Visually Manage Your Post Publication Dates with the Editorial Calendar WordPress Plugin</a> on <a href="http://wordcastnet.com/">WordCast</a>
56
 
57
+ <a href="http://askcharlyleetham.com/blog/guest-authors/powerful-plugins-that-keep-you-organized-and-increase-your-productivity/comment-page-1#comment-5791">Powerful Plugins That Keep You Organized and Increase Your Productivity</a> at <a href="http://www.askcharlyleetham.com">askcharlyleetham.com</a>.
58
+
59
  <a href="http://websuccessdiva.com/seo-copywriting-checklist/">SEO Copywriting Checklist for Wordpress Blogs</a> on <a href="http://websuccessdiva.com/">@WebSuccessDiva</a> is a good article about SEO and using an editorial calendar.
60
 
61
+ = Can I see a demo of the calendar? =
62
+
63
+ Check out our demo installation at <a href="http://www.zackgrossbart.com/extras/sandbox/wp-admin/edit.php?page=cal">Zack's WordPress Sandbox</a>.
64
+
65
+ = How do I get help using the calendar? =
66
+
67
+ The <a href="http://groups.google.com/group/wp-edcal">WordPress Editorial Calendar Google Group</a> is a good place to ask questions, find answers, and post feedback. We also follow the WordPress.org forum <a href="http://wordpress.org/tags/editorial-calendar?forum_id=10">Editorial Calendar discussion</a>.
68
+
69
  = How do I navigate in the calendar? =
70
 
71
+ You can move around in the calendar using the arrows next to the months at the top or your mouse wheel. You can also move using your keyboard:
72
 
73
  <ul>
74
  <li>Move 1 week into the past - Up Arrow</li>
79
 
80
  = How do I change the first day of the week? =
81
 
82
+ The editorial calendar follows the Week Starts On preference on the WordPress General Settings page. Choose the day you want and the calendar will use that day as the first day of the week.
83
 
84
  = What languages does the calendar support? =
85
 
86
  The calendar is available in Croatian, Czechoslovakian, English, French, and Greek.
87
 
88
+ = Can I add new languages? =
89
+
90
+ Yes please! We are looking for translators. It only takes about 20 minutes. If you're interested please <a href="mailto:wp-edcal@googlegroups.com">contact us</a>.
91
+
92
  = How does the calendar work =
93
 
94
  The calendar is written in JavaScript. It contacts your blog when it scrolls to get data about your posts. Just like Google Maps. <a href="http://www.zackgrossbart.com/hackito/edcal/">Creating a JQuery Powered Posts Calendar for WordPress</a> explains a lot of the technical details.
95
 
96
+ = Is the calendar collecting data about my blog? =
97
 
98
+ After you use the calendar a few times we'll prompt you to let us collect a little anonymous data about your blog. Information like the size of your browser window and the number of posts you have per day help us make the calendar even better.
99
+
100
+ The collected data is all available to the public. Check out the <a href="http://www.zackgrossbart.com/edcal/mint/">Editorial Calendar Statistics</a>.
101
 
102
  = What users can use the calendar? =
103
 
104
+ The editorial calendar works differently based on your permissions. Administrators and Editors can use every feature available in the calendar. Authors can use the calendar to edit and move their own posts, but other people's posts are read-only. Contributors and Subscribers can't see the calendar.
105
 
106
  = Can I use the editorial calendar with pages? =
107
 
108
+ Right now the calendar only supports posts. We're considering adding pages. If you would like to see pages show up in the calendar let us know.
109
 
110
  = Does the calendar support group blogs? =
111
 
112
+ If your blog has multiple authors the calendar will work just fine. You can show author names right in the calendar. Just look under screen options.
113
 
114
+ We are currently working on new features for multi-author blogs. If you have any ideas please <a href="mailto:wp-edcal@googlegroups.com">contact us</a>.
115
 
116
  = Can my readers tell I'm using the editorial calendar? =
117
 
131
 
132
  == Changelog ==
133
 
134
+ = 0.9 =
135
+ * The calendar now uses the QUnit unit test framework to make sure the calendar maintains high quality in every release. You can see the <a href="http://www.zackgrossbart.com/extras/sandbox/wp-admin/edit.php?page=cal&qunit=true">tests run</a> on our integration blog or run them on your own blog by adding &qunit=true to the end of the URL for the calendar.
136
+ * Made it easier to see the first day of the month using a special header and background color.
137
+ * Changed to a lighter gradient for the calendar header to match the new theme of WordPress 3.0.
138
+ * We now support pending review posts in the calendar.
139
+ * The calendar supports a feedback mechanism where we collect data about your use of the calendar. This anonymous data helps us improve the calendar and know which areas to focus on. Everyone can view the <a href="http://www.zackgrossbart.com/edcal/mint/">Editorial Calendar Statistics</a>.
140
+ * Auto-drafts are now hidden from the calendar.
141
+
142
  = 0.8 =
143
  * We got the message and added an edit link that takes you to the WordPress edit page in addition to a link for the quick edit dialog in the calendar.
144
  * The calendar will now remember the last date and start there again the next time you access it.