Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Fatal error: Uncaught exception 'JSMinException' with message 'Unterminated regular expression literal #11

Open
DWboutin opened this issue Mar 8, 2012 · 2 comments

Comments

@DWboutin
Copy link

DWboutin commented Mar 8, 2012

I got this error... Don't know from where when i minify this file below

$(document).ready(function () {
/**
* FullCalendar v1.5.1
* http://arshaw.com/fullcalendar/
*
* Use fullcalendar.css for basic styling.
* For event drag & drop, requires jQuery UI draggable.
* For event resizing, requires jQuery UI resizable.
*
* Copyright (c) 2011 Adam Shaw
* Dual licensed under the MIT and GPL licenses, located in
* MIT-LICENSE.txt and GPL-LICENSE.txt respectively.
*
* Date: Sat Apr 9 14:09:51 2011 -0700
*
*/

(function($, undefined) {


var defaults = {

    // display
    defaultView: 'month',
    aspectRatio: 1.35,
    header: {
        left: 'title',
        center: '',
        right: 'today prev,next'
    },
    weekends: true,

    // editing
    //editable: false,
    //disableDragging: false,
    //disableResizing: false,

    allDayDefault: true,
    ignoreTimezone: true,

    // event ajax
    lazyFetching: true,
    startParam: 'start',
    endParam: 'end',

    // time formats
    titleFormat: {
        month: 'MMMM yyyy',
        week: "MMM d[ yyyy]{ '—'[ MMM] d yyyy}",
        day: 'dddd, MMM d, yyyy'
    },
    columnFormat: {
        month: 'ddd',
        week: 'ddd M/d',
        day: 'dddd M/d'
    },
    timeFormat: { // for event elements
        '': 'h(:mm)t' // default
    },

    // locale
    isRTL: false,
    firstDay: 0,
    monthNames: ['January','February','March','April','May','June','July','August','September','October','November','December'],
    monthNamesShort: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
    dayNames: ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],
    dayNamesShort: ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
    buttonText: {
        prev: ' ◄ ',
        next: ' ► ',
        prevYear: ' << ',
        nextYear: ' >> ',
        today: 'today',
        month: 'month',
        week: 'week',
        day: 'day'
    },

    // jquery-ui theming
    theme: false,
    buttonIcons: {
        prev: 'circle-triangle-w',
        next: 'circle-triangle-e'
    },

    //selectable: false,
    unselectAuto: true,

    dropAccept: '*'

};

// right-to-left defaults
var rtlDefaults = {
    header: {
        left: 'next,prev today',
        center: '',
        right: 'title'
    },
    buttonText: {
        prev: ' ► ',
        next: ' ◄ ',
        prevYear: ' >> ',
        nextYear: ' << '
    },
    buttonIcons: {
        prev: 'circle-triangle-e',
        next: 'circle-triangle-w'
    }
};



var fc = $.fullCalendar = { version: "1.5.1" };
var fcViews = fc.views = {};


$.fn.fullCalendar = function(options) {


    // method calling
    if (typeof options == 'string') {
        var args = Array.prototype.slice.call(arguments, 1);
        var res;
        this.each(function() {
            var calendar = $.data(this, 'fullCalendar');
            if (calendar && $.isFunction(calendar[options])) {
                var r = calendar[options].apply(calendar, args);
                if (res === undefined) {
                    res = r;
                }
                if (options == 'destroy') {
                    $.removeData(this, 'fullCalendar');
                }
            }
        });
        if (res !== undefined) {
            return res;
        }
        return this;
    }


    // would like to have this logic in EventManager, but needs to happen before options are recursively extended
    var eventSources = options.eventSources || [];
    delete options.eventSources;
    if (options.events) {
        eventSources.push(options.events);
        delete options.events;
    }


    options = $.extend(true, {},
        defaults,
        (options.isRTL || options.isRTL===undefined && defaults.isRTL) ? rtlDefaults : {},
        options
    );


    this.each(function(i, _element) {
        var element = $(_element);
        var calendar = new Calendar(element, options, eventSources);
        element.data('fullCalendar', calendar); // TODO: look into memory leak implications
        calendar.render();
    });


    return this;

};


// function for adding/overriding defaults
function setDefaults(d) {
    $.extend(true, defaults, d);
}




function Calendar(element, options, eventSources) {
    var t = this;


    // exports
    t.options = options;
    t.render = render;
    t.destroy = destroy;
    t.refetchEvents = refetchEvents;
    t.reportEvents = reportEvents;
    t.reportEventChange = reportEventChange;
    t.rerenderEvents = rerenderEvents;
    t.changeView = changeView;
    t.select = select;
    t.unselect = unselect;
    t.prev = prev;
    t.next = next;
    t.prevYear = prevYear;
    t.nextYear = nextYear;
    t.today = today;
    t.gotoDate = gotoDate;
    t.incrementDate = incrementDate;
    t.formatDate = function(format, date) { return formatDate(format, date, options) };
    t.formatDates = function(format, date1, date2) { return formatDates(format, date1, date2, options) };
    t.getDate = getDate;
    t.getView = getView;
    t.option = option;
    t.trigger = trigger;


    // imports
    EventManager.call(t, options, eventSources);
    var isFetchNeeded = t.isFetchNeeded;
    var fetchEvents = t.fetchEvents;


    // locals
    var _element = element[0];
    var header;
    var headerElement;
    var content;
    var tm; // for making theme classes
    var currentView;
    var viewInstances = {};
    var elementOuterWidth;
    var suggestedViewHeight;
    var absoluteViewElement;
    var resizeUID = 0;
    var ignoreWindowResize = 0;
    var date = new Date();
    var events = [];
    var _dragElement;



    /* Main Rendering
    -----------------------------------------------------------------------------*/


    setYMD(date, options.year, options.month, options.date);


    function render(inc) {
        if (!content) {
            initialRender();
        }else{
            calcSize();
            markSizesDirty();
            markEventsDirty();
            renderView(inc);
        }
    }


    function initialRender() {
        tm = options.theme ? 'ui' : 'fc';
        element.addClass('fc');
        if (options.isRTL) {
            element.addClass('fc-rtl');
        }
        if (options.theme) {
            element.addClass('ui-widget');
        }
        content = $("<div class='fc-content' style='position:relative'/>")
            .prependTo(element);
        header = new Header(t, options);
        headerElement = header.render();
        if (headerElement) {
            element.prepend(headerElement);
        }
        changeView(options.defaultView);
        $(window).resize(windowResize);
        // needed for IE in a 0x0 iframe, b/c when it is resized, never triggers a windowResize
        if (!bodyVisible()) {
            lateRender();
        }
    }


    // called when we know the calendar couldn't be rendered when it was initialized,
    // but we think it's ready now
    function lateRender() {
        setTimeout(function() { // IE7 needs this so dimensions are calculated correctly
            if (!currentView.start && bodyVisible()) { // !currentView.start makes sure this never happens more than once
                renderView();
            }
        },0);
    }


    function destroy() {
        $(window).unbind('resize', windowResize);
        header.destroy();
        content.remove();
        element.removeClass('fc fc-rtl ui-widget');
    }



    function elementVisible() {
        return _element.offsetWidth !== 0;
    }


    function bodyVisible() {
        return $('body')[0].offsetWidth !== 0;
    }



    /* View Rendering
    -----------------------------------------------------------------------------*/

    // TODO: improve view switching (still weird transition in IE, and FF has whiteout problem)

    function changeView(newViewName) {
        if (!currentView || newViewName != currentView.name) {
            ignoreWindowResize++; // because setMinHeight might change the height before render (and subsequently setSize) is reached

            unselect();

            var oldView = currentView;
            var newViewElement;

            if (oldView) {
                (oldView.beforeHide || noop)(); // called before changing min-height. if called after, scroll state is reset (in Opera)
                setMinHeight(content, content.height());
                oldView.element.hide();
            }else{
                setMinHeight(content, 1); // needs to be 1 (not 0) for IE7, or else view dimensions miscalculated
            }
            content.css('overflow', 'hidden');

            currentView = viewInstances[newViewName];
            if (currentView) {
                currentView.element.show();
            }else{
                currentView = viewInstances[newViewName] = new fcViews[newViewName](
                    newViewElement = absoluteViewElement =
                        $("<div class='fc-view fc-view-" + newViewName + "' style='position:absolute'/>")
                            .appendTo(content),
                    t // the calendar object
                );
            }

            if (oldView) {
                header.deactivateButton(oldView.name);
            }
            header.activateButton(newViewName);

            renderView(); // after height has been set, will make absoluteViewElement's position=relative, then set to null

            content.css('overflow', '');
            if (oldView) {
                setMinHeight(content, 1);
            }

            if (!newViewElement) {
                (currentView.afterShow || noop)(); // called after setting min-height/overflow, so in final scroll state (for Opera)
            }

            ignoreWindowResize--;
        }
    }



    function renderView(inc) {
        if (elementVisible()) {
            ignoreWindowResize++; // because renderEvents might temporarily change the height before setSize is reached

            unselect();

            if (suggestedViewHeight === undefined) {
                calcSize();
            }

            var forceEventRender = false;
            if (!currentView.start || inc || date < currentView.start || date >= currentView.end) {
                // view must render an entire new date range (and refetch/render events)
                currentView.render(date, inc || 0); // responsible for clearing events
                setSize(true);
                forceEventRender = true;
            }
            else if (currentView.sizeDirty) {
                // view must resize (and rerender events)
                currentView.clearEvents();
                setSize();
                forceEventRender = true;
            }
            else if (currentView.eventsDirty) {
                currentView.clearEvents();
                forceEventRender = true;
            }
            currentView.sizeDirty = false;
            currentView.eventsDirty = false;
            updateEvents(forceEventRender);

            elementOuterWidth = element.outerWidth();

            header.updateTitle(currentView.title);
            var today = new Date();
            if (today >= currentView.start && today < currentView.end) {
                header.disableButton('today');
            }else{
                header.enableButton('today');
            }

            ignoreWindowResize--;
            currentView.trigger('viewDisplay', _element);
        }
    }



    /* Resizing
    -----------------------------------------------------------------------------*/


    function updateSize() {
        markSizesDirty();
        if (elementVisible()) {
            calcSize();
            setSize();
            unselect();
            currentView.clearEvents();
            currentView.renderEvents(events);
            currentView.sizeDirty = false;
        }
    }


    function markSizesDirty() {
        $.each(viewInstances, function(i, inst) {
            inst.sizeDirty = true;
        });
    }


    function calcSize() {
        if (options.contentHeight) {
            suggestedViewHeight = options.contentHeight;
        }
        else if (options.height) {
            suggestedViewHeight = options.height - (headerElement ? headerElement.height() : 0) - vsides(content);
        }
        else {
            suggestedViewHeight = Math.round(content.width() / Math.max(options.aspectRatio, .5));
        }
    }


    function setSize(dateChanged) { // todo: dateChanged?
        ignoreWindowResize++;
        currentView.setHeight(suggestedViewHeight, dateChanged);
        if (absoluteViewElement) {
            absoluteViewElement.css('position', 'relative');
            absoluteViewElement = null;
        }
        currentView.setWidth(content.width(), dateChanged);
        ignoreWindowResize--;
    }


    function windowResize() {
        if (!ignoreWindowResize) {
            if (currentView.start) { // view has already been rendered
                var uid = ++resizeUID;
                setTimeout(function() { // add a delay
                    if (uid == resizeUID && !ignoreWindowResize && elementVisible()) {
                        if (elementOuterWidth != (elementOuterWidth = element.outerWidth())) {
                            ignoreWindowResize++; // in case the windowResize callback changes the height
                            updateSize();
                            currentView.trigger('windowResize', _element);
                            ignoreWindowResize--;
                        }
                    }
                }, 200);
            }else{
                // calendar must have been initialized in a 0x0 iframe that has just been resized
                lateRender();
            }
        }
    }



    /* Event Fetching/Rendering
    -----------------------------------------------------------------------------*/


    // fetches events if necessary, rerenders events if necessary (or if forced)
    function updateEvents(forceRender) {
        if (!options.lazyFetching || isFetchNeeded(currentView.visStart, currentView.visEnd)) {
            refetchEvents();
        }
        else if (forceRender) {
            rerenderEvents();
        }
    }


    function refetchEvents() {
        fetchEvents(currentView.visStart, currentView.visEnd); // will call reportEvents
    }


    // called when event data arrives
    function reportEvents(_events) {
        events = _events;
        rerenderEvents();
    }


    // called when a single event's data has been changed
    function reportEventChange(eventID) {
        rerenderEvents(eventID);
    }


    // attempts to rerenderEvents
    function rerenderEvents(modifiedEventID) {
        markEventsDirty();
        if (elementVisible()) {
            currentView.clearEvents();
            currentView.renderEvents(events, modifiedEventID);
            currentView.eventsDirty = false;
        }
    }


    function markEventsDirty() {
        $.each(viewInstances, function(i, inst) {
            inst.eventsDirty = true;
        });
    }



    /* Selection
    -----------------------------------------------------------------------------*/


    function select(start, end, allDay) {
        currentView.select(start, end, allDay===undefined ? true : allDay);
    }


    function unselect() { // safe to be called before renderView
        if (currentView) {
            currentView.unselect();
        }
    }



    /* Date
    -----------------------------------------------------------------------------*/


    function prev() {
        renderView(-1);
    }


    function next() {
        renderView(1);
    }


    function prevYear() {
        addYears(date, -1);
        renderView();
    }


    function nextYear() {
        addYears(date, 1);
        renderView();
    }


    function today() {
        date = new Date();
        renderView();
    }


    function gotoDate(year, month, dateOfMonth) {
        if (year instanceof Date) {
            date = cloneDate(year); // provided 1 argument, a Date
        }else{
            setYMD(date, year, month, dateOfMonth);
        }
        renderView();
    }


    function incrementDate(years, months, days) {
        if (years !== undefined) {
            addYears(date, years);
        }
        if (months !== undefined) {
            addMonths(date, months);
        }
        if (days !== undefined) {
            addDays(date, days);
        }
        renderView();
    }


    function getDate() {
        return cloneDate(date);
    }



    /* Misc
    -----------------------------------------------------------------------------*/


    function getView() {
        return currentView;
    }


    function option(name, value) {
        if (value === undefined) {
            return options[name];
        }
        if (name == 'height' || name == 'contentHeight' || name == 'aspectRatio') {
            options[name] = value;
            updateSize();
        }
    }


    function trigger(name, thisObj) {
        if (options[name]) {
            return options[name].apply(
                thisObj || _element,
                Array.prototype.slice.call(arguments, 2)
            );
        }
    }



    /* External Dragging
    ------------------------------------------------------------------------*/

    if (options.droppable) {
        $(document)
            .bind('dragstart', function(ev, ui) {
                var _e = ev.target;
                var e = $(_e);
                if (!e.parents('.fc').length) { // not already inside a calendar
                    var accept = options.dropAccept;
                    if ($.isFunction(accept) ? accept.call(_e, e) : e.is(accept)) {
                        _dragElement = _e;
                        currentView.dragStart(_dragElement, ev, ui);
                    }
                }
            })
            .bind('dragstop', function(ev, ui) {
                if (_dragElement) {
                    currentView.dragStop(_dragElement, ev, ui);
                    _dragElement = null;
                }
            });
    }


}

function Header(calendar, options) {
    var t = this;


    // exports
    t.render = render;
    t.destroy = destroy;
    t.updateTitle = updateTitle;
    t.activateButton = activateButton;
    t.deactivateButton = deactivateButton;
    t.disableButton = disableButton;
    t.enableButton = enableButton;


    // locals
    var element = $([]);
    var tm;



    function render() {
        tm = options.theme ? 'ui' : 'fc';
        var sections = options.header;
        if (sections) {
            element = $("<table class='fc-header' style='width:100%'/>")
                .append(
                    $("<tr/>")
                        .append(renderSection('left'))
                        .append(renderSection('center'))
                        .append(renderSection('right'))
                );
            return element;
        }
    }


    function destroy() {
        element.remove();
    }


    function renderSection(position) {
        var e = $("<td class='fc-header-" + position + "'/>");
        var buttonStr = options.header[position];
        if (buttonStr) {
            $.each(buttonStr.split(' '), function(i) {
                if (i > 0) {
                    e.append("<span class='fc-header-space'/>");
                }
                var prevButton;
                $.each(this.split(','), function(j, buttonName) {
                    if (buttonName == 'title') {
                        e.append("<span class='fc-header-title'><h2>&nbsp;</h2></span>");
                        if (prevButton) {
                            prevButton.addClass(tm + '-corner-right');
                        }
                        prevButton = null;
                    }else{
                        var buttonClick;
                        if (calendar[buttonName]) {
                            buttonClick = calendar[buttonName]; // calendar method
                        }
                        else if (fcViews[buttonName]) {
                            buttonClick = function() {
                                button.removeClass(tm + '-state-hover'); // forget why
                                calendar.changeView(buttonName);
                            };
                        }
                        if (buttonClick) {
                            var icon = options.theme ? smartProperty(options.buttonIcons, buttonName) : null; // why are we using smartProperty here?
                            var text = smartProperty(options.buttonText, buttonName); // why are we using smartProperty here?
                            var button = $(
                                "<span class='fc-button fc-button-" + buttonName + " " + tm + "-state-default'>" +
                                    "<span class='fc-button-inner'>" +
                                        "<span class='fc-button-content'>" +
                                            (icon ?
                                                "<span class='fc-icon-wrap'>" +
                                                    "<span class='ui-icon ui-icon-" + icon + "'/>" +
                                                "</span>" :
                                                text
                                                ) +
                                        "</span>" +
                                        "<span class='fc-button-effect'><span></span></span>" +
                                    "</span>" +
                                "</span>"
                            );
                            if (button) {
                                button
                                    .click(function() {
                                        if (!button.hasClass(tm + '-state-disabled')) {
                                            buttonClick();
                                        }
                                    })
                                    .mousedown(function() {
                                        button
                                            .not('.' + tm + '-state-active')
                                            .not('.' + tm + '-state-disabled')
                                            .addClass(tm + '-state-down');
                                    })
                                    .mouseup(function() {
                                        button.removeClass(tm + '-state-down');
                                    })
                                    .hover(
                                        function() {
                                            button
                                                .not('.' + tm + '-state-active')
                                                .not('.' + tm + '-state-disabled')
                                                .addClass(tm + '-state-hover');
                                        },
                                        function() {
                                            button
                                                .removeClass(tm + '-state-hover')
                                                .removeClass(tm + '-state-down');
                                        }
                                    )
                                    .appendTo(e);
                                if (!prevButton) {
                                    button.addClass(tm + '-corner-left');
                                }
                                prevButton = button;
                            }
                        }
                    }
                });
                if (prevButton) {
                    prevButton.addClass(tm + '-corner-right');
                }
            });
        }
        return e;
    }


    function updateTitle(html) {
        element.find('h2')
            .html(html);
    }


    function activateButton(buttonName) {
        element.find('span.fc-button-' + buttonName)
            .addClass(tm + '-state-active');
    }


    function deactivateButton(buttonName) {
        element.find('span.fc-button-' + buttonName)
            .removeClass(tm + '-state-active');
    }


    function disableButton(buttonName) {
        element.find('span.fc-button-' + buttonName)
            .addClass(tm + '-state-disabled');
    }


    function enableButton(buttonName) {
        element.find('span.fc-button-' + buttonName)
            .removeClass(tm + '-state-disabled');
    }


}

fc.sourceNormalizers = [];
fc.sourceFetchers = [];

var ajaxDefaults = {
    dataType: 'json',
    cache: false
};

var eventGUID = 1;


function EventManager(options, _sources) {
    var t = this;


    // exports
    t.isFetchNeeded = isFetchNeeded;
    t.fetchEvents = fetchEvents;
    t.addEventSource = addEventSource;
    t.removeEventSource = removeEventSource;
    t.updateEvent = updateEvent;
    t.renderEvent = renderEvent;
    t.removeEvents = removeEvents;
    t.clientEvents = clientEvents;
    t.normalizeEvent = normalizeEvent;


    // imports
    var trigger = t.trigger;
    var getView = t.getView;
    var reportEvents = t.reportEvents;


    // locals
    var stickySource = { events: [] };
    var sources = [ stickySource ];
    var rangeStart, rangeEnd;
    var currentFetchID = 0;
    var pendingSourceCnt = 0;
    var loadingLevel = 0;
    var cache = [];


    for (var i=0; i<_sources.length; i++) {
        _addEventSource(_sources[i]);
    }



    /* Fetching
    -----------------------------------------------------------------------------*/


    function isFetchNeeded(start, end) {
        return !rangeStart || start < rangeStart || end > rangeEnd;
    }


    function fetchEvents(start, end) {
        rangeStart = start;
        rangeEnd = end;
        cache = [];
        var fetchID = ++currentFetchID;
        var len = sources.length;
        pendingSourceCnt = len;
        for (var i=0; i<len; i++) {
            fetchEventSource(sources[i], fetchID);
        }
    }


    function fetchEventSource(source, fetchID) {
        _fetchEventSource(source, function(events) {
            if (fetchID == currentFetchID) {
                if (events) {
                    for (var i=0; i<events.length; i++) {
                        events[i].source = source;
                        normalizeEvent(events[i]);
                    }
                    cache = cache.concat(events);
                }
                pendingSourceCnt--;
                if (!pendingSourceCnt) {
                    reportEvents(cache);
                }
            }
        });
    }


    function _fetchEventSource(source, callback) {
        var i;
        var fetchers = fc.sourceFetchers;
        var res;
        for (i=0; i<fetchers.length; i++) {
            res = fetchers[i](source, rangeStart, rangeEnd, callback);
            if (res === true) {
                // the fetcher is in charge. made its own async request
                return;
            }
            else if (typeof res == 'object') {
                // the fetcher returned a new source. process it
                _fetchEventSource(res, callback);
                return;
            }
        }
        var events = source.events;
        if (events) {
            if ($.isFunction(events)) {
                pushLoading();
                events(cloneDate(rangeStart), cloneDate(rangeEnd), function(events) {
                    callback(events);
                    popLoading();
                });
            }
            else if ($.isArray(events)) {
                callback(events);
            }
            else {
                callback();
            }
        }else{
            var url = source.url;
            if (url) {
                var success = source.success;
                var error = source.error;
                var complete = source.complete;
                var data = $.extend({}, source.data || {});
                var startParam = firstDefined(source.startParam, options.startParam);
                var endParam = firstDefined(source.endParam, options.endParam);
                if (startParam) {
                    data[startParam] = Math.round(+rangeStart / 1000);
                }
                if (endParam) {
                    data[endParam] = Math.round(+rangeEnd / 1000);
                }
                pushLoading();
                $.ajax($.extend({}, ajaxDefaults, source, {
                    data: data,
                    success: function(events) {
                        events = events || [];
                        var res = applyAll(success, this, arguments);
                        if ($.isArray(res)) {
                            events = res;
                        }
                        callback(events);
                    },
                    error: function() {
                        applyAll(error, this, arguments);
                        callback();
                    },
                    complete: function() {
                        applyAll(complete, this, arguments);
                        popLoading();
                    }
                }));
            }else{
                callback();
            }
        }
    }



    /* Sources
    -----------------------------------------------------------------------------*/


    function addEventSource(source) {
        source = _addEventSource(source);
        if (source) {
            pendingSourceCnt++;
            fetchEventSource(source, currentFetchID); // will eventually call reportEvents
        }
    }


    function _addEventSource(source) {
        if ($.isFunction(source) || $.isArray(source)) {
            source = { events: source };
        }
        else if (typeof source == 'string') {
            source = { url: source };
        }
        if (typeof source == 'object') {
            normalizeSource(source);
            sources.push(source);
            return source;
        }
    }


    function removeEventSource(source) {
        sources = $.grep(sources, function(src) {
            return !isSourcesEqual(src, source);
        });
        // remove all client events from that source
        cache = $.grep(cache, function(e) {
            return !isSourcesEqual(e.source, source);
        });
        reportEvents(cache);
    }



    /* Manipulation
    -----------------------------------------------------------------------------*/


    function updateEvent(event) { // update an existing event
        var i, len = cache.length, e,
            defaultEventEnd = getView().defaultEventEnd, // getView???
            startDelta = event.start - event._start,
            endDelta = event.end ?
                (event.end - (event._end || defaultEventEnd(event))) // event._end would be null if event.end
                : 0;                                                      // was null and event was just resized
        for (i=0; i<len; i++) {
            e = cache[i];
            if (e._id == event._id && e != event) {
                e.start = new Date(+e.start + startDelta);
                if (event.end) {
                    if (e.end) {
                        e.end = new Date(+e.end + endDelta);
                    }else{
                        e.end = new Date(+defaultEventEnd(e) + endDelta);
                    }
                }else{
                    e.end = null;
                }
                e.title = event.title;
                e.url = event.url;
                e.allDay = event.allDay;
                e.className = event.className;
                e.editable = event.editable;
                e.color = event.color;
                e.backgroudColor = event.backgroudColor;
                e.borderColor = event.borderColor;
                e.textColor = event.textColor;
                normalizeEvent(e);
            }
        }
        normalizeEvent(event);
        reportEvents(cache);
    }


    function renderEvent(event, stick) {
        normalizeEvent(event);
        if (!event.source) {
            if (stick) {
                stickySource.events.push(event);
                event.source = stickySource;
            }
            cache.push(event);
        }
        reportEvents(cache);
    }


    function removeEvents(filter) {
        if (!filter) { // remove all
            cache = [];
            // clear all array sources
            for (var i=0; i<sources.length; i++) {
                if ($.isArray(sources[i].events)) {
                    sources[i].events = [];
                }
            }
        }else{
            if (!$.isFunction(filter)) { // an event ID
                var id = filter + '';
                filter = function(e) {
                    return e._id == id;
                };
            }
            cache = $.grep(cache, filter, true);
            // remove events from array sources
            for (var i=0; i<sources.length; i++) {
                if ($.isArray(sources[i].events)) {
                    sources[i].events = $.grep(sources[i].events, filter, true);
                }
            }
        }
        reportEvents(cache);
    }


    function clientEvents(filter) {
        if ($.isFunction(filter)) {
            return $.grep(cache, filter);
        }
        else if (filter) { // an event ID
            filter += '';
            return $.grep(cache, function(e) {
                return e._id == filter;
            });
        }
        return cache; // else, return all
    }



    /* Loading State
    -----------------------------------------------------------------------------*/


    function pushLoading() {
        if (!loadingLevel++) {
            trigger('loading', null, true);
        }
    }


    function popLoading() {
        if (!--loadingLevel) {
            trigger('loading', null, false);
        }
    }



    /* Event Normalization
    -----------------------------------------------------------------------------*/


    function normalizeEvent(event) {
        var source = event.source || {};
        var ignoreTimezone = firstDefined(source.ignoreTimezone, options.ignoreTimezone);
        event._id = event._id || (event.id === undefined ? '_fc' + eventGUID++ : event.id + '');
        if (event.date) {
            if (!event.start) {
                event.start = event.date;
            }
            delete event.date;
        }
        event._start = cloneDate(event.start = parseDate(event.start, ignoreTimezone));
        event.end = parseDate(event.end, ignoreTimezone);
        if (event.end && event.end <= event.start) {
            event.end = null;
        }
        event._end = event.end ? cloneDate(event.end) : null;
        if (event.allDay === undefined) {
            event.allDay = firstDefined(source.allDayDefault, options.allDayDefault);
        }
        if (event.className) {
            if (typeof event.className == 'string') {
                event.className = event.className.split(/\s+/);
            }
        }else{
            event.className = [];
        }
        // TODO: if there is no start date, return false to indicate an invalid event
    }



    /* Utils
    ------------------------------------------------------------------------------*/


    function normalizeSource(source) {
        if (source.className) {
            // TODO: repeat code, same code for event classNames
            if (typeof source.className == 'string') {
                source.className = source.className.split(/\s+/);
            }
        }else{
            source.className = [];
        }
        var normalizers = fc.sourceNormalizers;
        for (var i=0; i<normalizers.length; i++) {
            normalizers[i](source);
        }
    }


    function isSourcesEqual(source1, source2) {
        return source1 && source2 && getSourcePrimitive(source1) == getSourcePrimitive(source2);
    }


    function getSourcePrimitive(source) {
        return ((typeof source == 'object') ? (source.events || source.url) : '') || source;
    }


}


fc.addDays = addDays;
fc.cloneDate = cloneDate;
fc.parseDate = parseDate;
fc.parseISO8601 = parseISO8601;
fc.parseTime = parseTime;
fc.formatDate = formatDate;
fc.formatDates = formatDates;



/* Date Math
-----------------------------------------------------------------------------*/

var dayIDs = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'],
    DAY_MS = 86400000,
    HOUR_MS = 3600000,
    MINUTE_MS = 60000;


function addYears(d, n, keepTime) {
    d.setFullYear(d.getFullYear() + n);
    if (!keepTime) {
        clearTime(d);
    }
    return d;
}


function addMonths(d, n, keepTime) { // prevents day overflow/underflow
    if (+d) { // prevent infinite looping on invalid dates
        var m = d.getMonth() + n,
            check = cloneDate(d);
        check.setDate(1);
        check.setMonth(m);
        d.setMonth(m);
        if (!keepTime) {
            clearTime(d);
        }
        while (d.getMonth() != check.getMonth()) {
            d.setDate(d.getDate() + (d < check ? 1 : -1));
        }
    }
    return d;
}


function addDays(d, n, keepTime) { // deals with daylight savings
    if (+d) {
        var dd = d.getDate() + n,
            check = cloneDate(d);
        check.setHours(9); // set to middle of day
        check.setDate(dd);
        d.setDate(dd);
        if (!keepTime) {
            clearTime(d);
        }
        fixDate(d, check);
    }
    return d;
}


function fixDate(d, check) { // force d to be on check's YMD, for daylight savings purposes
    if (+d) { // prevent infinite looping on invalid dates
        while (d.getDate() != check.getDate()) {
            d.setTime(+d + (d < check ? 1 : -1) * HOUR_MS);
        }
    }
}


function addMinutes(d, n) {
    d.setMinutes(d.getMinutes() + n);
    return d;
}


function clearTime(d) {
    d.setHours(0);
    d.setMinutes(0);
    d.setSeconds(0); 
    d.setMilliseconds(0);
    return d;
}


function cloneDate(d, dontKeepTime) {
    if (dontKeepTime) {
        return clearTime(new Date(+d));
    }
    return new Date(+d);
}


function zeroDate() { // returns a Date with time 00:00:00 and dateOfMonth=1
    var i=0, d;
    do {
        d = new Date(1970, i++, 1);
    } while (d.getHours()); // != 0
    return d;
}


function skipWeekend(date, inc, excl) {
    inc = inc || 1;
    while (!date.getDay() || (excl && date.getDay()==1 || !excl && date.getDay()==6)) {
        addDays(date, inc);
    }
    return date;
}


function dayDiff(d1, d2) { // d1 - d2
    return Math.round((cloneDate(d1, true) - cloneDate(d2, true)) / DAY_MS);
}


function setYMD(date, y, m, d) {
    if (y !== undefined && y != date.getFullYear()) {
        date.setDate(1);
        date.setMonth(0);
        date.setFullYear(y);
    }
    if (m !== undefined && m != date.getMonth()) {
        date.setDate(1);
        date.setMonth(m);
    }
    if (d !== undefined) {
        date.setDate(d);
    }
}



/* Date Parsing
-----------------------------------------------------------------------------*/


function parseDate(s, ignoreTimezone) { // ignoreTimezone defaults to true
    if (typeof s == 'object') { // already a Date object
        return s;
    }
    if (typeof s == 'number') { // a UNIX timestamp
        return new Date(s * 1000);
    }
    if (typeof s == 'string') {
        if (s.match(/^\d+(\.\d+)?$/)) { // a UNIX timestamp
            return new Date(parseFloat(s) * 1000);
        }
        if (ignoreTimezone === undefined) {
            ignoreTimezone = true;
        }
        return parseISO8601(s, ignoreTimezone) || (s ? new Date(s) : null);
    }

    // TODO: never return invalid dates (like from new Date(<string>)), return null instead
    return null;
}


function parseISO8601(s, ignoreTimezone) { // ignoreTimezone defaults to false
    // derived from http://delete.me.uk/2005/03/iso8601.html
    // TODO: for a know glitch/feature, read tests/issue_206_parseDate_dst.html
    var m = s.match(/^([0-9]{4})(-([0-9]{2})(-([0-9]{2})([T ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?$/);
    if (!m) {
        return null;
    }
    var date = new Date(m[1], 0, 1);
    if (ignoreTimezone || !m[14]) {
        var check = new Date(m[1], 0, 1, 9, 0);
        if (m[3]) {
            date.setMonth(m[3] - 1);
            check.setMonth(m[3] - 1);
        }
        if (m[5]) {
            date.setDate(m[5]);
            check.setDate(m[5]);
        }
        fixDate(date, check);
        if (m[7]) {
            date.setHours(m[7]);
        }
        if (m[8]) {
            date.setMinutes(m[8]);
        }
        if (m[10]) {
            date.setSeconds(m[10]);
        }
        if (m[12]) {
            date.setMilliseconds(Number("0." + m[12]) * 1000);
        }
        fixDate(date, check);
    }else{
        date.setUTCFullYear(
            m[1],
            m[3] ? m[3] - 1 : 0,
            m[5] || 1
        );
        date.setUTCHours(
            m[7] || 0,
            m[8] || 0,
            m[10] || 0,
            m[12] ? Number("0." + m[12]) * 1000 : 0
        );
        var offset = Number(m[16]) * 60 + (m[18] ? Number(m[18]) : 0);
        offset *= m[15] == '-' ? 1 : -1;
        date = new Date(+date + (offset * 60 * 1000));
    }
    return date;
}


function parseTime(s) { // returns minutes since start of day
    if (typeof s == 'number') { // an hour
        return s * 60;
    }
    if (typeof s == 'object') { // a Date object
        return s.getHours() * 60 + s.getMinutes();
    }
    var m = s.match(/(\d+)(?::(\d+))?\s*(\w+)?/);
    if (m) {
        var h = parseInt(m[1], 10);
        if (m[3]) {
            h %= 12;
            if (m[3].toLowerCase().charAt(0) == 'p') {
                h += 12;
            }
        }
        return h * 60 + (m[2] ? parseInt(m[2], 10) : 0);
    }
}



/* Date Formatting
-----------------------------------------------------------------------------*/
// TODO: use same function formatDate(date, [date2], format, [options])


function formatDate(date, format, options) {
    return formatDates(date, null, format, options);
}


function formatDates(date1, date2, format, options) {
    options = options || defaults;
    var date = date1,
        otherDate = date2,
        i, len = format.length, c,
        i2, formatter,
        res = '';
    for (i=0; i<len; i++) {
        c = format.charAt(i);
        if (c == "'") {
            for (i2=i+1; i2<len; i2++) {
                if (format.charAt(i2) == "'") {
                    if (date) {
                        if (i2 == i+1) {
                            res += "'";
                        }else{
                            res += format.substring(i+1, i2);
                        }
                        i = i2;
                    }
                    break;
                }
            }
        }
        else if (c == '(') {
            for (i2=i+1; i2<len; i2++) {
                if (format.charAt(i2) == ')') {
                    var subres = formatDate(date, format.substring(i+1, i2), options);
                    if (parseInt(subres.replace(/\D/, ''), 10)) {
                        res += subres;
                    }
                    i = i2;
                    break;
                }
            }
        }
        else if (c == '[') {
            for (i2=i+1; i2<len; i2++) {
                if (format.charAt(i2) == ']') {
                    var subformat = format.substring(i+1, i2);
                    var subres = formatDate(date, subformat, options);
                    if (subres != formatDate(otherDate, subformat, options)) {
                        res += subres;
                    }
                    i = i2;
                    break;
                }
            }
        }
        else if (c == '{') {
            date = date2;
            otherDate = date1;
        }
        else if (c == '}') {
            date = date1;
            otherDate = date2;
        }
        else {
            for (i2=len; i2>i; i2--) {
                if (formatter = dateFormatters[format.substring(i, i2)]) {
                    if (date) {
                        res += formatter(date, options);
                    }
                    i = i2 - 1;
                    break;
                }
            }
            if (i2 == i) {
                if (date) {
                    res += c;
                }
            }
        }
    }
    return res;
};


var dateFormatters = {
    s   : function(d)   { return d.getSeconds() },
    ss  : function(d)   { return zeroPad(d.getSeconds()) },
    m   : function(d)   { return d.getMinutes() },
    mm  : function(d)   { return zeroPad(d.getMinutes()) },
    h   : function(d)   { return d.getHours() % 12 || 12 },
    hh  : function(d)   { return zeroPad(d.getHours() % 12 || 12) },
    H   : function(d)   { return d.getHours() },
    HH  : function(d)   { return zeroPad(d.getHours()) },
    d   : function(d)   { return d.getDate() },
    dd  : function(d)   { return zeroPad(d.getDate()) },
    ddd : function(d,o) { return o.dayNamesShort[d.getDay()] },
    dddd: function(d,o) { return o.dayNames[d.getDay()] },
    M   : function(d)   { return d.getMonth() + 1 },
    MM  : function(d)   { return zeroPad(d.getMonth() + 1) },
    MMM : function(d,o) { return o.monthNamesShort[d.getMonth()] },
    MMMM: function(d,o) { return o.monthNames[d.getMonth()] },
    yy  : function(d)   { return (d.getFullYear()+'').substring(2) },
    yyyy: function(d)   { return d.getFullYear() },
    t   : function(d)   { return d.getHours() < 12 ? 'a' : 'p' },
    tt  : function(d)   { return d.getHours() < 12 ? 'am' : 'pm' },
    T   : function(d)   { return d.getHours() < 12 ? 'A' : 'P' },
    TT  : function(d)   { return d.getHours() < 12 ? 'AM' : 'PM' },
    u   : function(d)   { return formatDate(d, "yyyy-MM-dd'T'HH:mm:ss'Z'") },
    S   : function(d)   {
        var date = d.getDate();
        if (date > 10 && date < 20) {
            return 'th';
        }
        return ['st', 'nd', 'rd'][date%10-1] || 'th';
    }
};



fc.applyAll = applyAll;


/* Event Date Math
-----------------------------------------------------------------------------*/


function exclEndDay(event) {
    if (event.end) {
        return _exclEndDay(event.end, event.allDay);
    }else{
        return addDays(cloneDate(event.start), 1);
    }
}


function _exclEndDay(end, allDay) {
    end = cloneDate(end);
    return allDay || end.getHours() || end.getMinutes() ? addDays(end, 1) : clearTime(end);
}


function segCmp(a, b) {
    return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start);
}


function segsCollide(seg1, seg2) {
    return seg1.end > seg2.start && seg1.start < seg2.end;
}



/* Event Sorting
-----------------------------------------------------------------------------*/


// event rendering utilities
function sliceSegs(events, visEventEnds, start, end) {
    var segs = [],
        i, len=events.length, event,
        eventStart, eventEnd,
        segStart, segEnd,
        isStart, isEnd;
    for (i=0; i<len; i++) {
        event = events[i];
        eventStart = event.start;
        eventEnd = visEventEnds[i];
        if (eventEnd > start && eventStart < end) {
            if (eventStart < start) {
                segStart = cloneDate(start);
                isStart = false;
            }else{
                segStart = eventStart;
                isStart = true;
            }
            if (eventEnd > end) {
                segEnd = cloneDate(end);
                isEnd = false;
            }else{
                segEnd = eventEnd;
                isEnd = true;
            }
            segs.push({
                event: event,
                start: segStart,
                end: segEnd,
                isStart: isStart,
                isEnd: isEnd,
                msLength: segEnd - segStart
            });
        }
    } 
    return segs.sort(segCmp);
}


// event rendering calculation utilities
function stackSegs(segs) {
    var levels = [],
        i, len = segs.length, seg,
        j, collide, k;
    for (i=0; i<len; i++) {
        seg = segs[i];
        j = 0; // the level index where seg should belong
        while (true) {
            collide = false;
            if (levels[j]) {
                for (k=0; k<levels[j].length; k++) {
                    if (segsCollide(levels[j][k], seg)) {
                        collide = true;
                        break;
                    }
                }
            }
            if (collide) {
                j++;
            }else{
                break;
            }
        }
        if (levels[j]) {
            levels[j].push(seg);
        }else{
            levels[j] = [seg];
        }
    }
    return levels;
}



/* Event Element Binding
-----------------------------------------------------------------------------*/


function lazySegBind(container, segs, bindHandlers) {
    container.unbind('mouseover').mouseover(function(ev) {
        var parent=ev.target, e,
            i, seg;
        while (parent != this) {
            e = parent;
            parent = parent.parentNode;
        }
        if ((i = e._fci) !== undefined) {
            e._fci = undefined;
            seg = segs[i];
            bindHandlers(seg.event, seg.element, seg);
            $(ev.target).trigger(ev);
        }
        ev.stopPropagation();
    });
}



/* Element Dimensions
-----------------------------------------------------------------------------*/


function setOuterWidth(element, width, includeMargins) {
    for (var i=0, e; i<element.length; i++) {
        e = $(element[i]);
        e.width(Math.max(0, width - hsides(e, includeMargins)));
    }
}


function setOuterHeight(element, height, includeMargins) {
    for (var i=0, e; i<element.length; i++) {
        e = $(element[i]);
        e.height(Math.max(0, height - vsides(e, includeMargins)));
    }
}


// TODO: curCSS has been deprecated (jQuery 1.4.3 - 10/16/2010)


function hsides(element, includeMargins) {
    return hpadding(element) + hborders(element) + (includeMargins ? hmargins(element) : 0);
}


function hpadding(element) {
    return (parseFloat($.curCSS(element[0], 'paddingLeft', true)) || 0) +
           (parseFloat($.curCSS(element[0], 'paddingRight', true)) || 0);
}


function hmargins(element) {
    return (parseFloat($.curCSS(element[0], 'marginLeft', true)) || 0) +
           (parseFloat($.curCSS(element[0], 'marginRight', true)) || 0);
}


function hborders(element) {
    return (parseFloat($.curCSS(element[0], 'borderLeftWidth', true)) || 0) +
           (parseFloat($.curCSS(element[0], 'borderRightWidth', true)) || 0);
}


function vsides(element, includeMargins) {
    return vpadding(element) +  vborders(element) + (includeMargins ? vmargins(element) : 0);
}


function vpadding(element) {
    return (parseFloat($.curCSS(element[0], 'paddingTop', true)) || 0) +
           (parseFloat($.curCSS(element[0], 'paddingBottom', true)) || 0);
}


function vmargins(element) {
    return (parseFloat($.curCSS(element[0], 'marginTop', true)) || 0) +
           (parseFloat($.curCSS(element[0], 'marginBottom', true)) || 0);
}


function vborders(element) {
    return (parseFloat($.curCSS(element[0], 'borderTopWidth', true)) || 0) +
           (parseFloat($.curCSS(element[0], 'borderBottomWidth', true)) || 0);
}


function setMinHeight(element, height) {
    height = (typeof height == 'number' ? height + 'px' : height);
    element.each(function(i, _element) {
        _element.style.cssText += ';min-height:' + height + ';_height:' + height;
        // why can't we just use .css() ? i forget
    });
}



/* Misc Utils
-----------------------------------------------------------------------------*/


//TODO: arraySlice
//TODO: isFunction, grep ?


function noop() { }


function cmp(a, b) {
    return a - b;
}


function arrayMax(a) {
    return Math.max.apply(Math, a);
}


function zeroPad(n) {
    return (n < 10 ? '0' : '') + n;
}


function smartProperty(obj, name) { // get a camel-cased/namespaced property of an object
    if (obj[name] !== undefined) {
        return obj[name];
    }
    var parts = name.split(/(?=[A-Z])/),
        i=parts.length-1, res;
    for (; i>=0; i--) {
        res = obj[parts[i].toLowerCase()];
        if (res !== undefined) {
            return res;
        }
    }
    return obj[''];
}


function htmlEscape(s) {
    return s.replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/'/g, '&#039;')
        .replace(/"/g, '&quot;')
        .replace(/\n/g, '<br />');
}


function cssKey(_element) {
    return _element.id + '/' + _element.className + '/' + _element.style.cssText.replace(/(^|;)\s*(top|left|width|height)\s*:[^;]*/ig, '');
}


function disableTextSelection(element) {
    element
        .attr('unselectable', 'on')
        .css('MozUserSelect', 'none')
        .bind('selectstart.ui', function() { return false; });
}


/*
function enableTextSelection(element) {
    element
        .attr('unselectable', 'off')
        .css('MozUserSelect', '')
        .unbind('selectstart.ui');
}
*/


function markFirstLast(e) {
    e.children()
        .removeClass('fc-first fc-last')
        .filter(':first-child')
            .addClass('fc-first')
        .end()
        .filter(':last-child')
            .addClass('fc-last');
}


function setDayID(cell, date) {
    cell.each(function(i, _cell) {
        _cell.className = _cell.className.replace(/^fc-\w*/, 'fc-' + dayIDs[date.getDay()]);
        // TODO: make a way that doesn't rely on order of classes
    });
}


function getSkinCss(event, opt) {
    var source = event.source || {};
    var eventColor = event.color;
    var sourceColor = source.color;
    var optionColor = opt('eventColor');
    var backgroundColor =
        event.backgroundColor ||
        eventColor ||
        source.backgroundColor ||
        sourceColor ||
        opt('eventBackgroundColor') ||
        optionColor;
    var borderColor =
        event.borderColor ||
        eventColor ||
        source.borderColor ||
        sourceColor ||
        opt('eventBorderColor') ||
        optionColor;
    var textColor =
        event.textColor ||
        source.textColor ||
        opt('eventTextColor');
    var statements = [];
    if (backgroundColor) {
        statements.push('background-color:' + backgroundColor);
    }
    if (borderColor) {
        statements.push('border-color:' + borderColor);
    }
    if (textColor) {
        statements.push('color:' + textColor);
    }
    return statements.join(';');
}


function applyAll(functions, thisObj, args) {
    if ($.isFunction(functions)) {
        functions = [ functions ];
    }
    if (functions) {
        var i;
        var ret;
        for (i=0; i<functions.length; i++) {
            ret = functions[i].apply(thisObj, args) || ret;
        }
        return ret;
    }
}


function firstDefined() {
    for (var i=0; i<arguments.length; i++) {
        if (arguments[i] !== undefined) {
            return arguments[i];
        }
    }
}



fcViews.month = MonthView;

function MonthView(element, calendar) {
    var t = this;


    // exports
    t.render = render;


    // imports
    BasicView.call(t, element, calendar, 'month');
    var opt = t.opt;
    var renderBasic = t.renderBasic;
    var formatDate = calendar.formatDate;



    function render(date, delta) {
        if (delta) {
            addMonths(date, delta);
            date.setDate(1);
        }
        var start = cloneDate(date, true);
        start.setDate(1);
        var end = addMonths(cloneDate(start), 1);
        var visStart = cloneDate(start);
        var visEnd = cloneDate(end);
        var firstDay = opt('firstDay');
        var nwe = opt('weekends') ? 0 : 1;
        if (nwe) {
            skipWeekend(visStart);
            skipWeekend(visEnd, -1, true);
        }
        addDays(visStart, -((visStart.getDay() - Math.max(firstDay, nwe) + 7) % 7));
        addDays(visEnd, (7 - visEnd.getDay() + Math.max(firstDay, nwe)) % 7);
        var rowCnt = Math.round((visEnd - visStart) / (DAY_MS * 7));
        if (opt('weekMode') == 'fixed') {
            addDays(visEnd, (6 - rowCnt) * 7);
            rowCnt = 6;
        }
        t.title = formatDate(start, opt('titleFormat'));
        t.start = start;
        t.end = end;
        t.visStart = visStart;
        t.visEnd = visEnd;
        renderBasic(6, rowCnt, nwe ? 5 : 7, true);
    }


}

fcViews.basicWeek = BasicWeekView;

function BasicWeekView(element, calendar) {
    var t = this;


    // exports
    t.render = render;


    // imports
    BasicView.call(t, element, calendar, 'basicWeek');
    var opt = t.opt;
    var renderBasic = t.renderBasic;
    var formatDates = calendar.formatDates;



    function render(date, delta) {
        if (delta) {
            addDays(date, delta * 7);
        }
        var start = addDays(cloneDate(date), -((date.getDay() - opt('firstDay') + 7) % 7));
        var end = addDays(cloneDate(start), 7);
        var visStart = cloneDate(start);
        var visEnd = cloneDate(end);
        var weekends = opt('weekends');
        if (!weekends) {
            skipWeekend(visStart);
            skipWeekend(visEnd, -1, true);
        }
        t.title = formatDates(
            visStart,
            addDays(cloneDate(visEnd), -1),
            opt('titleFormat')
        );
        t.start = start;
        t.end = end;
        t.visStart = visStart;
        t.visEnd = visEnd;
        renderBasic(1, 1, weekends ? 7 : 5, false);
    }


}

fcViews.basicDay = BasicDayView;

//TODO: when calendar's date starts out on a weekend, shouldn't happen


function BasicDayView(element, calendar) {
    var t = this;


    // exports
    t.render = render;


    // imports
    BasicView.call(t, element, calendar, 'basicDay');
    var opt = t.opt;
    var renderBasic = t.renderBasic;
    var formatDate = calendar.formatDate;



    function render(date, delta) {
        if (delta) {
            addDays(date, delta);
            if (!opt('weekends')) {
                skipWeekend(date, delta < 0 ? -1 : 1);
            }
        }
        t.title = formatDate(date, opt('titleFormat'));
        t.start = t.visStart = cloneDate(date, true);
        t.end = t.visEnd = addDays(cloneDate(t.start), 1);
        renderBasic(1, 1, 1, false);
    }


}

setDefaults({
    weekMode: 'fixed'
});


function BasicView(element, calendar, viewName) {
    var t = this;


    // exports
    t.renderBasic = renderBasic;
    t.setHeight = setHeight;
    t.setWidth = setWidth;
    t.renderDayOverlay = renderDayOverlay;
    t.defaultSelectionEnd = defaultSelectionEnd;
    t.renderSelection = renderSelection;
    t.clearSelection = clearSelection;
    t.reportDayClick = reportDayClick; // for selection (kinda hacky)
    t.dragStart = dragStart;
    t.dragStop = dragStop;
    t.defaultEventEnd = defaultEventEnd;
    t.getHoverListener = function() { return hoverListener };
    t.colContentLeft = colContentLeft;
    t.colContentRight = colContentRight;
    t.dayOfWeekCol = dayOfWeekCol;
    t.dateCell = dateCell;
    t.cellDate = cellDate;
    t.cellIsAllDay = function() { return true };
    t.allDayRow = allDayRow;
    t.allDayBounds = allDayBounds;
    t.getRowCnt = function() { return rowCnt };
    t.getColCnt = function() { return colCnt };
    t.getColWidth = function() { return colWidth };
    t.getDaySegmentContainer = function() { return daySegmentContainer };


    // imports
    View.call(t, element, calendar, viewName);
    OverlayManager.call(t);
    SelectionManager.call(t);
    BasicEventRenderer.call(t);
    var opt = t.opt;
    var trigger = t.trigger;
    var clearEvents = t.clearEvents;
    var renderOverlay = t.renderOverlay;
    var clearOverlays = t.clearOverlays;
    var daySelectionMousedown = t.daySelectionMousedown;
    var formatDate = calendar.formatDate;


    // locals

    var head;
    var headCells;
    var body;
    var bodyRows;
    var bodyCells;
    var bodyFirstCells;
    var bodyCellTopInners;
    var daySegmentContainer;

    var viewWidth;
    var viewHeight;
    var colWidth;

    var rowCnt, colCnt;
    var coordinateGrid;
    var hoverListener;
    var colContentPositions;

    var rtl, dis, dit;
    var firstDay;
    var nwe;
    var tm;
    var colFormat;



    /* Rendering
    ------------------------------------------------------------*/


    disableTextSelection(element.addClass('fc-grid'));


    function renderBasic(maxr, r, c, showNumbers) {
        rowCnt = r;
        colCnt = c;
        updateOptions();
        var firstTime = !body;
        if (firstTime) {
            buildSkeleton(maxr, showNumbers);
        }else{
            clearEvents();
        }
        updateCells(firstTime);
    }



    function updateOptions() {
        rtl = opt('isRTL');
        if (rtl) {
            dis = -1;
            dit = colCnt - 1;
        }else{
            dis = 1;
            dit = 0;
        }
        firstDay = opt('firstDay');
        nwe = opt('weekends') ? 0 : 1;
        tm = opt('theme') ? 'ui' : 'fc';
        colFormat = opt('columnFormat');
    }



    function buildSkeleton(maxRowCnt, showNumbers) {
        var s;
        var headerClass = tm + "-widget-header";
        var contentClass = tm + "-widget-content";
        var i, j;
        var table;

        s =
            "<table class='fc-border-separate' style='width:100%' cellspacing='0'>" +
            "<thead>" +
            "<tr>";
        for (i=0; i<colCnt; i++) {
            s +=
                "<th class='fc- " + headerClass + "'/>"; // need fc- for setDayID
        }
        s +=
            "</tr>" +
            "</thead>" +
            "<tbody>";
        for (i=0; i<maxRowCnt; i++) {
            s +=
                "<tr class='fc-week" + i + "'>";
            for (j=0; j<colCnt; j++) {
                s +=
                    "<td class='fc- " + contentClass + " fc-day" + (i*colCnt+j) + "'>" + // need fc- for setDayID
                    "<div>" +
                    (showNumbers ?
                        "<div class='fc-day-number'/>" :
                        ''
                        ) +
                    "<div class='fc-day-content'>" +
                    "<div style='position:relative'>&nbsp;</div>" +
                    "</div>" +
                    "</div>" +
                    "</td>";
            }
            s +=
                "</tr>";
        }
        s +=
            "</tbody>" +
            "</table>";
        table = $(s).appendTo(element);

        head = table.find('thead');
        headCells = head.find('th');
        body = table.find('tbody');
        bodyRows = body.find('tr');
        bodyCells = body.find('td');
        bodyFirstCells = bodyCells.filter(':first-child');
        bodyCellTopInners = bodyRows.eq(0).find('div.fc-day-content div');

        markFirstLast(head.add(head.find('tr'))); // marks first+last tr/th's
        markFirstLast(bodyRows); // marks first+last td's
        bodyRows.eq(0).addClass('fc-first'); // fc-last is done in updateCells

        dayBind(bodyCells);

        daySegmentContainer =
            $("<div style='position:absolute;z-index:8;top:0;left:0'/>")
                .appendTo(element);
    }



    function updateCells(firstTime) {
        var dowDirty = firstTime || rowCnt == 1; // could the cells' day-of-weeks need updating?
        var month = t.start.getMonth();
        var today = clearTime(new Date());
        var cell;
        var date;
        var row;

        if (dowDirty) {
            headCells.each(function(i, _cell) {
                cell = $(_cell);
                date = indexDate(i);
                cell.html(formatDate(date, colFormat));
                setDayID(cell, date);
            });
        }

        bodyCells.each(function(i, _cell) {
            cell = $(_cell);
            date = indexDate(i);
            if (date.getMonth() == month) {
                cell.removeClass('fc-other-month');
            }else{
                cell.addClass('fc-other-month');
            }
            if (+date == +today) {
                cell.addClass(tm + '-state-highlight fc-today');
            }else{
                cell.removeClass(tm + '-state-highlight fc-today');
            }
            cell.find('div.fc-day-number').text(date.getDate());
            if (dowDirty) {
                setDayID(cell, date);
            }
        });

        bodyRows.each(function(i, _row) {
            row = $(_row);
            if (i < rowCnt) {
                row.show();
                if (i == rowCnt-1) {
                    row.addClass('fc-last');
                }else{
                    row.removeClass('fc-last');
                }
            }else{
                row.hide();
            }
        });
    }



    function setHeight(height) {
        viewHeight = height;

        var bodyHeight = viewHeight - head.height();
        var rowHeight;
        var rowHeightLast;
        var cell;

        if (opt('weekMode') == 'variable') {
            rowHeight = rowHeightLast = Math.floor(bodyHeight / (rowCnt==1 ? 2 : 6));
        }else{
            rowHeight = Math.floor(bodyHeight / rowCnt);
            rowHeightLast = bodyHeight - rowHeight * (rowCnt-1);
        }

        bodyFirstCells.each(function(i, _cell) {
            if (i < rowCnt) {
                cell = $(_cell);
                setMinHeight(
                    cell.find('> div'),
                    (i==rowCnt-1 ? rowHeightLast : rowHeight) - vsides(cell)
                );
            }
        });

    }


    function setWidth(width) {
        viewWidth = width;
        colContentPositions.clear();
        colWidth = Math.floor(viewWidth / colCnt);
        setOuterWidth(headCells.slice(0, -1), colWidth);
    }



    /* Day clicking and binding
    -----------------------------------------------------------*/


    function dayBind(days) {
        days.click(dayClick)
            .mousedown(daySelectionMousedown);
    }


    function dayClick(ev) {
        if (!opt('selectable')) { // if selectable, SelectionManager will worry about dayClick
            var index = parseInt(this.className.match(/fc\-day(\d+)/)[1]); // TODO: maybe use .data
            var date = indexDate(index);
            trigger('dayClick', this, date, true, ev);
        }
    }



    /* Semi-transparent Overlay Helpers
    ------------------------------------------------------*/


    function renderDayOverlay(overlayStart, overlayEnd, refreshCoordinateGrid) { // overlayEnd is exclusive
        if (refreshCoordinateGrid) {
            coordinateGrid.build();
        }
        var rowStart = cloneDate(t.visStart);
        var rowEnd = addDays(cloneDate(rowStart), colCnt);
        for (var i=0; i<rowCnt; i++) {
            var stretchStart = new Date(Math.max(rowStart, overlayStart));
            var stretchEnd = new Date(Math.min(rowEnd, overlayEnd));
            if (stretchStart < stretchEnd) {
                var colStart, colEnd;
                if (rtl) {
                    colStart = dayDiff(stretchEnd, rowStart)*dis+dit+1;
                    colEnd = dayDiff(stretchStart, rowStart)*dis+dit+1;
                }else{
                    colStart = dayDiff(stretchStart, rowStart);
                    colEnd = dayDiff(stretchEnd, rowStart);
                }
                dayBind(
                    renderCellOverlay(i, colStart, i, colEnd-1)
                );
            }
            addDays(rowStart, 7);
            addDays(rowEnd, 7);
        }
    }


    function renderCellOverlay(row0, col0, row1, col1) { // row1,col1 is inclusive
        var rect = coordinateGrid.rect(row0, col0, row1, col1, element);
        return renderOverlay(rect, element);
    }



    /* Selection
    -----------------------------------------------------------------------*/


    function defaultSelectionEnd(startDate, allDay) {
        return cloneDate(startDate);
    }


    function renderSelection(startDate, endDate, allDay) {
        renderDayOverlay(startDate, addDays(cloneDate(endDate), 1), true); // rebuild every time???
    }


    function clearSelection() {
        clearOverlays();
    }


    function reportDayClick(date, allDay, ev) {
        var cell = dateCell(date);
        var _element = bodyCells[cell.row*colCnt + cell.col];
        trigger('dayClick', _element, date, allDay, ev);
    }



    /* External Dragging
    -----------------------------------------------------------------------*/


    function dragStart(_dragElement, ev, ui) {
        hoverListener.start(function(cell) {
            clearOverlays();
            if (cell) {
                renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
            }
        }, ev);
    }


    function dragStop(_dragElement, ev, ui) {
        var cell = hoverListener.stop();
        clearOverlays();
        if (cell) {
            var d = cellDate(cell);
            trigger('drop', _dragElement, d, true, ev, ui);
        }
    }



    /* Utilities
    --------------------------------------------------------*/


    function defaultEventEnd(event) {
        return cloneDate(event.start);
    }


    coordinateGrid = new CoordinateGrid(function(rows, cols) {
        var e, n, p;
        headCells.each(function(i, _e) {
            e = $(_e);
            n = e.offset().left;
            if (i) {
                p[1] = n;
            }
            p = [n];
            cols[i] = p;
        });
        p[1] = n + e.outerWidth();
        bodyRows.each(function(i, _e) {
            if (i < rowCnt) {
                e = $(_e);
                n = e.offset().top;
                if (i) {
                    p[1] = n;
                }
                p = [n];
                rows[i] = p;
            }
        });
        p[1] = n + e.outerHeight();
    });


    hoverListener = new HoverListener(coordinateGrid);


    colContentPositions = new HorizontalPositionCache(function(col) {
        return bodyCellTopInners.eq(col);
    });


    function colContentLeft(col) {
        return colContentPositions.left(col);
    }


    function colContentRight(col) {
        return colContentPositions.right(col);
    }




    function dateCell(date) {
        return {
            row: Math.floor(dayDiff(date, t.visStart) / 7),
            col: dayOfWeekCol(date.getDay())
        };
    }


    function cellDate(cell) {
        return _cellDate(cell.row, cell.col);
    }


    function _cellDate(row, col) {
        return addDays(cloneDate(t.visStart), row*7 + col*dis+dit);
        // what about weekends in middle of week?
    }


    function indexDate(index) {
        return _cellDate(Math.floor(index/colCnt), index%colCnt);
    }


    function dayOfWeekCol(dayOfWeek) {
        return ((dayOfWeek - Math.max(firstDay, nwe) + colCnt) % colCnt) * dis + dit;
    }




    function allDayRow(i) {
        return bodyRows.eq(i);
    }


    function allDayBounds(i) {
        return {
            left: 0,
            right: viewWidth
        };
    }


}

function BasicEventRenderer() {
    var t = this;


    // exports
    t.renderEvents = renderEvents;
    t.compileDaySegs = compileSegs; // for DayEventRenderer
    t.clearEvents = clearEvents;
    t.bindDaySeg = bindDaySeg;


    // imports
    DayEventRenderer.call(t);
    var opt = t.opt;
    var trigger = t.trigger;
    //var setOverflowHidden = t.setOverflowHidden;
    var isEventDraggable = t.isEventDraggable;
    var isEventResizable = t.isEventResizable;
    var reportEvents = t.reportEvents;
    var reportEventClear = t.reportEventClear;
    var eventElementHandlers = t.eventElementHandlers;
    var showEvents = t.showEvents;
    var hideEvents = t.hideEvents;
    var eventDrop = t.eventDrop;
    var getDaySegmentContainer = t.getDaySegmentContainer;
    var getHoverListener = t.getHoverListener;
    var renderDayOverlay = t.renderDayOverlay;
    var clearOverlays = t.clearOverlays;
    var getRowCnt = t.getRowCnt;
    var getColCnt = t.getColCnt;
    var renderDaySegs = t.renderDaySegs;
    var resizableDayEvent = t.resizableDayEvent;



    /* Rendering
    --------------------------------------------------------------------*/


    function renderEvents(events, modifiedEventId) {
        reportEvents(events);
        renderDaySegs(compileSegs(events), modifiedEventId);
    }


    function clearEvents() {
        reportEventClear();
        getDaySegmentContainer().empty();
    }


    function compileSegs(events) {
        var rowCnt = getRowCnt(),
            colCnt = getColCnt(),
            d1 = cloneDate(t.visStart),
            d2 = addDays(cloneDate(d1), colCnt),
            visEventsEnds = $.map(events, exclEndDay),
            i, row,
            j, level,
            k, seg,
            segs=[];
        for (i=0; i<rowCnt; i++) {
            row = stackSegs(sliceSegs(events, visEventsEnds, d1, d2));
            for (j=0; j<row.length; j++) {
                level = row[j];
                for (k=0; k<level.length; k++) {
                    seg = level[k];
                    seg.row = i;
                    seg.level = j; // not needed anymore
                    segs.push(seg);
                }
            }
            addDays(d1, 7);
            addDays(d2, 7);
        }
        return segs;
    }


    function bindDaySeg(event, eventElement, seg) {
        if (isEventDraggable(event)) {
            draggableDayEvent(event, eventElement);
        }
        if (seg.isEnd && isEventResizable(event)) {
            resizableDayEvent(event, eventElement, seg);
        }
        eventElementHandlers(event, eventElement);
            // needs to be after, because resizableDayEvent might stopImmediatePropagation on click
    }



    /* Dragging
    ----------------------------------------------------------------------------*/


    function draggableDayEvent(event, eventElement) {
        var hoverListener = getHoverListener();
        var dayDelta;
        eventElement.draggable({
            zIndex: 9,
            delay: 50,
            opacity: opt('dragOpacity'),
            revertDuration: opt('dragRevertDuration'),
            start: function(e
@cagianx
Copy link

cagianx commented Jun 8, 2012

same error with the same library (fullcalendar) and also with Modernizr (http://modernizr.com/downloads/modernizr-2.5.3.js)

@jaydiablo
Copy link

In my branch of this project (https://github.com/jaydiablo/jsmin-php) I added modernizr to the test case, and it passes (which means that the minified version of the library matches the JSMin (original C library) minified version). Give it a shot if you'd like.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants