I managed to add listWeek on Fullcalendar.js Version 2.4.0 , but how can i add listMonth?
Code I Have added for listWeek is shown below
Want to add monthList View to fullcalendar version 2.4. 0
var ListView = fcViews.list = View.extend({
dayGrid: null, // the main subcomponent that does most of the heavy lifting
weekNumberWidth: null, // width of all the week-number cells running down the side
headRowEl: null, // the fake row element of the day-of-week header
//defultEventLimit: 5, //we need to show some events in each cell
viewDateOnLeft: false, //true to display date on left, false to display above the day
initialize: function() {
this.dayGrid = new DayGrid(this);
this.coordMap = this.dayGrid.coordMap; // the view's date-to-cell mapping is identical to the subcomponent's
},
// Sets the display range and computes all necessary dates
setRange: function(range) {
View.prototype.setRange.call(this, range); // call the super-method
this.dayGrid.breakOnWeeks = /year|month|week/.test(this.intervalUnit); // do before setRange
this.dayGrid.setRange(range);
},
// Renders the view into `this.el`, which should already be assigned.
renderDates: function() {
this.dayGrid.colCnt = 1;
this.dayGrid.rowCnt = this.dayGrid.cellDates.length;
this.dayGrid.numbersVisible = true;
if (this.opt('viewDateOnLeft')) {
this.viewDateOnLeft = this.opt('viewDateOnLeft');
this.el.removeClass('fc-display-date-above');
} else {
this.el.addClass('fc-display-date-above');
}
this.el.addClass('fc-basic-view').html(this.renderHtml());
this.headRowEl = this.el.find('thead fc-row');
this.scrollerEl = this.el.find('.fc-day-grid-container');
this.dayGrid.coordMap.containerEl = this.scrollerEl; // constrain clicks/etc to the dimensions of the scroller
this.dayGrid.el = this.el.find('.fc-day-grid');
this.dayGrid.renderDates(this.hasRigidRows());
},
// Make subcomponents ready for cleanup
unrenderDates: function() {
this.dayGrid.unrenderDates();
this.dayGrid.removeElement();
},
// Builds the HTML skeleton for the view.
renderHtml: function() {
return '' +
'<div class="' + this.widgetContentClass + '">' +
'<div class="fc-day-grid-container">' +
'<div class="fc-day-grid"/>' +
'</div>' +
'</div>';
},
// Generates the HTML that will go before the day-of week header cells.
// Queried by the DayGrid subcomponent when generating rows. Ordering depends on isRTL.
headIntroHtml: function() {
if (this.viewDateOnLeft === true) {
return '' +
'<th class="fc-week-number ' + this.widgetHeaderClass + '" ' + this.weekNumberStyleAttr() + '>' +
'<span>' +
'</span>' +
'</th>';
} else {
return '';
}
},
// Generates the HTML that will go before content-skeleton cells that display the day/week numbers.
// Queried by the DayGrid subcomponent. Ordering depends on isRTL.
numberIntroHtml: function(row) {
if (this.viewDateOnLeft === true) {
return '' +
'<td class="fc-week-number" ' + this.weekNumberStyleAttr() + '>' +
'<span>' + // needed for matchCellWidths
this.dayGrid.getCell(row, 0).start.format('ddd MMM D, YYYY') +
'</span>' +
'</td>';
} else {
return '';
}
},
// Generates the HTML that goes before the day bg cells for each day-row.
// Queried by the DayGrid subcomponent. Ordering depends on isRTL.
dayIntroHtml: function() {
if (this.viewDateOnLeft === true) {
return '<td class="fc-week-number ' + this.widgetContentClass + '" ' +
this.weekNumberStyleAttr() + '></td>';
} else {
return '';
}
},
// Generates the HTML that goes before every other type of row generated by DayGrid. Ordering depends on isRTL.
// Affects helper-skeleton and highlight-skeleton rows.
introHtml: function() {
if (this.viewDateOnLeft === true) {
return '<td class="fc-week-number" ' + this.weekNumberStyleAttr() + '></td>';
} else {
return '';
}
},
// Generates the HTML for the <td>s of the "number" row in the DayGrid's content skeleton.
// The number row will only exist if either day numbers or week numbers are turned on.
numberCellHtml: function(cell) {
if (this.viewDateOnLeft === true) {
return '<td/>';
} else {
var date = cell.start;
var classes;
classes = this.dayGrid.getDayClasses(date);
classes.unshift('my-fc-header');
return '' +
'<div class="' + classes.join(' ') + '" data-date="' + date.format() + '">' +
'<span class="my-fc-header-day">' +
date.format('dddd') +
'</span>' +
'<span class="my-fc-header-date">' +
date.format('DD-MM-YYYY') +
'</span>' +
'</div>';
}
},
// Generates an HTML attribute string for setting the width of the week number column, if it is known
weekNumberStyleAttr: function() {
if (this.weekNumberWidth !== null) {
return 'style="width:' + this.weekNumberWidth + 'px"';
}
return '';
},
// Determines whether each row should have a constant height
hasRigidRows: function() {
var eventLimit = this.opt('eventLimit');
if (eventLimit === true) {
eventLimit = this.defultEventLimit;
}
return eventLimit && typeof eventLimit !== 'number';
},
/* Dimensions
------------------------------------------------------------------------------------------------------------------*/
// Refreshes the horizontal dimensions of the view
updateWidth: function() {
// Make sure all week number cells running down the side have the same width.
// Record the width for cells created later.
this.weekNumberWidth = matchCellWidths(
this.el.find('.fc-week-number')
);
},
// Adjusts the vertical dimensions of the view to the specified values
setHeight: function(totalHeight, isAuto) {
var eventLimit = this.opt('eventLimit');
if (eventLimit === true) {
eventLimit = this.defultEventLimit;
}
var scrollerHeight;
// reset all heights to be natural
unsetScroller(this.scrollerEl);
uncompensateScroll(this.headRowEl);
this.dayGrid.removeSegPopover(); // kill the "more" popover if displayed
// is the event limit a constant level number?
if (eventLimit && typeof eventLimit === 'number') {
this.dayGrid.limitRows(eventLimit); // limit the levels first so the height can redistribute after
}
scrollerHeight = this.computeScrollerHeight(totalHeight);
this.setGridHeight(scrollerHeight, isAuto);
// is the event limit dynamically calculated?
if (eventLimit && typeof eventLimit !== 'number') {
this.dayGrid.limitRows(eventLimit); // limit the levels after the grid's row heights have been set
}
if (!isAuto && setPotentialScroller(this.scrollerEl, scrollerHeight)) { // using scrollbars?
compensateScroll(this.headRowEl, getScrollbarWidths(this.scrollerEl));
// doing the scrollbar compensation might have created text overflow which created more height. redo
scrollerHeight = this.computeScrollerHeight(totalHeight);
this.scrollerEl.height(scrollerHeight);
this.restoreScroll();
}
},
// Sets the height of just the DayGrid component in this view
setGridHeight: function(height, isAuto) {
if (isAuto) {
undistributeHeight(this.dayGrid.rowEls); // let the rows be their natural height with no expanding
} else {
distributeHeight(this.dayGrid.rowEls, height, true); // true = compensate for height-hogging rows
}
},
/* Events
------------------------------------------------------------------------------------------------------------------*/
// Sets the view's title property to the most updated computed value
updateTitle: function() {
this.title = this.computeTitle();
},
// Computes what the title at the top of the calendar should be for this view
computeTitle: function() {
return "Week of activity " + this.formatRange({
start: this.intervalStart,
end: this.intervalEnd
},
this.opt('titleFormat') || this.computeTitleFormat(),
this.opt('titleRangeSeparator')
);
},
// Renders the given events onto the view and populates the segments array
renderEvents: function(events) {
this.dayGrid.renderEvents(events);
//console.log(events);
/*var out = '';
jQuery.each(events, function(key, value){
this.dayGrid.renderEvents(value.description);
});*/
this.updateHeight(); // must compensate for events that overflow the row
View.prototype.renderEvents.call(this, events); // call the super-method
},
// Retrieves all segment objects that are rendered in the view
getSegs: function() {
return this.dayGrid.getSegs();
},
// Unrenders all event elements and clears internal segment data
unrenderEvents: function() {
this.dayGrid.unrenderEvents();
// we DON'T need to call updateHeight() because:
// A) a renderEvents() call always happens after this, which will eventually call updateHeight()
// B) in IE8, this causes a flash whenever events are rerendered
},
/* Event Dragging
------------------------------------------------------------------------------------------------------------------*/
// Renders a visual indication of an event being dragged over the view.
// A returned value of `true` signals that a mock "helper" event has been rendered.
renderDrag: function(start, end, seg) {
return this.dayGrid.renderDrag(start, end, seg);
},
// Unrenders the visual indication of an event being dragged over the view
destroyDrag: function() {
this.dayGrid.destroyDrag();
},
/* Selection
------------------------------------------------------------------------------------------------------------------*/
// Renders a visual indication of a selection
renderSelection: function(start, end) {
this.dayGrid.renderSelection(start, end);
},
// Unrenders a visual indications of a selection
destroySelection: function() {
this.dayGrid.destroySelection();
}
});
;;
/* A list view with simple day cells
----------------------------------------------------------------------------------------------------------------------*/
fcViews.listWeek = {
type: 'list',
duration: {
weeks: 1
}
};
;;
return fc; // export for Node/CommonJS
});
verison3
listweek_in_version_2.4.0
Related
I call calendar.refetchEvents(); inside an autorun Block to ensure the reactivity of the Scheduler (i'm using resource view) , when tested with big datasets , although i make sure to subscript to only a portion of 2 weeks worth of events , it's extremly slow .
my events arent Json based i'm using meteor and i loop over the events inside the Events function of the calendar .
are there some good fullcalendar practices i'm missing ?
calendar = $('#calendar').fullCalendar({
now: new Date(),
editable: true, // enable draggable events
droppable: true, // this allows things to be dropped onto the calendar
aspectRatio: 1.8,
disableDragging: true,
displayEventTime: false,
selectable:true,
allDaySlot:true,
slotDuration:'24:00',
lazyFetching:true,
scrollTime: '00:00', // undo default 6am scrollTime
header: {
left: 'today prev,next',
center: 'title',
right: 'timelineThreeDays'
},
defaultView: 'timelineThreeDays',
views: {
timelineThreeDays: {
type: 'timeline',
duration: { days: 14 }
}
},
eventAfterAllRender: function(){
Session.set("loading",false);
},
resourceLabelText: 'Employees',
eventRender: function (event, element) {
var originalClass = element[0].className;
if (event.id && event.id.indexOf("temp")>-1){
element[0].className = originalClass + ' dragEvent';
}
else if (event.id && event.id.indexOf("oloc")>-1){
element[0].className = originalClass + ' oloc';
}
else{
element[0].className = originalClass + ' hasmenu';
}
$(element[0]).attr('shift-id', event._id);
element.find('.fc-title').html((event.title?event.title+"<br/>":"")+(event.locationName?event.locationName+"<br/>":"")+moment(event.start).format("HH:mm")+ " "+moment(event.end).format("HH:mm"));
element.bind('mousedown', function (e) {
if (e.which == 3) {
Session.set("selectedShift",event._id);
}
});
},eventAfterRender: function(event, element, view) {
},
resourceRender: function(resourceObj, labelTds, bodyTds) {
var originalClass = labelTds[0].className;
var uid=resourceObj.id;
var resource = Meteor.users.findOne({_id:uid});
if(resource){
var img = Images.findOne({_id: resource.picture});
var imgUrl = img ? img.url() : "/images/default-avatar.png";
var styleString = "<img class='img-profil small' src='"+imgUrl+"'>" + resource.profile.firstname + " " + resource.profile.lastname+" <small style='font-size:0.6em;color:#D24D57;'>"+resource.profile.registeredTelephony+"</small>";
labelTds.find('.fc-cell-text').html("");
labelTds.find('.fc-cell-text').prepend(styleString);
labelTds[0].className = originalClass + ' hasResourceMenu';
}else{
var imgUrl = "/images/default-avatar.png";
var styleString = "<img class='img-profil small' src='"+imgUrl+"'>" + "Unassigned" + " " +" <small style='font-size:0.6em;color:#D24D57;'>"+"</small>";
labelTds.find('.fc-cell-text').html("");
labelTds.find('.fc-cell-text').prepend(styleString);
}
},
resources: function(callback) {
var users = [];
var data = Meteor.users.find({
$or:[
{"profile.showInScheduler":{$exists:false}},
{"profile.showInScheduler":true}
],
assignedTo:{$in:[Session.get("locationId")]},
'locations._id':Session.get("locationId"),
"profile.companyId":Session.get("companyId")
});
var arr = data.map(function(c) {
var employeeType = c.userSettings.employeeType;
var type = EmployeeType.findOne({_id:employeeType});
var img = Images.findOne({_id: c.picture});
var imgUrl = img ? img.url() : "/images/default-avatar.png";
c.name = c.name || "";
var totalHoursAllLocation = 0;
var totalHoursCurrentLocation = 0;
return {
id: c._id,
title: "t"
};
});
arr.push({id:"temp"+Session.get("companyId")+Session.get("locationId"),title:"<div class='img-profil small' style='background: url(/images/default-avatar.png);'></div> UnAssigned"});
callback(arr);
},
events: function(start, end, timezone, callback) {
},
drop: function(date, jsEvent, ui, resourceId) {
// retrieve the dropped element's stored Event Object
var locationId=Session.get("locationId");
var originalEventObject = $(this).data('eventObject');
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date;
//copiedEventObject.allDay = allDay;
shift = ShiftTypes.findOne({_id:copiedEventObject.id});
if(shift){
startDate = moment(date);
hour = shift.dayDuration.Start.split(":");
startDate.hours(hour[0]).minutes(hour[1]);
endDate = moment(date);
hour = shift.dayDuration.End.split(":");
endDate.hours(hour[0]).minutes(hour[1]);
if(moment(startDate).isAfter(endDate)){
endDate=endDate.add("1","days");
}
var data = {
shiftId:shift._id,
name:shift.name,
uid:resourceId,
locationId:Session.get("locationId"),
companyId:Session.get("companyId"),
day:date,start:startDate.utc().toDate(),
end:endDate.utc().toDate(),
type:"active"
};
if (SchedulesBeforeInsert(data,"dropActive")){
Schedules.insert(data,function (err,result) {});
}
}
},
eventResize: function( event, dayDelta, minuteDelta, revertFunc, jsEvent, ui, view ) {
endDate = moment.utc(new Date(event.start));
schedule = Schedules.findOne({_id:event.id});
var delta = dayDelta._days;
for(i=1;i<delta+1;i++){
Schedules.insert({start:moment.utc(event.start).add(i,"days").toDate(),end:moment.utc(schedule.end).add(i,"days").toDate(),uid:schedule.uid,locationId:Session.get("locationId"),companyId:schedule.companyId,name:schedule.name,shiftId:schedule.shiftId,attendanceCode:schedule.attendanceCode,type:schedule.type});
}
},
dayClick: function(date, jsEvent, view,res,res2) {
Session.set("selectedDay",moment(date).toDate());
Session.set("selectedRessource",res.id);
},
eventClick: function ( event, jsEvent, view ) {
toastr.success(moment(event.start).format('HH:mm') +" TO "+moment(event.endDate).format('HH:mm'))
},
eventReceive: function(event) { // called when a proper external event is dropped
console.log('eventReceive', event);
}
}).data().fullCalendar;
/********************* reactive calendar *****************/
this.autorun(function() {
console.log("autoRun")
Meteor.subscribe("schedules",Session.get("companyId"),moment($('#calendar').fullCalendar('getDate')).startOf('week').toDate(),moment($('#calendar').fullCalendar('getDate')).add(4,"weeks").endOf('week').toDate());
var events = [];
var usersInLocation = Meteor.users.find({$or:[{"profile.showInScheduler":{$exists:false}},{"profile.showInScheduler":true}],assignedTo:{$in:[Session.get("locationId")]},'locations._id':Session.get("locationId"),"profile.companyId":Session.get("companyId")}).fetch();
var userIds = _.pluck(usersInLocation,"_id");
userIds.push("temp"+Session.get("companyId")+Session.get("locationId"));
var data;
if(Session.get("displayAllLocations")===true){
reqEvents = Schedules.find({uid:{$in:userIds},companyId:Session.get("companyId"),type:{$in:["active","activeAttendance"]},start:{$gte:moment(Session.get("currentDate")).startOf('week').toDate()},end:{$lte:moment(Session.get("currentDate")).add(1,"week").endOf('week').toDate()}});
}else{
reqEvents = Schedules.find({uid:{$in:userIds},locationId:Session.get("locationId"),companyId:Session.get("companyId"),type:{$in:["active","activeAttendance"]},start:{$gte:moment(Session.get("currentDate")).startOf('week').toDate()},end:{$lte:moment(Session.get("currentDate")).add(1,"week").endOf('week').toDate()}});
}
reqEvents.forEach(function(evt){
var event = null;
color="";
if(evt.attendanceCode){
attendance =AttendanceCodes.findOne({_id:evt.attendanceCode});
color=attendance.color;
}
attendance = null;
color="";
id="";
locationName="";
if(evt.attendanceCode){
attendance =AttendanceCodes.findOne({_id:evt.attendanceCode})
if(attendance){
color=attendance.color;
}
}
else if(evt.employeeAttendanceCode){
attendance =AttendanceCodesPortal.findOne({_id:evt.employeeAttendanceCode})
if(attendance){
color=attendance.color;
}
}
id=evt._id;
if(evt.locationId!==Session.get("locationId")){
color="#EEEEEE";
id="oloc";
location =Locations.findOne({_id:evt.locationId});
if(location){
locationName=location.name;
}
}
if(evt.name != null){
event = {id:id,title:evt.name,start:evt.start,end:evt.end,color:color,resourceId:evt.uid,locationName:locationName};
}else{
event = {id:id,title:" ",start:evt.start,end:evt.end,color:color,resourceId:evt.uid,locationName:locationName};
}
events.push(event);
});
allUsersCursor =Meteor.users.find({
$or:[
{"profile.showInScheduler":{$exists:false}},
{"profile.showInScheduler":true}
],
assignedTo:{$in:[Session.get("locationId")]},
'locations._id':Session.get("locationId"),
"profile.companyId":Session.get("companyId")
}).fetch();
if(calendar){
calendar.removeEvents();
calendar.addEventSource(events);
calendar.refetchResources();
//SetUp the actions context menu
setUpContextMenu();
// Initialize the external events
$('#external-events div.external-event').each(function() {
var eventObject = {
title: $.trim($(this).text()), // use the element's text as the event title
id:$(this).attr("id")
};
// store the Event Object in the DOM element so we can get to it later
$(this).data('eventObject', eventObject);
// make the event draggable using jQuery UI
$(this).draggable({
helper: 'clone',
revert: 'invalid',
appendTo: 'body'// original position after the drag
});
});
}
});
I can see a number of non-Meteoric patterns in there that you should optimize out:
Your this.autorun will rerun anytime anything changes in your Schedules collection as well as for various user object changes (not just to the logged-in users but other users as well). Your autorun rebuilds your client-side data from scratch every time, including numerous joins. You could use a local collection to cache the joined data and then only add/change those documents when the underlying persisted document changes. This can be done with an observeChanges pattern. Or you could denormalize your data model a bit to avoid all those joins.
You're modifying the DOM by attaching events to a number of selectors using jQuery instead of using Meteor event handlers. This is also happening in your autorun which means you're attaching the same events to the same DOM objects repeatedly.
You use many Session variables and you use them repeatedly. These can be slow since they use browser local storage. You should .get() such data into a local variable once only and then refer to the local variable from then on.
Make sure that you only include fields in your Schedules publication that your code actually refers to on the client. The rest are overhead.
I'm using the excellent package Meteor Tags to implement tags, and I'm copying an example UI with a Selectize input. I've also copied another example how to use a filter with Selectize to avoid duplicate tags, this is great and means that if there is an existing tag "mytag", and I type "Mytag" in the select, "mytag" will be added to the document.
So far so good. The problem comes when I force all tags to be lowercase for consistency. I have replaced this line from the Meteor Tags example:
Patterns.addTag(input, {_id: that.data._id});
with this:
Patterns.addTag(input.toLowerCase(), {_id: that.data._id});
Now if I type "Mytag" into the select, and hit Enter, then the text "Mytag" remains in the input, and "mytag" is not shown in the list of selected tags.
What I want to happen is for the input to be cleared and "mytag" added to the list of tags in the select, just as though I had typed "mytag" and hit Enter.
If I refresh the page, "mytag" IS displayed as a tag, so I know that the tag "mytag" is in fact being added to my document. However I can't find any way to make the selectize input update itself without refreshing the page. I've tried all the methods I can find from the API docs without success. Any ideas?
Here is my full js code:
Template.tagInput.rendered = function () {
var that = this;
this.$('.tag-input').selectize({
valueField: 'name',
labelField: 'name',
searchField: ['name'],
create: function(input, cb) {
console.log('create tag: ', input)
Patterns.addTag(input.toLowerCase(), {_id: that.data._id});
var tag = Meteor.tags.findOne({collection: 'patterns', name: input});
if (cb) {
cb(tag);
}
return tag;
},
options: Meteor.tags.find().fetch({}),
render: {
item: function(item, escape) {
return '<div>' +
(item.name ? '<span class="name">' + escape(item.name) + '</span>' : '') +
'</div>';
},
option: function(item, escape) {
var name = item.name;
var caption = item.nRefs;
return '<div>' +
'<span class="name">' + escape(name) + '</span> ' +
(caption ? '<span class="badge">(x' + escape(caption) + ')</span>' : '') +
'</div>';
}
},
onItemAdd: function(value, $item) {
console.log('add tag: ', value);
Patterns.addTag(value, {_id: that.data._id});
},
onItemRemove: function(value) {
console.log('remove tag: ', value);
Patterns.removeTag(value, {_id: that.data._id});
},
createFilter: function (value)
{
// don't differentiate on case
// https://github.com/brianreavis/selectize.js/issues/796
for (var optValue in this.options)
{
var name = this.options[optValue].name; // Property defined by labelField
if (name.toLowerCase() === value.toLowerCase() && name !== value)
{
return false;
}
}
return true;
}
});
};
I want to add a new month view to fullcalendar where all the day are in column (like a timeline).
Is there a way to do that properly and remain compatible with the original plugin ?
Update
Note that fullcalendar now has another system for handling third party views and plugins.
How I do it now:
(function() {
'strict';
var FC = $.fullCalendar, // a reference to FullCalendar's root namespace
View = FC.View, // the class that all views must inherit from
ListView; // our subclass
ListView = View.extend({ // make a subclass of View
computeRange: function(date) {
var intervalDuration = moment.duration(this.opt('duration') || this.constructor.duration || {
days: 10
});
var intervalUnit = 'day';
var intervalStart = date.clone().startOf(intervalUnit);
var intervalEnd = intervalStart.clone().add(intervalDuration);
var start, end;
// normalize the range's time-ambiguity
intervalStart.stripTime();
intervalEnd.stripTime();
start = intervalStart.clone();
start = this.skipHiddenDays(start);
end = intervalEnd.clone();
end = this.skipHiddenDays(end, -1, true); // exclusively move backwards
return {
intervalDuration: intervalDuration,
intervalUnit: intervalUnit,
intervalStart: intervalStart,
intervalEnd: intervalEnd,
start: start,
end: end
};
},
initialize: function() {
// called once when the view is instantiated, when the user switches to the view.
// initialize member variables or do other setup tasks.
View.prototype.initialize.apply(this, arguments);
},
render: function() {
// responsible for displaying the skeleton of the view within the already-defined
// this.el, a jQuery element.
View.prototype.render.apply(this, arguments);
},
computeTitle: function() {
return moment().format(this.opt('titleFormat'));
},
setHeight: function(height, isAuto) {
// responsible for adjusting the pixel-height of the view. if isAuto is true, the
// view may be its natural height, and `height` becomes merely a suggestion.
this.el.height(height);
View.prototype.setHeight.apply(this, arguments);
},
renderEvents: function(events) {
// reponsible for rendering the given Event Objects
var noDebug = true;
noDebug || console.log(events);
var eventsCopy = events.slice().reverse(); //copy and reverse so we can modify while looping
var tbody = $('<tbody></tbody>');
this.scrollerEl = this.el.addClass('fc-scroller');
this.el.html('')
.append('<table style="border: 0; width:100%"></table>').children()
.append(tbody);
var periodEnd = this.end.clone(); //clone so as to not accidentally modify
noDebug || console.log('Period start: ' + this.start.format("YYYY MM DD HH:mm:ss Z") + ', and end: ' + this.end.format("YYYY MM DD HH:mm:ss Z"));
var currentDayStart = this.start.clone();
while (currentDayStart.isBefore(periodEnd)) {
var didAddDayHeader = false;
var currentDayEnd = currentDayStart.clone().add(1, 'days');
noDebug || console.log('=== this day start: ' + currentDayStart.format("YYYY MM DD HH:mm:ss Z") + ', and end: ' + currentDayEnd.format("YYYY MM DD HH:mm:ss Z"));
//Assume events were ordered descending originally (notice we reversed them)
for (var i = eventsCopy.length - 1; i >= 0; --i) {
var e = eventsCopy[i];
var eventStart = e.start.clone();
var eventEnd = this.calendar.getEventEnd(e);
if (!noDebug) {
console.log(e.title);
console.log('event index: ' + (events.length - i - 1) + ', and in copy: ' + i);
console.log('event start: ' + eventStart.format("YYYY MM DD HH:mm:ss Z"));
console.log('event end: ' + this.calendar.getEventEnd(e).format("YYYY MM DD HH:mm:ss Z"));
console.log('currentDayEnd: ' + currentDayEnd.format("YYYY MM DD HH:mm:ss Z"));
console.log(currentDayEnd.isAfter(eventStart));
}
if (currentDayStart.isAfter(eventEnd) || (currentDayStart.isSame(eventEnd) && !eventStart.isSame(eventEnd)) || periodEnd.isBefore(eventStart)) {
eventsCopy.splice(i, 1);
noDebug || console.log("--- Removed the above event");
} else if (currentDayEnd.isAfter(eventStart)) {
//We found an event to display
noDebug || console.log("+++ We added the above event");
if (!didAddDayHeader) {
tbody.append('\
<tr class="fc-header" date="">\
<th colspan="2">\
<span class="fc-header-day">' + currentDayStart.format('dddd') + '</span>\
<span class="fc-header-date">' + currentDayStart.format(this.opt('columnFormat')) + '</span>\
</th>\
</tr>');
didAddDayHeader = true;
}
/*
<td class="fc-event-handle">\
<span class="fc-event"></span>\
</td>\
*/
var segEl = $('\
<tr class="fc-row fc-event-container fc-content">\
<td class="fc-time">' + (e.allDay ? this.opt('allDayText') : e.start.format('H:mm') + '-' + e.end.format('H:mm')) + '</td>\
<td>\
<div class="fc-title">' + e.title + '</div>\
<div class="fc-description">' + e.location + '</div>\
</td>\
</tr>');
tbody.append(segEl);
//Tried to use fullcalendar code for this stuff but to no avail
(function(_this, myEvent, mySegEl) { //temp bug fix because 'e' seems to change
segEl.on('click', function(ev) {
return _this.trigger('eventClick', mySegEl, myEvent, ev);
});
})(this, e, segEl);
}
}
currentDayStart.add(1, 'days');
}
this.updateHeight();
View.prototype.renderEvents.apply(this, arguments);
},
destroyEvents: function() {
// responsible for undoing everything in renderEvents
View.prototype.destroyEvents.apply(this, arguments);
},
renderSelection: function(range) {
// accepts a {start,end} object made of Moments, and must render the selection
View.prototype.renderSelection.apply(this, arguments);
},
destroySelection: function() {
// responsible for undoing everything in renderSelection
View.prototype.destroySelection.apply(this, arguments);
}
});
FC.views.list = ListView; // register our class with the view system
})();
For older versions of fullcalendar
I made something like what you're talking about:
https://github.com/samedii/fullcalendar
Just set
basicListInterval: { 'days': 30 }
The view is called 'basicList'
Edit (more graphic solution):
src/basic/basicList.js
/* A view with a simple list
----------------------------------------------------------------------------------------------------------------------*/
fcViews.basicList = BasicListView; // register this view
function BasicListView(calendar) {
BasicView.call(this, calendar); // call the super-constructor
}
BasicListView.prototype = createObject(BasicView.prototype); // define the super-class
$.extend(BasicListView.prototype, {
name: 'basicList',
incrementDate: function(date, delta) {
var out = date.clone().stripTime().add(delta, 'days');
out = this.skipHiddenDays(out, delta < 0 ? -1 : 1);
return out;
},
render: function(date) {
this.intervalStart = date.clone().stripTime();
this.intervalEnd = this.intervalStart.clone().add(30, 'days');
this.start = this.skipHiddenDays(this.intervalStart);
this.end = this.skipHiddenDays(this.intervalEnd, -1, true);
this.title = this.calendar.formatRange(
this.start,
this.end.clone().subtract(1), // make inclusive by subtracting 1 ms
this.opt('titleFormat'),
' \u2014 ' // emphasized dash
);
BasicView.prototype.render.call(this, 30, 1, true); // call the super-method
}
});
I think you need to run npm install && bower install and then grunt dev to build.
I am using file uploader from one of the plugin.
That php file called at file onchange event by ajax.
I got a file at php.
I can upload file manually by the help of this
$target_path = "../../../uploads/extension/";
$target_path = $target_path . basename( $_FILES['file']['name']);
if(move_uploaded_file($_FILES['file']['tmp_name'], $target_path)) {
echo "The file ". basename( $_FILES['file']['name']).
" has been uploaded";
} else{
echo "There was an error uploading the file, please try again!";
}
But i want upload a file like other file uploading in Media. Its stored in uploads/2014/07 (i.e) upload/yearFolder/MonthFolder.
How to upload file like wise. I used wp_handle_upload(). but it returns undefined to call. So how to upload file.
Use the given steps
ajaxupload.3.5.js Code
/**
* Ajax upload
* Project page - http://valums.com/ajax-upload/
* Copyright (c) 2008 Andris Valums, http://valums.com
* Licensed under the MIT license (http://valums.com/mit-license/)
* Version 3.5 (23.06.2009)
*/
/**
* Changes from the previous version:
* 1. Added better JSON handling that allows to use 'application/javascript' as a response
* 2. Added demo for usage with jQuery UI dialog
* 3. Fixed IE "mixed content" issue when used with secure connections
*
* For the full changelog please visit:
* http://valums.com/ajax-upload-changelog/
*/
(function(){
var d = document, w = window;
/**
* Get element by id
*/
function get(element){
if (typeof element == "string")
element = d.getElementById(element);
return element;
}
/**
* Attaches event to a dom element
*/
function addEvent(el, type, fn){
if (w.addEventListener){
el.addEventListener(type, fn, false);
} else if (w.attachEvent){
var f = function(){
fn.call(el, w.event);
};
el.attachEvent('on' + type, f)
}
}
/**
* Creates and returns element from html chunk
*/
var toElement = function(){
var div = d.createElement('div');
return function(html){
div.innerHTML = html;
var el = div.childNodes[0];
div.removeChild(el);
return el;
}
}();
function hasClass(ele,cls){
return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
if (!hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
ele.className=ele.className.replace(reg,' ');
}
// getOffset function copied from jQuery lib (http://jquery.com/)
if (document.documentElement["getBoundingClientRect"]){
// Get Offset using getBoundingClientRect
// http://ejohn.org/blog/getboundingclientrect-is-awesome/
var getOffset = function(el){
var box = el.getBoundingClientRect(),
doc = el.ownerDocument,
body = doc.body,
docElem = doc.documentElement,
// for ie
clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
// In Internet Explorer 7 getBoundingClientRect property is treated as physical,
// while others are logical. Make all logical, like in IE8.
zoom = 1;
if (body.getBoundingClientRect) {
var bound = body.getBoundingClientRect();
zoom = (bound.right - bound.left)/body.clientWidth;
}
if (zoom > 1){
clientTop = 0;
clientLeft = 0;
}
var top = box.top/zoom + (window.pageYOffset || docElem && docElem.scrollTop/zoom || body.scrollTop/zoom) - clientTop,
left = box.left/zoom + (window.pageXOffset|| docElem && docElem.scrollLeft/zoom || body.scrollLeft/zoom) - clientLeft;
return {
top: top,
left: left
};
}
} else {
// Get offset adding all offsets
var getOffset = function(el){
if (w.jQuery){
return jQuery(el).offset();
}
var top = 0, left = 0;
do {
top += el.offsetTop || 0;
left += el.offsetLeft || 0;
}
while (el = el.offsetParent);
return {
left: left,
top: top
};
}
}
function getBox(el){
var left, right, top, bottom;
var offset = getOffset(el);
left = offset.left;
top = offset.top;
right = left + el.offsetWidth;
bottom = top + el.offsetHeight;
return {
left: left,
right: right,
top: top,
bottom: bottom
};
}
/**
* Crossbrowser mouse coordinates
*/
function getMouseCoords(e){
// pageX/Y is not supported in IE
// http://www.quirksmode.org/dom/w3c_cssom.html
if (!e.pageX && e.clientX){
// In Internet Explorer 7 some properties (mouse coordinates) are treated as physical,
// while others are logical (offset).
var zoom = 1;
var body = document.body;
if (body.getBoundingClientRect) {
var bound = body.getBoundingClientRect();
zoom = (bound.right - bound.left)/body.clientWidth;
}
return {
x: e.clientX / zoom + d.body.scrollLeft + d.documentElement.scrollLeft,
y: e.clientY / zoom + d.body.scrollTop + d.documentElement.scrollTop
};
}
return {
x: e.pageX,
y: e.pageY
};
}
/**
* Function generates unique id
*/
var getUID = function(){
var id = 0;
return function(){
return 'ValumsAjaxUpload' + id++;
}
}();
function fileFromPath(file){
return file.replace(/.*(\/|\\)/, "");
}
function getExt(file){
return (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : '';
}
// Please use AjaxUpload , Ajax_upload will be removed in the next version
Ajax_upload = AjaxUpload = function(button, options){
if (button.jquery){
// jquery object was passed
button = button[0];
} else if (typeof button == "string" && /^#.*/.test(button)){
button = button.slice(1);
}
button = get(button);
this._input = null;
this._button = button;
this._disabled = false;
this._submitting = false;
// Variable changes to true if the button was clicked
// 3 seconds ago (requred to fix Safari on Mac error)
this._justClicked = false;
this._parentDialog = d.body;
if (window.jQuery && jQuery.ui && jQuery.ui.dialog){
var parentDialog = jQuery(this._button).parents('.ui-dialog');
if (parentDialog.length){
this._parentDialog = parentDialog[0];
}
}
this._settings = {
// Location of the server-side upload script
action: 'upload.php',
// File upload name
name: 'userfile',
// Additional data to send
data: {},
// Submit file as soon as it's selected
autoSubmit: true,
// The type of data that you're expecting back from the server.
// Html and xml are detected automatically.
// Only useful when you are using json data as a response.
// Set to "json" in that case.
responseType: false,
// When user selects a file, useful with autoSubmit disabled
onChange: function(file, extension){},
// Callback to fire before file is uploaded
// You can return false to cancel upload
onSubmit: function(file, extension){},
// Fired when file upload is completed
// WARNING! DO NOT USE "FALSE" STRING AS A RESPONSE!
onComplete: function(file, response) {}
};
// Merge the users options with our defaults
for (var i in options) {
this._settings[i] = options[i];
}
this._createInput();
this._rerouteClicks();
}
// assigning methods to our class
AjaxUpload.prototype = {
setData : function(data){
this._settings.data = data;
},
disable : function(){
this._disabled = true;
},
enable : function(){
this._disabled = false;
},
// removes ajaxupload
destroy : function(){
if(this._input){
if(this._input.parentNode){
this._input.parentNode.removeChild(this._input);
}
this._input = null;
}
},
/**
* Creates invisible file input above the button
*/
_createInput : function(){
var self = this;
var input = d.createElement("input");
input.setAttribute('type', 'file');
input.setAttribute('name', this._settings.name);
input.setAttribute('id', this._settings.name);
var styles = {
'position' : 'absolute'
,'margin': '-5px 0 0 -175px'
,'padding': 0
,'top': '590px !important'
,'left': '560px !important'
,'width': '110px'
,'height': '30px'
,'fontSize': '14px'
,'opacity': 0
,'cursor': 'pointer'
,'display' : 'none'
,'zIndex' : 2147483583 //Max zIndex supported by Opera 9.0-9.2x
// Strange, I expected 2147483647
};
for (var i in styles){
input.style[i] = styles[i];
}
// Make sure that element opacity exists
// (IE uses filter instead)
if ( ! (input.style.opacity === "0")){
input.style.filter = "alpha(opacity=0)";
}
this._parentDialog.appendChild(input);
addEvent(input, 'change', function(){
// get filename from input
var file = fileFromPath(this.value);
if(self._settings.onChange.call(self, file, getExt(file)) == false ){
return;
}
// Submit form when value is changed
if (self._settings.autoSubmit){
self.submit();
}
});
// Fixing problem with Safari
// The problem is that if you leave input before the file select dialog opens
// it does not upload the file.
// As dialog opens slowly (it is a sheet dialog which takes some time to open)
// there is some time while you can leave the button.
// So we should not change display to none immediately
addEvent(input, 'click', function(){
self.justClicked = true;
setTimeout(function(){
// we will wait 3 seconds for dialog to open
self.justClicked = false;
}, 3000);
});
this._input = input;
},
_rerouteClicks : function (){
var self = this;
// IE displays 'access denied' error when using this method
// other browsers just ignore click()
// addEvent(this._button, 'click', function(e){
// self._input.click();
// });
var box, dialogOffset = {top:0, left:0}, over = false;
addEvent(self._button, 'mouseover', function(e){
if (!self._input || over) return;
over = true;
box = getBox(self._button);
if (self._parentDialog != d.body){
dialogOffset = getOffset(self._parentDialog);
}
});
// we can't use mouseout on the button,
// because invisible input is over it
addEvent(document, 'mousemove', function(e){
var input = self._input;
if (!input || !over) return;
if (self._disabled){
removeClass(self._button, 'hover');
input.style.display = 'none';
return;
}
var c = getMouseCoords(e);
if ((c.x >= box.left) && (c.x <= box.right) &&
(c.y >= box.top) && (c.y <= box.bottom)){
input.style.top = c.y - dialogOffset.top + 'px';
input.style.left = c.x - dialogOffset.left + 'px';
input.style.display = 'block';
addClass(self._button, 'hover');
} else {
// mouse left the button
over = false;
if (!self.justClicked){
input.style.display = 'none';
}
removeClass(self._button, 'hover');
}
});
},
/**
* Creates iframe with unique name
*/
_createIframe : function(){
// unique name
// We cannot use getTime, because it sometimes return
// same value in safari :(
var id = getUID();
// Remove ie6 "This page contains both secure and nonsecure items" prompt
// http://tinyurl.com/77w9wh
var iframe = toElement('<iframe src="javascript:false;" name="' + id + '" />');
iframe.id = id;
iframe.style.display = 'none';
d.body.appendChild(iframe);
return iframe;
},
/**
* Upload file without refreshing the page
*/
submit : function(){
var self = this, settings = this._settings;
if (this._input.value === ''){
// there is no file
return;
}
// get filename from input
var file = fileFromPath(this._input.value);
// execute user event
if (! (settings.onSubmit.call(this, file, getExt(file)) == false)) {
// Create new iframe for this submission
var iframe = this._createIframe();
// Do not submit if user function returns false
var form = this._createForm(iframe);
form.appendChild(this._input);
form.submit();
d.body.removeChild(form);
form = null;
this._input = null;
// create new input
this._createInput();
var toDeleteFlag = false;
addEvent(iframe, 'load', function(e){
if (// For Safari
iframe.src == "javascript:'%3Chtml%3E%3C/html%3E';" ||
// For FF, IE
iframe.src == "javascript:'<html></html>';"){
// First time around, do not delete.
if( toDeleteFlag ){
// Fix busy state in FF3
setTimeout( function() {
d.body.removeChild(iframe);
}, 0);
}
return;
}
var doc = iframe.contentDocument ? iframe.contentDocument : frames[iframe.id].document;
// fixing Opera 9.26
if (doc.readyState && doc.readyState != 'complete'){
// Opera fires load event multiple times
// Even when the DOM is not ready yet
// this fix should not affect other browsers
return;
}
// fixing Opera 9.64
if (doc.body && doc.body.innerHTML == "false"){
// In Opera 9.64 event was fired second time
// when body.innerHTML changed from false
// to server response approx. after 1 sec
return;
}
var response;
if (doc.XMLDocument){
// response is a xml document IE property
response = doc.XMLDocument;
} else if (doc.body){
// response is html document or plain text
response = doc.body.innerHTML;
if (settings.responseType && settings.responseType.toLowerCase() == 'json'){
// If the document was sent as 'application/javascript' or
// 'text/javascript', then the browser wraps the text in a <pre>
// tag and performs html encoding on the contents. In this case,
// we need to pull the original text content from the text node's
// nodeValue property to retrieve the unmangled content.
// Note that IE6 only understands text/html
if (doc.body.firstChild && doc.body.firstChild.nodeName.toUpperCase() == 'PRE'){
response = doc.body.firstChild.firstChild.nodeValue;
}
if (response) {
response = window["eval"]("(" + response + ")");
} else {
response = {};
}
}
} else {
// response is a xml document
var response = doc;
}
settings.onComplete.call(self, file, response);
// Reload blank page, so that reloading main page
// does not re-submit the post. Also, remember to
// delete the frame
toDeleteFlag = true;
// Fix IE mixed content issue
iframe.src = "javascript:'<html></html>';";
});
} else {
// clear input to allow user to select same file
// Doesn't work in IE6
// this._input.value = '';
d.body.removeChild(this._input);
this._input = null;
// create new input
this._createInput();
}
},
/**
* Creates form, that will be submitted to iframe
*/
_createForm : function(iframe){
var settings = this._settings;
// method, enctype must be specified here
// because changing this attr on the fly is not allowed in IE 6/7
var form = toElement('<form method="post" enctype="multipart/form-data"></form>');
form.style.display = 'none';
form.action = settings.action;
form.target = iframe.name;
d.body.appendChild(form);
// Create hidden input element for each data key
for (var prop in settings.data){
var el = d.createElement("input");
el.type = 'hidden';
el.name = prop;
el.value = settings.data[prop];
form.appendChild(el);
}
return form;
}
};
})();
add this file in your header(like:<script type="text/javascript" src="<?php print get_stylesheet_directory_uri() ;?>/js/ajaxupload.3.5.js" ></script> )
Now place this code below it in header
<script type="text/javascript" >
jQuery(function(){
var btnUpload=jQuery('#upload');
var status=jQuery('#status');
new AjaxUpload(btnUpload, {
action: '<?php echo get_template_directory_uri();?>/upload-file.php',
name: 'uploadfile',
onSubmit: function(file, ext){
if (! (ext && /^(jpg|png|jpeg|gif)$/.test(ext))){
// extension is not allowed
status.text('Only JPG, PNG or GIF files are allowed');
return false;
}
//status.text('Uploading...');
status.html('<img style="float:right;position:absolute;right:-30px;top:10px" width="16" border="0" height="16" alt="" src="<?php echo get_template_directory_uri(); ?>/img/ajax-loader.gif" id="pleasewait">');
},
onComplete: function(file, response){
//alert(response);
//On completion clear the status
status.text('');
//Add uploaded file to list
if(response!='error'){
//jQuery('<li></li>').appendTo('#files').html('<img src="'+response+'" alt="" /><br />'+file).addClass('success');
jQuery('<li></li>').appendTo('#files').html(file).addClass('success');
jQuery('<input type="hidden" id="uploaded-pres" value="'+response+'"/>').appendTo('#files');
} else{
jQuery('<li></li>').appendTo('#files').text(file).addClass('error');
}
}
});
});
</script>
Now make a upload-file.php in root folder
<?php
include_once('../../../wp-load.php');
if ( ! function_exists( 'wp_handle_upload' ) ) require_once( ABSPATH . 'wp-admin/includes/file.php' );
$uploadedfile = $_FILES['uploadfile'];
$upload_overrides = array( 'test_form' => false );
$movefile = wp_handle_upload( $uploadedfile, $upload_overrides );
if ( $movefile ) {
//echo "File is successfully uploaded.\n";
echo $movefile['url'];
} else {
echo "error";
}
?>
And lastly in html where you want to use upload button just put this
<div id="upload" ><span>Upload File<span></div><span id="status" ></span>
<ul id="files" ></ul>
Note: Its looking lengthy because i have written the js file code and php code at one place.
So arrange these file.
Hello Every One!!!
I added codes for getting unique dropdownlist in the columns of jqgrid .
Dropdownlist is coming but it is coming for the first page of the jqgrid means that dropdownlist has the unique values of the first page of the jqgrid whereas i need all the unique values of the whole Jqgrid..
Below I am posting my codes...
grid = $("#gridId");
getUniqueNames = function (columnName) {
var texts = grid.jqGrid('getCol', columnName), uniqueTexts = [],
textsLength = texts.length, text, textsMap = {}, i;
for (i = 0; i < textsLength; i++) {
text = texts[i];
if (text !== undefined && textsMap[text] === undefined) {
// to test whether the texts is unique we place it in the map.
textsMap[text] = true;
uniqueTexts.push(text);
}
}
return uniqueTexts;
},
buildSearchSelect = function (uniqueNames) {
var values = ":All";
$.each(uniqueNames, function () {
values += ";" + this + ":" + this;
});
return values;
},
setSearchSelect = function (columnName) {
grid.jqGrid('setColProp', columnName,
{
stype: 'select',
searchoptions: {
value: buildSearchSelect(getUniqueNames(columnName)),
sopt: ['eq']
}
}
);
};
This function i have called like this...
setSearchSelect('extension');
grid.jqGrid('setColProp', 'Name',
{
searchoptions: {
sopt: ['cn'],
dataInit: function (elem) {
$(elem).autocomplete({
source: getUniqueNames('Name'),
delay: 0,
minLength: 0
});
}
}
});
setSearchSelect('username');
grid.jqGrid('setColProp', 'Name',
{
searchoptions: {
sopt: ['cn'],
dataInit: function (elem) {
$(elem).autocomplete({
source: getUniqueNames('Name'),
delay: 0,
minLength: 0
});
}
}
});
In between these two code snippets I am loading data into jqgrid locally using Ajax call.
Any help will be heartely appreciated..
Thanx in advance..
I belive getCol will return only currently loaded data from jqgrid (for defined column).
Because at first you load just first page, autocomplete has no way of knowing unique values of column for more than that!
You will have to either load all pages at once (small dataset) or
fill autocomplete from database.