From 38d5c9faf1051bfd675e93fa97a1cd40f573a770 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Fri, 10 Apr 2009 07:00:41 +0000 Subject: [PATCH 001/185] Initial directory structure. From b676a330d97dd626c133209135aa6e5e288b5f8c Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Sun, 26 Apr 2009 04:43:27 +0000 Subject: [PATCH 002/185] --- Makefile | 18 + docs/Makefile | 82 + docs/conf.py | 190 ++ docs/index.txt | 228 ++ docs/templates/layout.html | 8 + examples/example.html | 73 + examples/gcal.html | 54 + examples/json.html | 41 + examples/json_events.php | 25 + full_calendar.css | 135 ++ full_calendar.js | 726 ++++++ gcal.js | 55 + jquery/jquery.js | 4376 ++++++++++++++++++++++++++++++++++++ jquery/ui.core.js | 519 +++++ jquery/ui.draggable.js | 766 +++++++ version.txt | 1 + 16 files changed, 7297 insertions(+) create mode 100644 Makefile create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.txt create mode 100644 docs/templates/layout.html create mode 100644 examples/example.html create mode 100644 examples/gcal.html create mode 100644 examples/json.html create mode 100644 examples/json_events.php create mode 100644 full_calendar.css create mode 100644 full_calendar.js create mode 100644 gcal.js create mode 100644 jquery/jquery.js create mode 100644 jquery/ui.core.js create mode 100644 jquery/ui.draggable.js create mode 100644 version.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0399baf --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ + +FILES =\ + *.js\ + *.css\ + jquery\ + examples + +zip: + @mkdir -p build/full_calendar + @cp -rt build/full_calendar ${FILES} + @sed -i "s/Version:/& `cat version.txt`/" build/full_calendar/full_calendar.js + @mkdir -p dist + @cd build;\ + zip -r ../dist/full_calendar_`cat ../version.txt`.zip * + +clean: + @rm -rf build/* + @rm -rf dist/* diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..1fee6dd --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,82 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html web pickle htmlhelp latex changes linkcheck + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview over all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + +clean: + -rm -rf build/* + +html: + mkdir -p build/html build/doctrees + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html + @echo + @echo "Build finished. The HTML pages are in build/html." + +arshaw: + mkdir -p build/html build/doctrees + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html + cp -r build/html/* /var/www/arshaw/pages/fullcalendar/docs/ + @echo + @echo "Build finished. The HTML pages are in build/html." + +pickle: + mkdir -p build/pickle build/doctrees + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +web: pickle + +json: + mkdir -p build/json build/doctrees + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + mkdir -p build/htmlhelp build/doctrees + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in build/htmlhelp." + +latex: + mkdir -p build/latex build/doctrees + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex + @echo + @echo "Build finished; the LaTeX files are in build/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + mkdir -p build/changes build/doctrees + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes + @echo + @echo "The overview file is in build/changes." + +linkcheck: + mkdir -p build/linkcheck build/doctrees + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in build/linkcheck/output.txt." diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..cf7a292 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# +# FullCalendar documentation build configuration file, created by +# sphinx-quickstart on Sat Apr 11 19:03:41 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# The contents of this file are pickled, so don't put values in the namespace +# that aren't pickleable (module imports are okay, they're removed automatically). +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If your extensions are in another directory, add it here. If the directory +# is relative to the documentation root, use os.path.abspath to make it +# absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# General configuration +# --------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['templates'] + +# The suffix of source filenames. +source_suffix = '.txt' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'FullCalendar' +copyright = u'2009, Adam Shaw' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = open('../version.txt').read() +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# Options for HTML output +# ----------------------- + +# The style sheet to use for HTML and HTML Help pages. A file of that name +# must exist either in Sphinx' static/ path, or in one of the custom paths +# given in html_static_path. +html_style = 'default.css' + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, the reST sources are included in the HTML build as _sources/. +#html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +html_file_suffix = '.php' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'FullCalendardoc' + + +# Options for LaTeX output +# ------------------------ + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, document class [howto/manual]). +latex_documents = [ + ('index', 'FullCalendar.tex', ur'FullCalendar Documentation', + ur'Adam Shaw', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/docs/index.txt b/docs/index.txt new file mode 100644 index 0000000..797a4e9 --- /dev/null +++ b/docs/index.txt @@ -0,0 +1,228 @@ + +.. highlight:: javascript + +Usage +===== + +.. code-block:: javascript + + .fullCalendar({ + // initializes a calendar + // see options, data provider, and triggered events below + }) + + .fullCalendar('nextMonth') // move the calendar one month ahead + + .fullCalendar('currentMonth') // move to current month + + .fullCalendar('prevMonth') // move back one month + + .fullCalendar('gotoMonth', year, month) + // go to an arbitrary month. 'month' is zero-based + + +Options +======= + + **year**, **month**: integers + The month that will be displayed when the calendar first loads. + ``month`` is zero-based (January is 0, February is 1, etc). + + **draggable**: boolean, default:``false`` + Determines if all events on the calendar can be dragged & dropped. If + ``true``, requires `jQuery UI `_ core and draggables. + Can be overridden on a per-event basis with the ``draggable`` property of + each :ref:`CalEvent ` object. + + **fixedWeeks**: boolean, default:``true`` + If ``true``, the calendar will always be 6 weeks tall. If ``false``, the + calendar's height will vary per month. + + **abbrevDayHeadings**: boolean, default:``true`` + Whether to display "Sun" versus "Sunday" for days of the week. + + **title**: boolean, default:``true`` + Determines whether a title such as "January 2009" will be displayed at the + top of the calendar. + + **buttons**: boolean/hash, default:``true`` + Determines whether navigation buttons will be displayed at the top of the + calendar. A hash with keys 'today', 'prev', and 'next' will determine if + each individual button is displayed. Strings can be provided to change + each button's text. + + **showTime**: boolean/ ``"guess"``, default:``"guess"`` + Determines if times such as "8a" or "1p" will be displayed before each + event's title. ``"guess"`` displays times only for events with non-zero + start or end times. + + **eventDragOpacity**: float + The opacity of an event element while it is being dragged (0.0 - 1.0) + + **eventRevertDuration**: integer + Controls the duration (in milliseconds) of the animation of an event + snapping back into place. + + +.. _EventDataProvider: + +Event Data Provider +=================== + + **events**: array/string/function + An array of :ref:`CalEvent` can be used to hardcode events into the + calendar. + + Or, a URL can be provided. This URL should return JSON for an array of + :ref:`CalEvent`. GET parameters, determined by the ``startParam`` and + ``endParam`` options, will be inserted into the URL. These parameters + indicate the UNIX timestamp of the start of the first visible day + (inclusive) and the end of the last visible day (exclusive). + + Or, a function can be provided for custom fetching. The function is + queried every time event data is needed. The function is passed a ``start`` + Date object, an ``end`` Date object, and a function to be called when + fetching is complete. Here is the general form:: + + events: function(start, end, callback) { + + // do some asynchronous ajax + $.getJSON("/myscript", + { + start: start.getTime(), + end: end.getTime() + }, + function(result) { + + // format the result into an array of 'CalEvent' objects + // (not seen here) + + // then, pass the 'calevent' array to the callback + callback(calevents); + + }); + + } + + **startParam**: string, default:``"start"`` + This GET parameter will be inserted into the URL when ``event`` is a URL + string. Example of a resulting URL: ``/myjsonscript?start=1238562000`` + + **endParam**: string, default:``"end"`` + This GET parameter will be inserted into the URL when ``event`` is a URL + string. Example of a resulting URL: ``/myjsonscript?end=1240691444`` + + +.. _TriggeredEvents: + +Triggered Events +================ + + **monthDisplay**: function(year, month, monthTitle) + Triggered once when the calendar loads and every time the + calendar's month is changed. ``month`` is zero-based. ``monthTitle`` + contains the new title of the month (ex: "January 2009") + + **loading**: function(isLoading) + Triggered with a ``true`` argument when the calendar begins fetching + events via AJAX. Triggered with ``false`` when done. + + **dayClick**: function(dayDate) + Triggered when the user clicks on a day. ``dayDate`` is a Date object with + it's time set to 00:00:00. + + ``this`` is set to the TD element of the clicked day. + + **eventRender**: function(calEvent, element) + Triggered before an element is rendered for the given ``calEvent``. + ``element`` is the jQuery element that will be used by default. You can modify + this element or return a brand new element that will be used instead. + + **eventClick**, **eventMouseover**, **eventMouseout**: function(calEvent, domEvent) + Triggered on click/mouseover/mouseout actions for an event. + ``calEvent`` holds that event's information (date, title, etc). + ``domEvent`` holds the native DOM event (with information about click position, etc). + + ``this`` is set to the event's TABLE element + + For ``eventClick``, return ``false`` to prevent the browser from going to + calEvent's URL. + + **eventDragStart**, **eventDragStop**: function(calEvent, domEvent, ui) + Triggered before/after an event is dragged (but not necessarily moved to a new day). + ``calEvent`` holds that event's information (date, title, etc). + ``domEvent`` holds the native DOM event (with information about click position, etc). + ``ui`` holds the jQuery UI object. + + ``this`` is set to the event's TABLE element + + **eventDrop**: function(calEvent, dayDelta, domEvent, ui) + Triggered when dragging stops and the event has moved to a *different* day. + ``dayDelta`` holds the number of days the event was moved forward (a positive number) + or backwards (a negative number). + + ``dayDelta`` is elegant for dealing with multi-day and repeating events. + If updating a remote database, just add the ``dayDelta`` to the start + and end times of all events with the given ``calEvent.id`` + + +.. _CalEvent: + +CalEvent Objects +================ + +A ``CalEvent`` is a data structure that frequents FullCalendar's API. It is +used when a custom event-fetcher needs to report to the :ref:`EventDataProvider`. +It is also used in various :ref:`TriggeredEvents`. Here are the properties of a +``CalEvent``\: + + **id**: integer/string, + Uniquely identifies the given event. Absolutely essential for multi-day + and repeating events to be dragged and dropped correctly. + + **title**: string, + The text on an event's element + + **date**: date + Alias for ``start`` + + **start**: date + A javascript Date object indicating the date/time an event begins. + Events with ambiguous time-of-day should use 00:00:00. + + When reporting to the :ref:`EventDataProvider`, for convenience, + one can also use a string in IETF format (ex: "Wed, 18 Oct 2009 13:00:00 EST"), + a string in ISO8601 format (ex: "2009-11-05T13:15:30Z") or an integer + UNIX timestamp. + + **end**: date (optional) + A javascript Date object indicating the date/time an event ends + (exclusively). If an event has an ambiguous end time, ``end`` should be + set to midnight of the next day. This is implied if ``end`` is omitted. + (For convenience with the :ref:`EventDataProvider`). + + IETF and ISO8601 strings can be used for the :ref:`EventDataProvider`. + + **draggable**: boolean (optional) + Overrides the master ``draggable`` property for this single event. + + **showTime**: boolean/ ``"guess"`` (optional) + Overrides the master ``showTime`` property for this single event. + +When giving events to the :ref:`EventDataProvider`, one can include other +properties beyond the ones listed. This is useful if you want to earmark your +events with additional data to be retrieved later during a +:ref:`Triggered Event `. + + +Extras +====== + +FullCalendar provides some extra date utilities\: + + **$.parseISO8601(string, ignoreTimezone)** + Parses an ISO8601 string and returns a ``Date`` object + + **$.ISO8601String(date)** + Takes a ``Date`` object and returns an ISO8601 string + diff --git a/docs/templates/layout.html b/docs/templates/layout.html new file mode 100644 index 0000000..3524703 --- /dev/null +++ b/docs/templates/layout.html @@ -0,0 +1,8 @@ + + + + + +{% block body %}{% endblock %} + + diff --git a/examples/example.html b/examples/example.html new file mode 100644 index 0000000..6e2ba84 --- /dev/null +++ b/examples/example.html @@ -0,0 +1,73 @@ + + + + + + + + + + + + +
+ + diff --git a/examples/gcal.html b/examples/gcal.html new file mode 100644 index 0000000..951df3b --- /dev/null +++ b/examples/gcal.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + +
+ + diff --git a/examples/json.html b/examples/json.html new file mode 100644 index 0000000..47edc3b --- /dev/null +++ b/examples/json.html @@ -0,0 +1,41 @@ + + + + + + + + + + + + +
+

json_events.php needs to be running in the same directory.

+ + diff --git a/examples/json_events.php b/examples/json_events.php new file mode 100644 index 0000000..bf39ba6 --- /dev/null +++ b/examples/json_events.php @@ -0,0 +1,25 @@ + 1, + 'title' => "Event1", + 'start' => "$year-$month-10", + 'url' => "http://yahoo.com/" + ), + + array( + 'id' => 2, + 'title' => "Event2", + 'start' => "$year-$month-20", + 'end' => "$year-$month-22", + 'url' => "http://yahoo.com/" + ) + + )); + +?> diff --git a/full_calendar.css b/full_calendar.css new file mode 100644 index 0000000..c49f715 --- /dev/null +++ b/full_calendar.css @@ -0,0 +1,135 @@ + +/* top area w/ month title and buttons */ + +.full-calendar-header { + text-align: left; + } + +.full-calendar-buttons { + float: right; + } + +.full-calendar-buttons input { + vertical-align: middle; + font-size: 1.0em; + } + +.full-calendar-prev, +.full-calendar-next { + width: 40px; + margin-left: 5px; + } + + + +/* borders */ + +.full-calendar-month { + clear: both; + overflow: hidden; /* + ^ prevents draggable events from leaving. + reason for our long-winded border css. + borders now look consistent across doctypes. */ + border: 1px solid #ccc; /* border color & style */ + } + +.full-calendar-month table { + border-collapse: collapse; + border-spacing: 0; + } + +.full-calendar-month th.day-heading, +.full-calendar-month td.day { + padding: 0; + vertical-align: top; + border-style: solid; /* border style */ + border-color: #ccc; /* border color */ + border-width: 1px 0 0 1px; + } + +.full-calendar-month tr.day-headings th { + border-top: 0; + } + +.full-calendar-month th.sun, +.full-calendar-month td.sun { + border-left: 0; + } + + + +/* day styling */ + +.full-calendar-month td.today { + background: #FFFFCC; + } + +.full-calendar-month .day-number { + text-align: right; + padding-right: 2px; + } + +.full-calendar-month .other-month .day-number { + color: #bbb; + } + +.full-calendar-month .day-content { + padding: 2px 2px 0; /* distance between events and day edges */ + } + +.full-calendar-month td.day { + /* FullCalendar automatically chooses a height, but this can be overridden: */ + /* height: 100px !important; */ + } + + + +/* event styling */ + +.full-calendar-month .event { + margin-bottom: 2px; + font-size: .85em; + cursor: pointer; + text-align: left; + } + +.full-calendar-month .ui-draggable-dragging td { + cursor: move; + } + +.full-calendar-month .event td { + background: #C1D9EC; + padding: 0; + } + +.full-calendar-month .event td.ne, +.full-calendar-month .event td.nw, +.full-calendar-month .event td.se, +.full-calendar-month .event td.sw { + background: none; + width: 1px; /* <-- remove if you dont want "rounded" corners */ + height: 1px; /* <-- */ + } + +.full-calendar-month .nobg td { + background: none; + } + +.full-calendar-month .event td.c { + padding: 0 2px; + } + +.full-calendar-month .event-time { + font-weight: bold; + } + + + +/* the rectangle that covers a day when dragging an event */ + +.full-calendar-month .over-day { + background: #ADDBFF; + opacity: .2; + filter: alpha(opacity=20); + } + diff --git a/full_calendar.js b/full_calendar.js new file mode 100644 index 0000000..5e8c5ed --- /dev/null +++ b/full_calendar.js @@ -0,0 +1,726 @@ +/* + * Version: + * + */ + +(function($) { + + $.fn.fullCalendar = function(options) { + + if (typeof options == 'string') { + var args = Array.prototype.slice.call(arguments, 1); + this.each(function() { + $.data(this, 'fullCalendar')[options].apply(this, args); + }); + return this; + } + + options = options || {}; + + var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime; + var startParam = options.startParam || 'start'; + var endParam = options.endParam || 'end'; + var bo = options.buttons; + + this.each(function() { + + + + var date = options.year ? new Date(options.year, options.month || 0, 1) : new Date(); + var start, end, today, numWeeks; + var events = $.isArray(options.events) ? cleanEvents(options.events) : null; + var ignoreResizes = false; + + function updateMonth() { + clearEvents(); + render(); + } + + function today() { + date = new Date(); + updateMonth(); + } + + function prevMonth() { + addMonths(date, -1); + updateMonth(); + } + + function nextMonth() { + addMonths(date, 1); + updateMonth(); + } + + function gotoMonth(year, month) { + date = new Date(year, month, 1); + updateMonth(); + } + + $.data(this, 'fullCalendar', { + today:today, prevMonth:prevMonth, nextMonth:nextMonth, gotoMonth:gotoMonth + }); + + + + + + + var titleElement, todayButton, monthElement; + var header = $("
").appendTo(this); + + if (bo != false) { + var buttons = $("
").appendTo(header); + todayButton = + $("") + .click(today); + var prevButton = + $("") + .click(prevMonth); + var nextButton = + $("") + .click(nextMonth); + if (typeof bo == 'object') { + if (bo.today != false) { + if (typeof bo.today == 'string') todayButton.val(bo.today); + buttons.append(todayButton); + } + if (bo.prev != false) { + if (typeof bo.prev == 'string') prevButton.val(bo.prev); + buttons.append(prevButton); + } + if (bo.next != false) { + if (typeof bo.next == 'string') nextButton.val(bo.next); + buttons.append(nextButton); + } + }else{ + buttons + .append(todayButton) + .append(prevButton) + .append(nextButton); + } + } + + if (options.title !== false) + titleElement = $("

").appendTo(header); + + monthElement = $("
").appendTo(this); + + + + + + + var tbody, glass, monthTitle; + + function render() { + + ignoreResizes = true; + date.setDate(1); + clearTime(date); + var year = date.getFullYear(); + var month = date.getMonth(); + monthTitle = monthNames[month] + ' ' + year; + if (titleElement) titleElement.text(monthTitle); + + clearTime(date); + start = cloneDate(date); + addDays(start, -start.getDay()); + end = cloneDate(date); + addMonths(end, 1); + addDays(end, (7 - end.getDay()) % 7); + numWeeks = Math.round((end.getTime() - start.getTime()) / 604800000); + if (options.fixedWeeks != false) { + addDays(end, (6 - numWeeks) * 7); + numWeeks = 6; + } + + today = clearTime(new Date()); + if (todayButton) { + if (today.getFullYear() == year && today.getMonth() == month) { + todayButton.css('visibility', 'hidden'); + }else{ + todayButton.css('visibility', 'visible'); + } + } + + if (!tbody) { + + tbody = ""; + for (var i=0; i<7; i++) { + tbody += + "" + + (options.abbrevDayHeadings!=false ? dayAbbrevs[i] : dayNames[i]) + + ""; + } + var d = cloneDate(start); + for (var i=0; i"; + for (var j=0; j<7; j++) { + tbody += + "
" + d.getDate() + "
" + + "
"; + addDays(d, 1); + } + tbody += ""; + } + tbody += ""; + tbody = $(tbody) + .appendTo($("") + .appendTo(monthElement)); + + glass = $("
") + .appendTo(monthElement) + .click(function(ev, ui) { + if (options.dayClick) { + buildDayGrid(); + var td = dayTD(ev.pageX, ev.pageY); + if (td) return options.dayClick.call(td, dayDate(td)); + } + }); + + }else{ + + var diff = numWeeks - (tbody.find('tr').length - 1); + if (diff < 0) { + tbody.find('tr:gt(' + numWeeks + ')').remove(); + } + else if (diff > 0) { + var trs = ""; + for (var i=0; i"; + for (var j=0; j<7; j++) { + trs += + "
"; + } + trs += ""; + } + if (trs) tbody.append(trs); + } + + var d = cloneDate(start); + tbody.find('td').each(function() { + if (d.getMonth() == month) { + $(this).removeClass('other-month'); + }else{ + $(this).addClass('other-month'); + } + if (d.getTime() == today.getTime()) { + $(this).addClass('today'); + }else{ + $(this).removeClass('today'); + } + $(this.childNodes[0]).text(d.getDate()); + addDays(d, 1); + }); + + } + + resizeTable(); + + if (typeof options.events == 'string') { + if (options.loading) options.loading(true); + var jsonOptions = {}; + jsonOptions[startParam] = Math.round(start.getTime() / 1000); + jsonOptions[endParam] = Math.round(end.getTime() / 1000); + $.getJSON(options.events, jsonOptions, function(data) { + events = cleanEvents(data); + renderEvents(events); + if (options.loading) options.loading(false); + }); + } + else if ($.isFunction(options.events)) { + if (options.loading) options.loading(true); + options.events(start, end, + function(data) { + events = cleanEvents(data); + renderEvents(events); + if (options.loading) options.loading(false); + }); + } + else renderEvents(events); + + ignoreResizes = false; + + if (options.monthDisplay) + options.monthDisplay(date.getFullYear(), date.getMonth(), monthTitle); + + } + + + + + + + var eventMatrix = []; + + function renderEvents() { + eventMatrix = []; + var i = 0; + var ws = cloneDate(start); + var we = addDays(cloneDate(ws), 7); + while (ws.getTime() < end.getTime()) { + var segs = []; + $.each(events, function(j, event) { + if (event.end.getTime() > ws.getTime() && event.start.getTime() < we.getTime()) { + var ss, se, isStart, isEnd; + if (event.start.getTime() < ws.getTime()) { + ss = cloneDate(ws); + isStart = false; + }else{ + ss = cloneDate(event.start); + isStart = true; + } + if (event.end.getTime() > we.getTime()) { + se = cloneDate(we); + isEnd = false; + }else{ + se = cloneDate(event.end); + isEnd = true; + } + ss = clearTime(ss); + se = clearTime((se.getHours()==0 && se.getMinutes()==0) ? se : addDays(se, 1)); + segs.push({ + event: event, start: ss, end: se, + isStart: isStart, isEnd: isEnd, msLength: se - ss + }); + } + }); + segs.sort(function(a, b) { return b.msLength - a.msLength; }); + var levels = []; + $.each(segs, function(j, seg) { + var l = 0; // level index + while (true) { + var collide = false; + if (levels[l]) { + for (var k=0; k levels[l][k].start.getTime() && + seg.start.getTime() < levels[l][k].end.getTime()) { + collide = true; + break; + } + } + } + if (collide) { + l++; + continue; + }else{ + break; + } + } + if (levels[l]) levels[l].push(seg); + else levels[l] = [seg]; + }); + eventMatrix[i] = levels; + addDays(ws, 7); + addDays(we, 7); + i++; + } + _renderEvents(); + } + + + + + var eventElements = []; // [[event, element], ...] + + function _renderEvents() { + for (var i=0; i") + .append("" + + (seg.isStart ? "") + .append("" + + (seg.isStart ? "") + .append("" + + (seg.isStart ? ""); + buildEventText(element.find('td.c'), event, + typeof event.showTime == 'undefined' ? showTime : event.showTime); + if (options.eventRender) { + var res = options.eventRender(event, element); + if (typeof res != 'undefined') { + if (res === false) continue; + if (res !== true) element = $(res); + } + } + element + .css({ + position: 'absolute', + top: top, + left: left1, + width: left2 - left1, + 'z-index': 3 + }) + .appendTo(monthElement); + initEventElement(event, element); + var h = element.outerHeight({margin:true}); + if (h > maxh) maxh = h; + } + height += maxh; + top += maxh; + } + innerDiv.height(height); + } + } + + + + + function initEventElement(event, element) { + element.click(function(ev) { + if (!element.hasClass('ui-draggable-dragging')) { + if (options.eventClick) { + var res = options.eventClick.call(this, event, ev); + if (res === false) return false; + } + if (event.url) window.location.href = event.url; + } + }); + if (options.eventMouseover) + element.mouseover(function(ev) { + options.eventMouseover.call(this, event, ev); + }); + if (options.eventMouseout) + element.mouseout(function(ev) { + options.eventMouseout.call(this, event, ev); + }); + if (typeof event.draggable != 'undefined') { + if (event.draggable) + draggableEvent(event, element); + } + else if (options.draggable) { + draggableEvent(event, element); + } + eventElements.push([event, element]); + } + + + + + var dragStartTD, dragTD; + var dayOverlay; + + function draggableEvent(event, element) { + element.draggable({ + zIndex: 3, + delay: 50, + opacity: options.eventDragOpacity, + revertDuration: options.eventRevertDuration, + start: function(ev, ui) { + // hide other elements with same event + for (var i=0; i") + .appendTo(monthElement); + buildDayGrid(); + dragTD = dragStartTD = null; + eventDrag(this, ev, ui); + if (options.eventDragStart) + options.eventDragStart.call(this, event, ev, ui); + }, + drag: function(ev, ui) { + eventDrag(this, ev, ui); + }, + stop: function(ev, ui) { + if (!dragTD || dragTD == dragStartTD) { + // show all events + for (var i=0; i dayY0 + dayY[r+1]) r++; + while (c < cmax && x > dayX0 + dayX[c+1]) c++; + if (r < 0 || r >= rmax || c < 0 || c >= cmax) + return currTD = null; + else if (!currTD || r != currR || c != currC) { + currR = r; + currC = c; + currTD = tbody.find('tr:eq('+(r+1)+') td:eq('+c+')').get(0); + currTDX = dayX[c]; + currTDY = dayY[r]; + currTDW = dayX[c+1] - currTDX; + currTDH = dayY[r+1] - currTDY; + return currTD; + } + return currTD; + } + + function dayDate(node) { + var i, tds = tbody.get(0).getElementsByTagName('td'); + for (i=0; i") + .text((h%12 || 12) + (h<12 ? 'a' : 'p') + ' ')); + } + } + element.append($("") + .text(event.title)); + } + + + function cleanEvents(events) { + $.each(events, function(i, event) { + if (event.date) event.start = event.date; + event.start = cleanDate(event.start); + event.end = cleanDate(event.end); + if (!event.end) event.end = addDays(cloneDate(event.start), 1); + }); + return events; + } + + + + // date utils + + var monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December']; + var dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; + var dayAbbrevs = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; + + function addMonths(d, n, keepTime) { + d.setMonth(d.getMonth() + n); + if (keepTime) return d; + return clearTime(d); + } + + function addDays(d, n, keepTime) { + d.setDate(d.getDate() + n); + if (keepTime) return d; + return clearTime(d); + } + + function clearTime(d) { + d.setHours(0); + d.setMinutes(0); + d.setSeconds(0); + d.setMilliseconds(0); + return d; + } + + function cloneDate(d) { + return new Date(+d); + } + + function cleanDate(d) { + if (typeof d == 'string') + return $.parseISO8601(d, true) || Date.parse(d) || new Date(parseInt(d)); + if (typeof d == 'number') + return new Date(d * 1000); + return d; + } + + $.parseISO8601 = function(s, ignoreTimezone) { + // derived from http://delete.me.uk/2005/03/iso8601.html + var regexp = "([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})))?)?)?)?"; + var d = s.match(new RegExp(regexp)); + if (!d) return null; + var offset = 0; + var date = new Date(d[1], 0, 1); + if (d[3]) { date.setMonth(d[3] - 1); } + if (d[5]) { date.setDate(d[5]); } + if (d[7]) { date.setHours(d[7]); } + if (d[8]) { date.setMinutes(d[8]); } + if (d[10]) { date.setSeconds(d[10]); } + if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); } + if (!ignoreTimezone) { + if (d[14]) { + offset = (Number(d[16]) * 60) + Number(d[17]); + offset *= ((d[15] == '-') ? 1 : -1); + } + offset -= date.getTimezoneOffset(); + } + return new Date(Number(date) + (offset * 60 * 1000)); + }; + + $.ISO8601String = function(date) { + // derived from http://delete.me.uk/2005/03/iso8601.html + var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; } + return date.getUTCFullYear() + + "-" + zeropad(date.getUTCMonth() + 1) + + "-" + zeropad(date.getUTCDate()) + + "T" + zeropad(date.getUTCHours()) + + ":" + zeropad(date.getUTCMinutes()) + + ":" + zeropad(date.getUTCSeconds()) + + "Z"; + }; + + + + // general utils + + if (!$.isArray) { // not in jQuery pre 1.3 + $.isArray = function(obj) { + return toString.call(obj) === "[object Array]"; + }; + } + +})(jQuery); diff --git a/gcal.js b/gcal.js new file mode 100644 index 0000000..56c1161 --- /dev/null +++ b/gcal.js @@ -0,0 +1,55 @@ +(function($) { + + $.fn.gcalFullCalendar = function(options) { + + var feedURL; + if (options && typeof options.events == 'string') { + feedURL = options.events; + } + else return this; + + feedURL = feedURL.replace(/\/basic$/, '/full'); + + $.extend(options, { + + events: function(start, end, callback) { + $.getJSON(feedURL + "?alt=json-in-script&callback=?", + { + 'start-min': $.ISO8601String(start), + 'start-max': $.ISO8601String(end), + 'singleevents': true + }, + function(data) { + var events = []; + if (data.feed.entry) + $.each(data.feed.entry, function(i, entry) { + var url; + $.each(entry['link'], function(j, link) { + if (link.type == 'text/html') url = link.href; + }); + events.push({ + id: entry['gCal$uid']['value'], + url: url, + title: entry['title']['$t'], + start: $.parseISO8601(entry['gd$when'][0]['startTime'], true), + end: $.parseISO8601(entry['gd$when'][0]['endTime'], true), + location: entry['gd$where'][0]['valueString'], + description: entry['content']['$t'], + allDay: entry['gd$when'][0]['startTime'].indexOf('T') == -1, + draggable: false + }); + }); + callback(events); + }); + }, + + eventRender: function(event, element) { + if (!event.allDay) element.addClass('nobg'); + } + + }); + + return this.fullCalendar(options); + }; + +})(jQuery); diff --git a/jquery/jquery.js b/jquery/jquery.js new file mode 100644 index 0000000..9263574 --- /dev/null +++ b/jquery/jquery.js @@ -0,0 +1,4376 @@ +/*! + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){ + +var + // Will speed up references to window, and allows munging its name. + window = this, + // Will speed up references to undefined, and allows munging its name. + undefined, + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + // Map over the $ in case of overwrite + _$ = window.$, + + jQuery = window.jQuery = window.$ = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this[0] = selector; + this.length = 1; + this.context = selector; + return this; + } + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + var match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) + selector = jQuery.clean( [ match[1] ], context ); + + // HANDLE: $("#id") + else { + var elem = document.getElementById( match[3] ); + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem && elem.id != match[3] ) + return jQuery().find( selector ); + + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery( elem || [] ); + ret.context = document; + ret.selector = selector; + return ret; + } + + // HANDLE: $(expr, [context]) + // (which is just equivalent to: $(content).find(expr) + } else + return jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) + return jQuery( document ).ready( selector ); + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return this.setArray(jQuery.isArray( selector ) ? + selector : + jQuery.makeArray(selector)); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.3.2", + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num === undefined ? + + // Return a 'clean' array + Array.prototype.slice.call( this ) : + + // Return just the object + this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = jQuery( elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) + ret.selector = this.selector + (this.selector ? " " : "") + selector; + else if ( name ) + ret.selector = this.selector + "." + name + "(" + selector + ")"; + + // Return the newly-formed element set + return ret; + }, + + // Force the current matched set of elements to become + // the specified array of elements (destroying the stack in the process) + // You should use pushStack() in order to do this, but maintain the stack + setArray: function( elems ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + this.length = 0; + Array.prototype.push.apply( this, elems ); + + return this; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem && elem.jquery ? elem[0] : elem + , this ); + }, + + attr: function( name, value, type ) { + var options = name; + + // Look for the case where we're accessing a style value + if ( typeof name === "string" ) + if ( value === undefined ) + return this[0] && jQuery[ type || "attr" ]( this[0], name ); + + else { + options = {}; + options[ name ] = value; + } + + // Check to see if we're setting style values + return this.each(function(i){ + // Set all the styles + for ( name in options ) + jQuery.attr( + type ? + this.style : + this, + name, jQuery.prop( this, options[ name ], type, i, name ) + ); + }); + }, + + css: function( key, value ) { + // ignore negative width and height values + if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) + value = undefined; + return this.attr( key, value, "curCSS" ); + }, + + text: function( text ) { + if ( typeof text !== "object" && text != null ) + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + + var ret = ""; + + jQuery.each( text || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + ret += this.nodeType != 1 ? + this.nodeValue : + jQuery.fn.text( [ this ] ); + }); + }); + + return ret; + }, + + wrapAll: function( html ) { + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).clone(); + + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); + + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } + + return this; + }, + + wrapInner: function( html ) { + return this.each(function(){ + jQuery( this ).contents().wrapAll( html ); + }); + }, + + wrap: function( html ) { + return this.each(function(){ + jQuery( this ).wrapAll( html ); + }); + }, + + append: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.appendChild( elem ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery( [] ); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: [].push, + sort: [].sort, + splice: [].splice, + + find: function( selector ) { + if ( this.length === 1 ) { + var ret = this.pushStack( [], "find", selector ); + ret.length = 0; + jQuery.find( selector, this[0], ret ); + return ret; + } else { + return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + })), "find", selector ); + } + }, + + clone: function( events ) { + // Do the clone + var ret = this.map(function(){ + if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // attributes in IE that are actually only stored + // as properties will not be copied (such as the + // the name attribute on an input). + var html = this.outerHTML; + if ( !html ) { + var div = this.ownerDocument.createElement("div"); + div.appendChild( this.cloneNode(true) ); + html = div.innerHTML; + } + + return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; + } else + return this.cloneNode(true); + }); + + // Copy the events from the original to the clone + if ( events === true ) { + var orig = this.find("*").andSelf(), i = 0; + + ret.find("*").andSelf().each(function(){ + if ( this.nodeName !== orig[i].nodeName ) + return; + + var events = jQuery.data( orig[i], "events" ); + + for ( var type in events ) { + for ( var handler in events[ type ] ) { + jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + } + } + + i++; + }); + } + + // Return the cloned set + return ret; + }, + + filter: function( selector ) { + return this.pushStack( + jQuery.isFunction( selector ) && + jQuery.grep(this, function(elem, i){ + return selector.call( elem, i ); + }) || + + jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ + return elem.nodeType === 1; + }) ), "filter", selector ); + }, + + closest: function( selector ) { + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, + closer = 0; + + return this.map(function(){ + var cur = this; + while ( cur && cur.ownerDocument ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { + jQuery.data(cur, "closest", closer); + return cur; + } + cur = cur.parentNode; + closer++; + } + }); + }, + + not: function( selector ) { + if ( typeof selector === "string" ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); + }, + + add: function( selector ) { + return this.pushStack( jQuery.unique( jQuery.merge( + this.get(), + typeof selector === "string" ? + jQuery( selector ) : + jQuery.makeArray( selector ) + ))); + }, + + is: function( selector ) { + return !!selector && jQuery.multiFilter( selector, this ).length > 0; + }, + + hasClass: function( selector ) { + return !!selector && this.is( "." + selector ); + }, + + val: function( value ) { + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if( jQuery.nodeName( elem, 'option' ) ) + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type == "select-one"; + + // Nothing was selected + if ( index < 0 ) + return null; + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) + return value; + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(/\r/g, ""); + + } + + return undefined; + } + + if ( typeof value === "number" ) + value += ''; + + return this.each(function(){ + if ( this.nodeType != 1 ) + return; + + if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); + + else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(value); + + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); + }, + + html: function( value ) { + return value === undefined ? + (this[0] ? + this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : + null) : + this.empty().append( value ); + }, + + replaceWith: function( value ) { + return this.after( value ).remove(); + }, + + eq: function( i ) { + return this.slice( i, +i + 1 ); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ), + "slice", Array.prototype.slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function(elem, i){ + return callback.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + domManip: function( args, table, callback ) { + if ( this[0] ) { + var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), + scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), + first = fragment.firstChild; + + if ( first ) + for ( var i = 0, l = this.length; i < l; i++ ) + callback.call( root(this[i], first), this.length > 1 || i > 0 ? + fragment.cloneNode(true) : fragment ); + + if ( scripts ) + jQuery.each( scripts, evalScript ); + } + + return this; + + function root( elem, cur ) { + return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; + } + } +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { + if ( elem.src ) + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + + else + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + + if ( elem.parentNode ) + elem.parentNode.removeChild( elem ); +} + +function now(){ + return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) + target = {}; + + // extend jQuery itself if only one argument is passed + if ( length == i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + // Recurse if we're merging object values + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) + target[ name ] = jQuery.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + + // Don't bring in undefined values + else if ( copy !== undefined ) + target[ name ] = copy; + + } + + // Return the modified object + return target; +}; + +// exclude the following css properties to add px +var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, + // cache defaultView + defaultView = document.defaultView || {}, + toString = Object.prototype.toString; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) + window.jQuery = _jQuery; + + return jQuery; + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + // check if an element is in a (or is an) XML document + isXMLDoc: function( elem ) { + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); + }, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && /\S/.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + if ( jQuery.support.scriptEval ) + script.appendChild( document.createTextNode( data ) ); + else + script.text = data; + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, length = object.length; + + if ( args ) { + if ( length === undefined ) { + for ( name in object ) + if ( callback.apply( object[ name ], args ) === false ) + break; + } else + for ( ; i < length; ) + if ( callback.apply( object[ i++ ], args ) === false ) + break; + + // A special, fast, case for the most common use of each + } else { + if ( length === undefined ) { + for ( name in object ) + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + } + + return object; + }, + + prop: function( elem, value, type, i, name ) { + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, i ); + + // Handle passing in a number to a CSS property + return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, classNames ) { + jQuery.each((classNames || "").split(/\s+/), function(i, className){ + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) + elem.className += (elem.className ? " " : "") + className; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, classNames ) { + if (elem.nodeType == 1) + elem.className = classNames !== undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; + }, + + // internal only, use hasClass("class") + has: function( elem, className ) { + return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var old = {}; + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( var name in options ) + elem.style[ name ] = old[ name ]; + }, + + css: function( elem, name, force, extra ) { + if ( name == "width" || name == "height" ) { + var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + + function getWH() { + val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + + if ( extra === "border" ) + return; + + jQuery.each( which, function() { + if ( !extra ) + val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + if ( extra === "margin" ) + val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; + else + val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + } + + if ( elem.offsetWidth !== 0 ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, Math.round(val)); + } + + return jQuery.curCSS( elem, name, force ); + }, + + curCSS: function( elem, name, force ) { + var ret, style = elem.style; + + // We need to handle opacity special in IE + if ( name == "opacity" && !jQuery.support.opacity ) { + ret = jQuery.attr( style, "opacity" ); + + return ret == "" ? + "1" : + ret; + } + + // Make sure we're using the right name for getting the float value + if ( name.match( /float/i ) ) + name = styleFloat; + + if ( !force && style && style[ name ] ) + ret = style[ name ]; + + else if ( defaultView.getComputedStyle ) { + + // Only "float" is needed here + if ( name.match( /float/i ) ) + name = "float"; + + name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + + var computedStyle = defaultView.getComputedStyle( elem, null ); + + if ( computedStyle ) + ret = computedStyle.getPropertyValue( name ); + + // We should always get a number back from opacity + if ( name == "opacity" && ret == "" ) + ret = "1"; + + } else if ( elem.currentStyle ) { + var camelCase = name.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + + ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { + // Remember the original values + var left = style.left, rsLeft = elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + elem.runtimeStyle.left = elem.currentStyle.left; + style.left = ret || 0; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + elem.runtimeStyle.left = rsLeft; + } + } + + return ret; + }, + + clean: function( elems, context, fragment ) { + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { + var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); + if ( match ) + return [ context.createElement( match[1] ) ]; + } + + var ret = [], scripts = [], div = context.createElement("div"); + + jQuery.each(elems, function(i, elem){ + if ( typeof elem === "number" ) + elem += ''; + + if ( !elem ) + return; + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? + all : + front + ">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); + + var wrap = + // option or optgroup + !tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "
" + + "
" + + "
" + + "
" : '') + + "" + + (seg.isEnd ? "" : '') + "
" : '') + + "" + + (seg.isEnd ? "" : '') + "
" : '') + + "" + + (seg.isEnd ? "" : '') + "
", "
" ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and - + - + - + +

json_events.php needs to be running in the same directory.

diff --git a/full_calendar.css b/fullcalendar.css similarity index 99% rename from full_calendar.css rename to fullcalendar.css index c49f715..7b7083f 100644 --- a/full_calendar.css +++ b/fullcalendar.css @@ -22,7 +22,7 @@ -/* borders */ +/* table & borders */ .full-calendar-month { clear: both; diff --git a/full_calendar.js b/fullcalendar.js similarity index 96% rename from full_calendar.js rename to fullcalendar.js index 3a95bcc..8175529 100644 --- a/full_calendar.js +++ b/fullcalendar.js @@ -1,16 +1,18 @@ /* * FullCalendar - * Date: - * Revision: + * http://arshaw.com/fullcalendar/ * - * Examples and documentation at: http://arshaw.com/fullcalendar/ + * use fullcalendar.css for basic styling + * requires jQuery UI core and draggables ONLY if you plan to do drag & drop * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * + * Date: + * Revision: */ - + (function($) { $.fn.fullCalendar = function(options) { @@ -26,17 +28,14 @@ options = options || {}; var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime; - var startParam = options.startParam || 'start'; - var endParam = options.endParam || 'end'; var bo = options.buttons; this.each(function() { - - var date = options.year ? new Date(options.year, options.month || 0, 1) : new Date(); var start, end, today, numWeeks; - var events = $.isArray(options.events) ? cleanEvents(options.events) : null; + var events = typeof options.events != 'string' && !$.isFunction(options.events) ? + cleanEvents(options.events) : null; var ignoreResizes = false; function updateMonth() { @@ -65,7 +64,11 @@ } $.data(this, 'fullCalendar', { - today:today, prevMonth:prevMonth, nextMonth:nextMonth, gotoMonth:gotoMonth + today: today, + prevMonth: prevMonth, + nextMonth: nextMonth, + gotoMonth: gotoMonth, + refresh: updateMonth }); @@ -234,8 +237,8 @@ if (typeof options.events == 'string') { if (options.loading) options.loading(true); var jsonOptions = {}; - jsonOptions[startParam] = Math.round(start.getTime() / 1000); - jsonOptions[endParam] = Math.round(end.getTime() / 1000); + jsonOptions[options.startParam || 'start'] = Math.round(start.getTime() / 1000); + jsonOptions[options.endParam || 'end'] = Math.round(end.getTime() / 1000); $.getJSON(options.events, jsonOptions, function(data) { events = cleanEvents(data); renderEvents(events); @@ -251,7 +254,7 @@ if (options.loading) options.loading(false); }); } - else renderEvents(events); + else if (events) renderEvents(events); ignoreResizes = false; @@ -720,15 +723,5 @@ ":" + zeropad(date.getUTCSeconds()) + "Z"; }; - - - - // general utils - - if (!$.isArray) { // not in jQuery pre 1.3 - $.isArray = function(obj) { - return toString.call(obj) === "[object Array]"; - }; - } })(jQuery); diff --git a/jquery/jquery.js b/jquery/jquery.js index 9263574..b1ae21d 100644 --- a/jquery/jquery.js +++ b/jquery/jquery.js @@ -1,4 +1,4 @@ -/*! +/* * jQuery JavaScript Library v1.3.2 * http://jquery.com/ * @@ -9,4368 +9,11 @@ * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) * Revision: 6246 */ -(function(){ - -var - // Will speed up references to window, and allows munging its name. - window = this, - // Will speed up references to undefined, and allows munging its name. - undefined, - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - // Map over the $ in case of overwrite - _$ = window.$, - - jQuery = window.jQuery = window.$ = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); - }, - - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - // Make sure that a selection was provided - selector = selector || document; - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this[0] = selector; - this.length = 1; - this.context = selector; - return this; - } - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - var match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) - selector = jQuery.clean( [ match[1] ], context ); - - // HANDLE: $("#id") - else { - var elem = document.getElementById( match[3] ); - - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem && elem.id != match[3] ) - return jQuery().find( selector ); - - // Otherwise, we inject the element directly into the jQuery object - var ret = jQuery( elem || [] ); - ret.context = document; - ret.selector = selector; - return ret; - } - - // HANDLE: $(expr, [context]) - // (which is just equivalent to: $(content).find(expr) - } else - return jQuery( context ).find( selector ); - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) - return jQuery( document ).ready( selector ); - - // Make sure that old selector state is passed along - if ( selector.selector && selector.context ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return this.setArray(jQuery.isArray( selector ) ? - selector : - jQuery.makeArray(selector)); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.3.2", - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num === undefined ? - - // Return a 'clean' array - Array.prototype.slice.call( this ) : - - // Return just the object - this[ num ]; - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = jQuery( elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) - ret.selector = this.selector + (this.selector ? " " : "") + selector; - else if ( name ) - ret.selector = this.selector + "." + name + "(" + selector + ")"; - - // Return the newly-formed element set - return ret; - }, - - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - Array.prototype.push.apply( this, elems ); - - return this; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem && elem.jquery ? elem[0] : elem - , this ); - }, - - attr: function( name, value, type ) { - var options = name; - - // Look for the case where we're accessing a style value - if ( typeof name === "string" ) - if ( value === undefined ) - return this[0] && jQuery[ type || "attr" ]( this[0], name ); - - else { - options = {}; - options[ name ] = value; - } - - // Check to see if we're setting style values - return this.each(function(i){ - // Set all the styles - for ( name in options ) - jQuery.attr( - type ? - this.style : - this, - name, jQuery.prop( this, options[ name ], type, i, name ) - ); - }); - }, - - css: function( key, value ) { - // ignore negative width and height values - if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) - value = undefined; - return this.attr( key, value, "curCSS" ); - }, - - text: function( text ) { - if ( typeof text !== "object" && text != null ) - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); - - var ret = ""; - - jQuery.each( text || this, function(){ - jQuery.each( this.childNodes, function(){ - if ( this.nodeType != 8 ) - ret += this.nodeType != 1 ? - this.nodeValue : - jQuery.fn.text( [ this ] ); - }); - }); - - return ret; - }, - - wrapAll: function( html ) { - if ( this[0] ) { - // The elements to wrap the target around - var wrap = jQuery( html, this[0].ownerDocument ).clone(); - - if ( this[0].parentNode ) - wrap.insertBefore( this[0] ); - - wrap.map(function(){ - var elem = this; - - while ( elem.firstChild ) - elem = elem.firstChild; - - return elem; - }).append(this); - } - - return this; - }, - - wrapInner: function( html ) { - return this.each(function(){ - jQuery( this ).contents().wrapAll( html ); - }); - }, - - wrap: function( html ) { - return this.each(function(){ - jQuery( this ).wrapAll( html ); - }); - }, - - append: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.appendChild( elem ); - }); - }, - - prepend: function() { - return this.domManip(arguments, true, function(elem){ - if (this.nodeType == 1) - this.insertBefore( elem, this.firstChild ); - }); - }, - - before: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this ); - }); - }, - - after: function() { - return this.domManip(arguments, false, function(elem){ - this.parentNode.insertBefore( elem, this.nextSibling ); - }); - }, - - end: function() { - return this.prevObject || jQuery( [] ); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: [].push, - sort: [].sort, - splice: [].splice, - - find: function( selector ) { - if ( this.length === 1 ) { - var ret = this.pushStack( [], "find", selector ); - ret.length = 0; - jQuery.find( selector, this[0], ret ); - return ret; - } else { - return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ - return jQuery.find( selector, elem ); - })), "find", selector ); - } - }, - - clone: function( events ) { - // Do the clone - var ret = this.map(function(){ - if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { - // IE copies events bound via attachEvent when - // using cloneNode. Calling detachEvent on the - // clone will also remove the events from the orignal - // In order to get around this, we use innerHTML. - // Unfortunately, this means some modifications to - // attributes in IE that are actually only stored - // as properties will not be copied (such as the - // the name attribute on an input). - var html = this.outerHTML; - if ( !html ) { - var div = this.ownerDocument.createElement("div"); - div.appendChild( this.cloneNode(true) ); - html = div.innerHTML; - } - - return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; - } else - return this.cloneNode(true); - }); - - // Copy the events from the original to the clone - if ( events === true ) { - var orig = this.find("*").andSelf(), i = 0; - - ret.find("*").andSelf().each(function(){ - if ( this.nodeName !== orig[i].nodeName ) - return; - - var events = jQuery.data( orig[i], "events" ); - - for ( var type in events ) { - for ( var handler in events[ type ] ) { - jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); - } - } - - i++; - }); - } - - // Return the cloned set - return ret; - }, - - filter: function( selector ) { - return this.pushStack( - jQuery.isFunction( selector ) && - jQuery.grep(this, function(elem, i){ - return selector.call( elem, i ); - }) || - - jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ - return elem.nodeType === 1; - }) ), "filter", selector ); - }, - - closest: function( selector ) { - var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, - closer = 0; - - return this.map(function(){ - var cur = this; - while ( cur && cur.ownerDocument ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { - jQuery.data(cur, "closest", closer); - return cur; - } - cur = cur.parentNode; - closer++; - } - }); - }, - - not: function( selector ) { - if ( typeof selector === "string" ) - // test special case where just one selector is passed in - if ( isSimple.test( selector ) ) - return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); - else - selector = jQuery.multiFilter( selector, this ); - - var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; - return this.filter(function() { - return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; - }); - }, - - add: function( selector ) { - return this.pushStack( jQuery.unique( jQuery.merge( - this.get(), - typeof selector === "string" ? - jQuery( selector ) : - jQuery.makeArray( selector ) - ))); - }, - - is: function( selector ) { - return !!selector && jQuery.multiFilter( selector, this ).length > 0; - }, - - hasClass: function( selector ) { - return !!selector && this.is( "." + selector ); - }, - - val: function( value ) { - if ( value === undefined ) { - var elem = this[0]; - - if ( elem ) { - if( jQuery.nodeName( elem, 'option' ) ) - return (elem.attributes.value || {}).specified ? elem.value : elem.text; - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type == "select-one"; - - // Nothing was selected - if ( index < 0 ) - return null; - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) - return value; - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - } - - // Everything else, we just grab the value - return (elem.value || "").replace(/\r/g, ""); - - } - - return undefined; - } - - if ( typeof value === "number" ) - value += ''; - - return this.each(function(){ - if ( this.nodeType != 1 ) - return; - - if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) - this.checked = (jQuery.inArray(this.value, value) >= 0 || - jQuery.inArray(this.name, value) >= 0); - - else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(value); - - jQuery( "option", this ).each(function(){ - this.selected = (jQuery.inArray( this.value, values ) >= 0 || - jQuery.inArray( this.text, values ) >= 0); - }); - - if ( !values.length ) - this.selectedIndex = -1; - - } else - this.value = value; - }); - }, - - html: function( value ) { - return value === undefined ? - (this[0] ? - this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : - null) : - this.empty().append( value ); - }, - - replaceWith: function( value ) { - return this.after( value ).remove(); - }, - - eq: function( i ) { - return this.slice( i, +i + 1 ); - }, - - slice: function() { - return this.pushStack( Array.prototype.slice.apply( this, arguments ), - "slice", Array.prototype.slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function(elem, i){ - return callback.call( elem, i, elem ); - })); - }, - - andSelf: function() { - return this.add( this.prevObject ); - }, - - domManip: function( args, table, callback ) { - if ( this[0] ) { - var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), - scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), - first = fragment.firstChild; - - if ( first ) - for ( var i = 0, l = this.length; i < l; i++ ) - callback.call( root(this[i], first), this.length > 1 || i > 0 ? - fragment.cloneNode(true) : fragment ); - - if ( scripts ) - jQuery.each( scripts, evalScript ); - } - - return this; - - function root( elem, cur ) { - return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? - (elem.getElementsByTagName("tbody")[0] || - elem.appendChild(elem.ownerDocument.createElement("tbody"))) : - elem; - } - } -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -function evalScript( i, elem ) { - if ( elem.src ) - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - - else - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - - if ( elem.parentNode ) - elem.parentNode.removeChild( elem ); -} - -function now(){ - return +new Date; -} - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) - target = {}; - - // extend jQuery itself if only one argument is passed - if ( length == i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) - // Extend the base object - for ( var name in options ) { - var src = target[ name ], copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) - continue; - - // Recurse if we're merging object values - if ( deep && copy && typeof copy === "object" && !copy.nodeType ) - target[ name ] = jQuery.extend( deep, - // Never move original objects, clone them - src || ( copy.length != null ? [ ] : { } ) - , copy ); - - // Don't bring in undefined values - else if ( copy !== undefined ) - target[ name ] = copy; - - } - - // Return the modified object - return target; -}; - -// exclude the following css properties to add px -var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, - // cache defaultView - defaultView = document.defaultView || {}, - toString = Object.prototype.toString; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) - window.jQuery = _jQuery; - - return jQuery; - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - - // check if an element is in a (or is an) XML document - isXMLDoc: function( elem ) { - return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || - !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); - }, - - // Evalulates a script in a global context - globalEval: function( data ) { - if ( data && /\S/.test(data) ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - script.type = "text/javascript"; - if ( jQuery.support.scriptEval ) - script.appendChild( document.createTextNode( data ) ); - else - script.text = data; - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, length = object.length; - - if ( args ) { - if ( length === undefined ) { - for ( name in object ) - if ( callback.apply( object[ name ], args ) === false ) - break; - } else - for ( ; i < length; ) - if ( callback.apply( object[ i++ ], args ) === false ) - break; - - // A special, fast, case for the most common use of each - } else { - if ( length === undefined ) { - for ( name in object ) - if ( callback.call( object[ name ], name, object[ name ] ) === false ) - break; - } else - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} - } - - return object; - }, - - prop: function( elem, value, type, i, name ) { - // Handle executable functions - if ( jQuery.isFunction( value ) ) - value = value.call( elem, i ); - - // Handle passing in a number to a CSS property - return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? - value + "px" : - value; - }, - - className: { - // internal only, use addClass("class") - add: function( elem, classNames ) { - jQuery.each((classNames || "").split(/\s+/), function(i, className){ - if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) - elem.className += (elem.className ? " " : "") + className; - }); - }, - - // internal only, use removeClass("class") - remove: function( elem, classNames ) { - if (elem.nodeType == 1) - elem.className = classNames !== undefined ? - jQuery.grep(elem.className.split(/\s+/), function(className){ - return !jQuery.className.has( classNames, className ); - }).join(" ") : - ""; - }, - - // internal only, use hasClass("class") - has: function( elem, className ) { - return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; - } - }, - - // A method for quickly swapping in/out CSS properties to get correct calculations - swap: function( elem, options, callback ) { - var old = {}; - // Remember the old values, and insert the new ones - for ( var name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - callback.call( elem ); - - // Revert the old values - for ( var name in options ) - elem.style[ name ] = old[ name ]; - }, - - css: function( elem, name, force, extra ) { - if ( name == "width" || name == "height" ) { - var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; - - function getWH() { - val = name == "width" ? elem.offsetWidth : elem.offsetHeight; - - if ( extra === "border" ) - return; - - jQuery.each( which, function() { - if ( !extra ) - val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; - if ( extra === "margin" ) - val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; - else - val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; - }); - } - - if ( elem.offsetWidth !== 0 ) - getWH(); - else - jQuery.swap( elem, props, getWH ); - - return Math.max(0, Math.round(val)); - } - - return jQuery.curCSS( elem, name, force ); - }, - - curCSS: function( elem, name, force ) { - var ret, style = elem.style; - - // We need to handle opacity special in IE - if ( name == "opacity" && !jQuery.support.opacity ) { - ret = jQuery.attr( style, "opacity" ); - - return ret == "" ? - "1" : - ret; - } - - // Make sure we're using the right name for getting the float value - if ( name.match( /float/i ) ) - name = styleFloat; - - if ( !force && style && style[ name ] ) - ret = style[ name ]; - - else if ( defaultView.getComputedStyle ) { - - // Only "float" is needed here - if ( name.match( /float/i ) ) - name = "float"; - - name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); - - var computedStyle = defaultView.getComputedStyle( elem, null ); - - if ( computedStyle ) - ret = computedStyle.getPropertyValue( name ); - - // We should always get a number back from opacity - if ( name == "opacity" && ret == "" ) - ret = "1"; - - } else if ( elem.currentStyle ) { - var camelCase = name.replace(/\-(\w)/g, function(all, letter){ - return letter.toUpperCase(); - }); - - ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; - - // From the awesome hack by Dean Edwards - // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 - - // If we're not dealing with a regular pixel number - // but a number that has a weird ending, we need to convert it to pixels - if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { - // Remember the original values - var left = style.left, rsLeft = elem.runtimeStyle.left; - - // Put in the new values to get a computed value out - elem.runtimeStyle.left = elem.currentStyle.left; - style.left = ret || 0; - ret = style.pixelLeft + "px"; - - // Revert the changed values - style.left = left; - elem.runtimeStyle.left = rsLeft; - } - } - - return ret; - }, - - clean: function( elems, context, fragment ) { - context = context || document; - - // !context.createElement fails in IE with an error but returns typeof 'object' - if ( typeof context.createElement === "undefined" ) - context = context.ownerDocument || context[0] && context[0].ownerDocument || document; - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { - var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); - if ( match ) - return [ context.createElement( match[1] ) ]; - } - - var ret = [], scripts = [], div = context.createElement("div"); - - jQuery.each(elems, function(i, elem){ - if ( typeof elem === "number" ) - elem += ''; - - if ( !elem ) - return; - - // Convert html string into DOM nodes - if ( typeof elem === "string" ) { - // Fix "XHTML"-style tags in all browsers - elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ - return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? - all : - front + ">"; - }); - - // Trim whitespace, otherwise indexOf won't work as expected - var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); - - var wrap = - // option or optgroup - !tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && - [ 1, "", "
" ] || - - !tags.indexOf("", "" ] || - - // matched above - (!tags.indexOf("", "" ] || - - !tags.indexOf("", "" ] || - - // IE can't serialize and + + + + + + +
+ + diff --git a/legacy/examples/gcal.html b/legacy/examples/gcal.html new file mode 100644 index 0000000..e3669e5 --- /dev/null +++ b/legacy/examples/gcal.html @@ -0,0 +1,54 @@ + + + + + + + + + + + + +
+ + diff --git a/legacy/examples/json.html b/legacy/examples/json.html new file mode 100644 index 0000000..60c8a2c --- /dev/null +++ b/legacy/examples/json.html @@ -0,0 +1,56 @@ + + + + + + + + + + + + + +
+

json_events.php needs to be running in the same directory.

+ + diff --git a/legacy/examples/json_events.php b/legacy/examples/json_events.php new file mode 100644 index 0000000..bf39ba6 --- /dev/null +++ b/legacy/examples/json_events.php @@ -0,0 +1,25 @@ + 1, + 'title' => "Event1", + 'start' => "$year-$month-10", + 'url' => "http://yahoo.com/" + ), + + array( + 'id' => 2, + 'title' => "Event2", + 'start' => "$year-$month-20", + 'end' => "$year-$month-22", + 'url' => "http://yahoo.com/" + ) + + )); + +?> diff --git a/legacy/jquery/jquery.js b/legacy/jquery/jquery.js new file mode 100644 index 0000000..82b98e1 --- /dev/null +++ b/legacy/jquery/jquery.js @@ -0,0 +1,32 @@ +/* + * jQuery 1.2.6 - New Wave Javascript + * + * Copyright (c) 2008 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ + * $Rev: 5685 $ + */ +(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else +while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else +xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else +jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else +s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); \ No newline at end of file diff --git a/legacy/jquery/ui.core.js b/legacy/jquery/ui.core.js new file mode 100644 index 0000000..2bd55b5 --- /dev/null +++ b/legacy/jquery/ui.core.js @@ -0,0 +1,529 @@ +/* + * jQuery UI 1.6 + * + * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI + */ +;(function($) { + +var _remove = $.fn.remove, + isFF2 = $.browser.mozilla && (parseFloat($.browser.version) < 1.9); + +//Helper functions and ui object +$.ui = { + + version: "1.6", + + // $.ui.plugin is deprecated. Use the proxy pattern instead. + plugin: { + add: function(module, option, set) { + var proto = $.ui[module].prototype; + for(var i in set) { + proto.plugins[i] = proto.plugins[i] || []; + proto.plugins[i].push([option, set[i]]); + } + }, + call: function(instance, name, args) { + var set = instance.plugins[name]; + if(!set) { return; } + + for (var i = 0; i < set.length; i++) { + if (instance.options[set[i][0]]) { + set[i][1].apply(instance.element, args); + } + } + } + }, + + contains: function(a, b) { + var safari2 = $.browser.safari && $.browser.version < 522; + if (a.contains && !safari2) { + return a.contains(b); + } + if (a.compareDocumentPosition) + return !!(a.compareDocumentPosition(b) & 16); + while (b = b.parentNode) + if (b == a) return true; + return false; + }, + + cssCache: {}, + css: function(name) { + if ($.ui.cssCache[name]) { return $.ui.cssCache[name]; } + var tmp = $('
').addClass(name).css({position:'absolute', top:'-5000px', left:'-5000px', display:'block'}).appendTo('body'); + + //if (!$.browser.safari) + //tmp.appendTo('body'); + + //Opera and Safari set width and height to 0px instead of auto + //Safari returns rgba(0,0,0,0) when bgcolor is not set + $.ui.cssCache[name] = !!( + (!(/auto|default/).test(tmp.css('cursor')) || (/^[1-9]/).test(tmp.css('height')) || (/^[1-9]/).test(tmp.css('width')) || + !(/none/).test(tmp.css('backgroundImage')) || !(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))) + ); + try { $('body').get(0).removeChild(tmp.get(0)); } catch(e){} + return $.ui.cssCache[name]; + }, + + hasScroll: function(el, a) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ($(el).css('overflow') == 'hidden') { return false; } + + var scroll = (a && a == 'left') ? 'scrollLeft' : 'scrollTop', + has = false; + + if (el[scroll] > 0) { return true; } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[scroll] = 1; + has = (el[scroll] > 0); + el[scroll] = 0; + return has; + }, + + isOverAxis: function(x, reference, size) { + //Determines when x coordinate is over "b" element axis + return (x > reference) && (x < (reference + size)); + }, + + isOver: function(y, x, top, left, height, width) { + //Determines when x, y coordinates is over "b" element + return $.ui.isOverAxis(y, top, height) && $.ui.isOverAxis(x, left, width); + }, + + keyCode: { + BACKSPACE: 8, + CAPS_LOCK: 20, + COMMA: 188, + CONTROL: 17, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + INSERT: 45, + LEFT: 37, + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SHIFT: 16, + SPACE: 32, + TAB: 9, + UP: 38 + } + +}; + +// WAI-ARIA normalization +if (isFF2) { + var attr = $.attr, + removeAttr = $.fn.removeAttr, + ariaNS = "http://www.w3.org/2005/07/aaa", + ariaState = /^aria-/, + ariaRole = /^wairole:/; + + $.attr = function(elem, name, value) { + var set = value !== undefined; + + return (name == 'role' + ? (set + ? attr.call(this, elem, name, "wairole:" + value) + : (attr.apply(this, arguments) || "").replace(ariaRole, "")) + : (ariaState.test(name) + ? (set + ? elem.setAttributeNS(ariaNS, + name.replace(ariaState, "aaa:"), value) + : attr.call(this, elem, name.replace(ariaState, "aaa:"))) + : attr.apply(this, arguments))); + }; + + $.fn.removeAttr = function(name) { + return (ariaState.test(name) + ? this.each(function() { + this.removeAttributeNS(ariaNS, name.replace(ariaState, "")); + }) : removeAttr.call(this, name)); + }; +} + +//jQuery plugins +$.fn.extend({ + + remove: function() { + // Safari has a native remove event which actually removes DOM elements, + // so we have to use triggerHandler instead of trigger (#3037). + $("*", this).add(this).each(function() { + $(this).triggerHandler("remove"); + }); + return _remove.apply(this, arguments ); + }, + + enableSelection: function() { + return this + .attr('unselectable', 'off') + .css('MozUserSelect', '') + .unbind('selectstart.ui'); + }, + + disableSelection: function() { + return this + .attr('unselectable', 'on') + .css('MozUserSelect', 'none') + .bind('selectstart.ui', function() { return false; }); + }, + + scrollParent: function() { + + var scrollParent; + if(($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) { + scrollParent = this.parents().filter(function() { + return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); + }).eq(0); + } else { + scrollParent = this.parents().filter(function() { + return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); + }).eq(0); + } + + return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent; + + + } + +}); + + +//Additional selectors +$.extend($.expr[':'], { + + data: function(a, i, m) { + return $.data(a, m[3]); + }, + + // TODO: add support for object, area + tabbable: function(a, i, m) { + + var nodeName = a.nodeName.toLowerCase(); + function isVisible(element) { + return !($(element).is(':hidden') || $(element).parents(':hidden').length); + } + + return ( + // in tab order + a.tabIndex >= 0 && + + ( // filter node types that participate in the tab order + + // anchor tag + ('a' == nodeName && a.href) || + + // enabled form element + (/input|select|textarea|button/.test(nodeName) && + 'hidden' != a.type && !a.disabled) + ) && + + // visible on page + isVisible(a) + ); + + } + +}); + + +// $.widget is a factory to create jQuery plugins +// taking some boilerplate code out of the plugin code +function getter(namespace, plugin, method, args) { + function getMethods(type) { + var methods = $[namespace][plugin][type] || []; + return (typeof methods == 'string' ? methods.split(/,?\s+/) : methods); + } + + var methods = getMethods('getter'); + if (args.length == 1 && typeof args[0] == 'string') { + methods = methods.concat(getMethods('getterSetter')); + } + return ($.inArray(method, methods) != -1); +} + +$.widget = function(name, prototype) { + var namespace = name.split(".")[0]; + name = name.split(".")[1]; + + // create plugin method + $.fn[name] = function(options) { + var isMethodCall = (typeof options == 'string'), + args = Array.prototype.slice.call(arguments, 1); + + // prevent calls to internal methods + if (isMethodCall && options.substring(0, 1) == '_') { + return this; + } + + // handle getter methods + if (isMethodCall && getter(namespace, name, options, args)) { + var instance = $.data(this[0], name); + return (instance ? instance[options].apply(instance, args) + : undefined); + } + + // handle initialization and non-getter methods + return this.each(function() { + var instance = $.data(this, name); + + // constructor + (!instance && !isMethodCall && + $.data(this, name, new $[namespace][name](this, options))); + + // method call + (instance && isMethodCall && $.isFunction(instance[options]) && + instance[options].apply(instance, args)); + }); + }; + + // create widget constructor + $[namespace] = $[namespace] || {}; + $[namespace][name] = function(element, options) { + var self = this; + + this.widgetName = name; + this.widgetEventPrefix = $[namespace][name].eventPrefix || name; + this.widgetBaseClass = namespace + '-' + name; + + this.options = $.extend({}, + $.widget.defaults, + $[namespace][name].defaults, + $.metadata && $.metadata.get(element)[name], + options); + + this.element = $(element) + .bind('setData.' + name, function(event, key, value) { + return self._setData(key, value); + }) + .bind('getData.' + name, function(event, key) { + return self._getData(key); + }) + .bind('remove', function() { + return self.destroy(); + }); + + this._init(); + }; + + // add widget prototype + $[namespace][name].prototype = $.extend({}, $.widget.prototype, prototype); + + // TODO: merge getter and getterSetter properties from widget prototype + // and plugin prototype + $[namespace][name].getterSetter = 'option'; +}; + +$.widget.prototype = { + _init: function() {}, + destroy: function() { + this.element.removeData(this.widgetName); + }, + + option: function(key, value) { + var options = key, + self = this; + + if (typeof key == "string") { + if (value === undefined) { + return this._getData(key); + } + options = {}; + options[key] = value; + } + + $.each(options, function(key, value) { + self._setData(key, value); + }); + }, + _getData: function(key) { + return this.options[key]; + }, + _setData: function(key, value) { + this.options[key] = value; + + if (key == 'disabled') { + this.element[value ? 'addClass' : 'removeClass']( + this.widgetBaseClass + '-disabled'); + } + }, + + enable: function() { + this._setData('disabled', false); + }, + disable: function() { + this._setData('disabled', true); + }, + + _trigger: function(type, event, data) { + var eventName = (type == this.widgetEventPrefix + ? type : this.widgetEventPrefix + type); + event = event || $.event.fix({ type: eventName, target: this.element[0] }); + return this.element.triggerHandler(eventName, [event, data], this.options[type]); + } +}; + +$.widget.defaults = { + disabled: false +}; + + +/** Mouse Interaction Plugin **/ + +$.ui.mouse = { + _mouseInit: function() { + var self = this; + + this.element + .bind('mousedown.'+this.widgetName, function(event) { + return self._mouseDown(event); + }) + .bind('click.'+this.widgetName, function(event) { + if(self._preventClickEvent) { + self._preventClickEvent = false; + return false; + } + }); + + // Prevent text selection in IE + if ($.browser.msie) { + this._mouseUnselectable = this.element.attr('unselectable'); + this.element.attr('unselectable', 'on'); + } + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind('.'+this.widgetName); + + // Restore text selection in IE + ($.browser.msie + && this.element.attr('unselectable', this._mouseUnselectable)); + }, + + _mouseDown: function(event) { + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var self = this, + btnIsLeft = (event.which == 1), + elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + self.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return self._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return self._mouseUp(event); + }; + $(document) + .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + // preventDefault() is used to prevent the selection of text here - + // however, in Safari, this causes select boxes not to be selectable + // anymore, so this fix is needed + if(!$.browser.safari) event.preventDefault(); + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.browser.msie && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + this._preventClickEvent = true; + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(event) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(event) {}, + _mouseDrag: function(event) {}, + _mouseStop: function(event) {}, + _mouseCapture: function(event) { return true; } +}; + +$.ui.mouse.defaults = { + cancel: null, + distance: 1, + delay: 0 +}; + +})(jQuery); diff --git a/legacy/jquery/ui.draggable.js b/legacy/jquery/ui.draggable.js new file mode 100644 index 0000000..9d770fb --- /dev/null +++ b/legacy/jquery/ui.draggable.js @@ -0,0 +1,711 @@ +/* + * jQuery UI Draggable 1.6 + * + * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * ui.core.js + */ +(function($) { + +$.widget("ui.draggable", $.extend({}, $.ui.mouse, { + + _init: function() { + + if (this.options.helper == 'original' && !(/^(?:r|a|f)/).test(this.element.css("position"))) + this.element[0].style.position = 'relative'; + + (this.options.cssNamespace && this.element.addClass(this.options.cssNamespace+"-draggable")); + (this.options.disabled && this.element.addClass('ui-draggable-disabled')); + + this._mouseInit(); + + }, + + destroy: function() { + if(!this.element.data('draggable')) return; + this.element.removeData("draggable").unbind(".draggable").removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled'); + this._mouseDestroy(); + }, + + _mouseCapture: function(event) { + + var o = this.options; + + if (this.helper || o.disabled || $(event.target).is('.ui-resizable-handle')) + return false; + + //Quit if we're not on a valid handle + this.handle = this._getHandle(event); + if (!this.handle) + return false; + + return true; + + }, + + _mouseStart: function(event) { + + var o = this.options; + + //Create and append the visible helper + this.helper = this._createHelper(event); + + //Cache the helper size + this._cacheHelperProportions(); + + //If ddmanager is used for droppables, set the global draggable + if($.ui.ddmanager) + $.ui.ddmanager.current = this; + + /* + * - Position generation - + * This block generates everything position related - it's the core of draggables. + */ + + //Cache the margins of the original element + this._cacheMargins(); + + //Store the helper's css position + this.cssPosition = this.helper.css("position"); + this.scrollParent = this.helper.scrollParent(); + + //The element's absolute position on the page minus margins + this.offset = this.element.offset(); + this.offset = { + top: this.offset.top - this.margins.top, + left: this.offset.left - this.margins.left + }; + + $.extend(this.offset, { + click: { //Where the click happened, relative to the element + left: event.pageX - this.offset.left, + top: event.pageY - this.offset.top + }, + parent: this._getParentOffset(), + relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper + }); + + //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied + if(o.cursorAt) + this._adjustOffsetFromHelper(o.cursorAt); + + //Generate the original position + this.originalPosition = this._generatePosition(event); + + //Set a containment if given in the options + if(o.containment) + this._setContainment(); + + //Call plugins and callbacks + this._propagate("start", event); + + //Recache the helper size + this._cacheHelperProportions(); + + //Prepare the droppable offsets + if ($.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(this, event); + + this.helper.addClass("ui-draggable-dragging"); + this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position + return true; + }, + + _mouseDrag: function(event, noPropagation) { + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + //Call plugins and callbacks and use the resulting position if something is returned + if(!noPropagation) this.position = this._propagate("drag", event) || this.position; + + if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px'; + if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px'; + if($.ui.ddmanager) $.ui.ddmanager.drag(this, event); + + return false; + }, + + _mouseStop: function(event) { + + //If we are using droppables, inform the manager about the drop + var dropped = false; + if ($.ui.ddmanager && !this.options.dropBehaviour) + var dropped = $.ui.ddmanager.drop(this, event); + + if((this.options.revert == "invalid" && !dropped) || (this.options.revert == "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) { + var self = this; + $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() { + self._propagate("stop", event); + self._clear(); + }); + } else { + this._propagate("stop", event); + this._clear(); + } + + return false; + }, + + _getHandle: function(event) { + + var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false; + $(this.options.handle, this.element) + .find("*") + .andSelf() + .each(function() { + if(this == event.target) handle = true; + }); + + return handle; + + }, + + _createHelper: function(event) { + + var o = this.options; + var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper == 'clone' ? this.element.clone() : this.element); + + if(!helper.parents('body').length) + helper.appendTo((o.appendTo == 'parent' ? this.element[0].parentNode : o.appendTo)); + + if(helper[0] != this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) + helper.css("position", "absolute"); + + return helper; + + }, + + _adjustOffsetFromHelper: function(obj) { + if(obj.left != undefined) this.offset.click.left = obj.left + this.margins.left; + if(obj.right != undefined) this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left; + if(obj.top != undefined) this.offset.click.top = obj.top + this.margins.top; + if(obj.bottom != undefined) this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top; + }, + + _getParentOffset: function() { + + this.offsetParent = this.helper.offsetParent(); var po = this.offsetParent.offset(); //Get the offsetParent and cache its position + + if((this.offsetParent[0] == document.body && $.browser.mozilla) //Ugly FF3 fix + || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix + po = { top: 0, left: 0 }; + + return { + top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0), + left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0) + }; + + }, + + _getRelativeOffset: function() { + + if(this.cssPosition == "relative") { + var p = this.element.position(); + return { + top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(), + left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft() + }; + } else { + return { top: 0, left: 0 }; + } + + }, + + _cacheMargins: function() { + this.margins = { + left: (parseInt(this.element.css("marginLeft"),10) || 0), + top: (parseInt(this.element.css("marginTop"),10) || 0) + }; + }, + + _cacheHelperProportions: function() { + this.helperProportions = { + width: this.helper.outerWidth(), + height: this.helper.outerHeight() + }; + }, + + _setContainment: function() { + + var o = this.options; + if(o.containment == 'parent') o.containment = this.helper[0].parentNode; + if(o.containment == 'document' || o.containment == 'window') this.containment = [ + 0 - this.offset.relative.left - this.offset.parent.left, + 0 - this.offset.relative.top - this.offset.parent.top, + $(o.containment == 'document' ? document : window).width() - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - this.margins.left - (parseInt(this.element.css("marginRight"),10) || 0), + ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - this.margins.top - (parseInt(this.element.css("marginBottom"),10) || 0) + ]; + + if(!(/^(document|window|parent)$/).test(o.containment)) { + var ce = $(o.containment)[0]; + var co = $(o.containment).offset(); + var over = ($(ce).css("overflow") != 'hidden'); + + this.containment = [ + co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.relative.left - this.offset.parent.left - this.margins.left, + co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.relative.top - this.offset.parent.top - this.margins.top, + co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - this.offset.relative.left - this.offset.parent.left - this.helperProportions.width - this.margins.left, + co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - this.offset.relative.top - this.offset.parent.top - this.helperProportions.height - this.margins.top + ]; + } + + }, + + _convertPositionTo: function(d, pos) { + + if(!pos) pos = this.position; + var mod = d == "absolute" ? 1 : -1; + var scroll = this[(this.cssPosition == 'absolute' ? 'offset' : 'scroll')+'Parent'], scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + return { + top: ( + pos.top // the calculated relative position + + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border) + + ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod + + this.margins.top * mod //Add the margin (you don't want the margin counting in intersection methods) + ), + left: ( + pos.left // the calculated relative position + + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent + + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border) + + ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : ( scrollIsRootNode ? 0 : scroll.scrollLeft() ) ) * mod + + this.margins.left * mod //Add the margin (you don't want the margin counting in intersection methods) + ) + }; + }, + + _generatePosition: function(event) { + + var o = this.options, scroll = this[(this.cssPosition == 'absolute' ? 'offset' : 'scroll')+'Parent'], scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName); + + var position = { + top: ( + event.pageY // The absolute mouse position + - this.offset.click.top // Click offset (relative to the element) + - this.offset.relative.top // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.top // The offsetParent's offset without borders (offset + border) + + ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) + ), + left: ( + event.pageX // The absolute mouse position + - this.offset.click.left // Click offset (relative to the element) + - this.offset.relative.left // Only for relative positioned nodes: Relative offset from element to offset parent + - this.offset.parent.left // The offsetParent's offset without borders (offset + border) + + ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) + ) + }; + + if(!this.originalPosition) return position; //If we are not dragging yet, we won't check for options + + /* + * - Position constraining - + * Constrain the position to a mix of grid, containment. + */ + if(this.containment) { + if(position.left < this.containment[0]) position.left = this.containment[0]; + if(position.top < this.containment[1]) position.top = this.containment[1]; + if(position.left > this.containment[2]) position.left = this.containment[2]; + if(position.top > this.containment[3]) position.top = this.containment[3]; + } + + if(o.grid) { + var top = this.originalPosition.top + Math.round((position.top - this.originalPosition.top) / o.grid[1]) * o.grid[1]; + position.top = this.containment ? (!(top < this.containment[1] || top > this.containment[3]) ? top : (!(top < this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top; + + var left = this.originalPosition.left + Math.round((position.left - this.originalPosition.left) / o.grid[0]) * o.grid[0]; + position.left = this.containment ? (!(left < this.containment[0] || left > this.containment[2]) ? left : (!(left < this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left; + } + + return position; + }, + + _clear: function() { + this.helper.removeClass("ui-draggable-dragging"); + if(this.helper[0] != this.element[0] && !this.cancelHelperRemoval) this.helper.remove(); + //if($.ui.ddmanager) $.ui.ddmanager.current = null; + this.helper = null; + this.cancelHelperRemoval = false; + }, + + // From now on bulk stuff - mainly helpers + + _propagate: function(n, event) { + $.ui.plugin.call(this, n, [event, this._uiHash()]); + if(n == "drag") this.positionAbs = this._convertPositionTo("absolute"); //The absolute position has to be recalculated after plugins + return this.element.triggerHandler(n == "drag" ? n : "drag"+n, [event, this._uiHash()], this.options[n]); + }, + + plugins: {}, + + _uiHash: function(event) { + return { + helper: this.helper, + position: this.position, + absolutePosition: this.positionAbs, + options: this.options + }; + } + +})); + +$.extend($.ui.draggable, { + version: "1.6", + defaults: { + appendTo: "parent", + axis: false, + cancel: ":input", + connectToSortable: false, + containment: false, + cssNamespace: "ui", + cursor: "default", + cursorAt: null, + delay: 0, + distance: 1, + grid: false, + handle: false, + helper: "original", + iframeFix: false, + opacity: 1, + refreshPositions: false, + revert: false, + revertDuration: 500, + scope: "default", + scroll: true, + scrollSensitivity: 20, + scrollSpeed: 20, + snap: false, + snapMode: "both", + snapTolerance: 20, + stack: false, + zIndex: null + } +}); + +$.ui.plugin.add("draggable", "connectToSortable", { + start: function(event, ui) { + + var inst = $(this).data("draggable"); + inst.sortables = []; + $(ui.options.connectToSortable).each(function() { + // 'this' points to a string, and should therefore resolved as query, but instead, if the string is assigned to a variable, it loops through the strings properties, + // so we have to append '' to make it anonymous again + $(this+'').each(function() { + if($.data(this, 'sortable')) { + var sortable = $.data(this, 'sortable'); + inst.sortables.push({ + instance: sortable, + shouldRevert: sortable.options.revert + }); + sortable._refreshItems(); //Do a one-time refresh at start to refresh the containerCache + sortable._propagate("activate", event, inst); + } + }); + }); + + }, + stop: function(event, ui) { + + //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper + var inst = $(this).data("draggable"); + + $.each(inst.sortables, function() { + if(this.instance.isOver) { + this.instance.isOver = 0; + inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance + this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work) + if(this.shouldRevert) this.instance.options.revert = true; //revert here + this.instance._mouseStop(event); + + //Also propagate receive event, since the sortable is actually receiving a element + this.instance.element.triggerHandler("sortreceive", [event, $.extend(this.instance._ui(), { sender: inst.element })], this.instance.options["receive"]); + + this.instance.options.helper = this.instance.options._helper; + + if(inst.options.helper == 'original') { + this.instance.currentItem.css({ top: 'auto', left: 'auto' }); + } + + } else { + this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance + this.instance._propagate("deactivate", event, inst); + } + + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("draggable"), self = this; + + var checkPos = function(o) { + var dyClick = this.offset.click.top, dxClick = this.offset.click.left; + var helperTop = this.positionAbs.top, helperLeft = this.positionAbs.left; + var itemHeight = o.height, itemWidth = o.width; + var itemTop = o.top, itemLeft = o.left; + + return $.ui.isOver(helperTop + dyClick, helperLeft + dxClick, itemTop, itemLeft, itemHeight, itemWidth); + }; + + $.each(inst.sortables, function(i) { + + if(checkPos.call(inst, this.instance.containerCache)) { + + //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once + if(!this.instance.isOver) { + this.instance.isOver = 1; + //Now we fake the start of dragging for the sortable instance, + //by cloning the list group item, appending it to the sortable and using it as inst.currentItem + //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one) + this.instance.currentItem = $(self).clone().appendTo(this.instance.element).data("sortable-item", true); + this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it + this.instance.options.helper = function() { return ui.helper[0]; }; + + event.target = this.instance.currentItem[0]; + this.instance._mouseCapture(event, true); + this.instance._mouseStart(event, true, true); + + //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes + this.instance.offset.click.top = inst.offset.click.top; + this.instance.offset.click.left = inst.offset.click.left; + this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left; + this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top; + + inst._propagate("toSortable", event); + + } + + //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable + if(this.instance.currentItem) this.instance._mouseDrag(event); + + } else { + + //If it doesn't intersect with the sortable, and it intersected before, + //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval + if(this.instance.isOver) { + this.instance.isOver = 0; + this.instance.cancelHelperRemoval = true; + this.instance.options.revert = false; //No revert here + this.instance._mouseStop(event, true); + this.instance.options.helper = this.instance.options._helper; + + //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size + this.instance.currentItem.remove(); + if(this.instance.placeholder) this.instance.placeholder.remove(); + + inst._propagate("fromSortable", event); + } + + }; + + }); + + } +}); + +$.ui.plugin.add("draggable", "cursor", { + start: function(event, ui) { + var t = $('body'); + if (t.css("cursor")) ui.options._cursor = t.css("cursor"); + t.css("cursor", ui.options.cursor); + }, + stop: function(event, ui) { + if (ui.options._cursor) $('body').css("cursor", ui.options._cursor); + } +}); + +$.ui.plugin.add("draggable", "iframeFix", { + start: function(event, ui) { + $(ui.options.iframeFix === true ? "iframe" : ui.options.iframeFix).each(function() { + $('
') + .css({ + width: this.offsetWidth+"px", height: this.offsetHeight+"px", + position: "absolute", opacity: "0.001", zIndex: 1000 + }) + .css($(this).offset()) + .appendTo("body"); + }); + }, + stop: function(event, ui) { + $("div.ui-draggable-iframeFix").each(function() { this.parentNode.removeChild(this); }); //Remove frame helpers + } +}); + +$.ui.plugin.add("draggable", "opacity", { + start: function(event, ui) { + var t = $(ui.helper); + if(t.css("opacity")) ui.options._opacity = t.css("opacity"); + t.css('opacity', ui.options.opacity); + }, + stop: function(event, ui) { + if(ui.options._opacity) $(ui.helper).css('opacity', ui.options._opacity); + } +}); + +$.ui.plugin.add("draggable", "scroll", { + start: function(event, ui) { + var o = ui.options; + var i = $(this).data("draggable"); + + if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') i.overflowOffset = i.scrollParent.offset(); + + }, + drag: function(event, ui) { + + var o = ui.options, scrolled = false; + var i = $(this).data("draggable"); + + if(i.scrollParent[0] != document && i.scrollParent[0].tagName != 'HTML') { + + if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed; + else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) + i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed; + + if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed; + else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) + i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed; + + } else { + + if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed); + else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) + scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed); + + if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed); + else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) + scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed); + + } + + if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) + $.ui.ddmanager.prepareOffsets(i, event); + + + + // This is a special case where we need to modify a offset calculated on start, since the following happened: + // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent + // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that + // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag + if(scrolled !== false && i.cssPosition == 'absolute' && i.scrollParent[0] != document && $.ui.contains(i.scrollParent[0], i.offsetParent[0])) { + i.offset.parent = i._getParentOffset(); + + } + + // This is another very weird special case that only happens for relative elements: + // 1. If the css position is relative + // 2. and the scroll parent is the document or similar to the offset parent + // we have to refresh the relative offset during the scroll so there are no jumps + if(scrolled !== false && i.cssPosition == 'relative' && !(i.scrollParent[0] != document && i.scrollParent[0] != i.offsetParent[0])) { + i.offset.relative = i._getRelativeOffset(); + } + + + } +}); + +$.ui.plugin.add("draggable", "snap", { + start: function(event, ui) { + + var inst = $(this).data("draggable"); + inst.snapElements = []; + + $(ui.options.snap.constructor != String ? ( ui.options.snap.items || ':data(draggable)' ) : ui.options.snap).each(function() { + var $t = $(this); var $o = $t.offset(); + if(this != inst.element[0]) inst.snapElements.push({ + item: this, + width: $t.outerWidth(), height: $t.outerHeight(), + top: $o.top, left: $o.left + }); + }); + + }, + drag: function(event, ui) { + + var inst = $(this).data("draggable"); + var d = ui.options.snapTolerance; + + var x1 = ui.absolutePosition.left, x2 = x1 + inst.helperProportions.width, + y1 = ui.absolutePosition.top, y2 = y1 + inst.helperProportions.height; + + for (var i = inst.snapElements.length - 1; i >= 0; i--){ + + var l = inst.snapElements[i].left, r = l + inst.snapElements[i].width, + t = inst.snapElements[i].top, b = t + inst.snapElements[i].height; + + //Yes, I know, this is insane ;) + if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) { + if(inst.snapElements[i].snapping) (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + inst.snapElements[i].snapping = false; + continue; + } + + if(ui.options.snapMode != 'inner') { + var ts = Math.abs(t - y2) <= d; + var bs = Math.abs(b - y1) <= d; + var ls = Math.abs(l - x2) <= d; + var rs = Math.abs(r - x1) <= d; + if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top; + if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top; + if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left; + if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left; + } + + var first = (ts || bs || ls || rs); + + if(ui.options.snapMode != 'outer') { + var ts = Math.abs(t - y1) <= d; + var bs = Math.abs(b - y2) <= d; + var ls = Math.abs(l - x1) <= d; + var rs = Math.abs(r - x2) <= d; + if(ts) ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top; + if(bs) ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top; + if(ls) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left; + if(rs) ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left; + } + + if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) + (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item }))); + inst.snapElements[i].snapping = (ts || bs || ls || rs || first); + + }; + + } +}); + +$.ui.plugin.add("draggable", "stack", { + start: function(event, ui) { + var group = $.makeArray($(ui.options.stack.group)).sort(function(a,b) { + return (parseInt($(a).css("zIndex"),10) || ui.options.stack.min) - (parseInt($(b).css("zIndex"),10) || ui.options.stack.min); + }); + + $(group).each(function(i) { + this.style.zIndex = ui.options.stack.min + i; + }); + + this[0].style.zIndex = ui.options.stack.min + group.length; + } +}); + +$.ui.plugin.add("draggable", "zIndex", { + start: function(event, ui) { + var t = $(ui.helper); + if(t.css("zIndex")) ui.options._zIndex = t.css("zIndex"); + t.css('zIndex', ui.options.zIndex); + }, + stop: function(event, ui) { + if(ui.options._zIndex) $(ui.helper).css('zIndex', ui.options._zIndex); + } +}); + +})(jQuery); From f7e0ca7d73bf92564624db6345d422d0a7abded2 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Sun, 26 Apr 2009 21:41:15 +0000 Subject: [PATCH 006/185] --- fullcalendar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fullcalendar.js b/fullcalendar.js index 8175529..918dd70 100644 --- a/fullcalendar.js +++ b/fullcalendar.js @@ -5,6 +5,7 @@ * use fullcalendar.css for basic styling * requires jQuery UI core and draggables ONLY if you plan to do drag & drop * + * Copyright (c) 2009 Adam Shaw * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html From d8b727a3c739b29f9f1036a677fa5894ea1a6cc5 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Mon, 27 Apr 2009 01:16:11 +0000 Subject: [PATCH 007/185] --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 49d5957..d3827e7 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.1 +1.0 From 1da7dc92e0641dcbba332d98ec49a40113ca0cb4 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Mon, 27 Apr 2009 03:46:02 +0000 Subject: [PATCH 008/185] added gcal doc comments --- gcal.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/gcal.js b/gcal.js index 56c1161..4b676b5 100644 --- a/gcal.js +++ b/gcal.js @@ -1,3 +1,24 @@ +/* + * gcalFullCalendar extension for fullCalendar + * http://arshaw.com/fullcalendar/ + * + * Same usage/options as fullCalendar. + * However, enter your Google Calendar's public feed URL in the 'events' option. + * Here is how to find it in the Google Calendar interface: + * + * -> click the arrow next to your calendar's name + * -> click "Share this calendar" + * -> check "Make this calendar public" and then Save + * -> click the arrow again, then click "Calendar settings" + * -> in the "Calendar Address" section, click the XML rectangle + * -> the URL is displayed + * + * Copyright (c) 2009 Adam Shaw + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + (function($) { $.fn.gcalFullCalendar = function(options) { From 943ec262d2d351b505bcd03626ac9075054c321f Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Tue, 5 May 2009 14:42:35 +0000 Subject: [PATCH 009/185] fixed IE JSON caching issue --- fullcalendar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fullcalendar.js b/fullcalendar.js index 918dd70..e9c59e5 100644 --- a/fullcalendar.js +++ b/fullcalendar.js @@ -240,6 +240,7 @@ var jsonOptions = {}; jsonOptions[options.startParam || 'start'] = Math.round(start.getTime() / 1000); jsonOptions[options.endParam || 'end'] = Math.round(end.getTime() / 1000); + jsonOptions['_t'] = (new Date()).getTime(); $.getJSON(options.events, jsonOptions, function(data) { events = cleanEvents(data); renderEvents(events); From 4e419032f0797595fc304eff9b20ffb16c044421 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Fri, 8 May 2009 19:34:50 +0000 Subject: [PATCH 010/185] localization: start-of-week and right-to-left --- examples/basic.html | 11 ++- fullcalendar.css | 42 +++++++-- fullcalendar.js | 219 ++++++++++++++++++++++++++------------------ 3 files changed, 172 insertions(+), 100 deletions(-) diff --git a/examples/basic.html b/examples/basic.html index 6038de2..c83ccb8 100644 --- a/examples/basic.html +++ b/examples/basic.html @@ -35,7 +35,7 @@ { id: 1, title: "Long Event", - start: new Date(y, m, 6), + start: new Date(y, m, 6, 14, 0), end: new Date(y, m, 11) }, { @@ -60,7 +60,14 @@ end: new Date(y, m, 29), url: "http://facebook.com/" } - ] + ], + weekStart: 1, + rightToLeft: true, + fixedWeeks: false, + title: true, + eventDrop: function(event, delta) { + //alert(delta); + } }); }); diff --git a/fullcalendar.css b/fullcalendar.css index 7b7083f..e97f71d 100644 --- a/fullcalendar.css +++ b/fullcalendar.css @@ -2,22 +2,40 @@ /* top area w/ month title and buttons */ .full-calendar-header { - text-align: left; + } + +.full-calendar-title { + float: left; + margin-top: 0; + } + +.r2l .full-calendar-title { + float: right; } .full-calendar-buttons { float: right; + margin-bottom: 1em; + } + +.r2l .full-calendar-buttons { + float: left; } .full-calendar-buttons input { vertical-align: middle; font-size: 1.0em; + margin-left: 5px; + } + +.r2l .full-calendar-buttons input { + float: right; + margin: 0 5px 0 0; } .full-calendar-prev, .full-calendar-next { width: 40px; - margin-left: 5px; } @@ -31,6 +49,7 @@ reason for our long-winded border css. borders now look consistent across doctypes. */ border: 1px solid #ccc; /* border color & style */ + text-align: left; } .full-calendar-month table { @@ -38,7 +57,7 @@ border-spacing: 0; } -.full-calendar-month th.day-heading, +.full-calendar-month th, .full-calendar-month td.day { padding: 0; vertical-align: top; @@ -47,12 +66,13 @@ border-width: 1px 0 0 1px; } -.full-calendar-month tr.day-headings th { +.full-calendar-month th { border-top: 0; + text-align: center; } -.full-calendar-month th.sun, -.full-calendar-month td.sun { +.full-calendar-month th.first, /* left edge? */ +.full-calendar-month td.first { border-left: 0; } @@ -66,7 +86,11 @@ .full-calendar-month .day-number { text-align: right; - padding-right: 2px; + padding: 0 2px; + } + +.r2l .full-calendar-month .day-number { + text-align: left; } .full-calendar-month .other-month .day-number { @@ -93,6 +117,10 @@ text-align: left; } +.r2l .full-calendar-month .event { + text-align: right; + } + .full-calendar-month .ui-draggable-dragging td { cursor: move; } diff --git a/fullcalendar.js b/fullcalendar.js index e9c59e5..57eb01e 100644 --- a/fullcalendar.js +++ b/fullcalendar.js @@ -29,7 +29,19 @@ options = options || {}; var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime; - var bo = options.buttons; + var bo = typeof options.buttons == 'undefined' ? true : options.buttons; + var weekStart = (options.weekStart || 0) % 7; + + var r2l = options.rightToLeft; + var dis, dit; // day index sign / translate + if (r2l) { + dis = -1; + dit = 6; + this.addClass('r2l'); + }else{ + dis = 1; + dit = 0; + } this.each(function() { @@ -80,40 +92,31 @@ var titleElement, todayButton, monthElement; var header = $("
").appendTo(this); - if (bo != false) { - var buttons = $("
").appendTo(header); - todayButton = - $("") - .click(today); - var prevButton = - $("") - .click(prevMonth); - var nextButton = - $("") - .click(nextMonth); - if (typeof bo == 'object') { - if (bo.today != false) { - if (typeof bo.today == 'string') todayButton.val(bo.today); - buttons.append(todayButton); - } - if (bo.prev != false) { - if (typeof bo.prev == 'string') prevButton.val(bo.prev); - buttons.append(prevButton); - } - if (bo.next != false) { - if (typeof bo.next == 'string') nextButton.val(bo.next); - buttons.append(nextButton); - } - }else{ - buttons - .append(todayButton) - .append(prevButton) - .append(nextButton); - } - } - if (options.title !== false) titleElement = $("

").appendTo(header); + + if (bo) { + var buttons = $("
").appendTo(header); + var prevButton, nextButton; + if (bo == true || bo.today != false) { + todayButton = $("") + .appendTo(buttons) + .click(today); + if (typeof bo.today == 'string') todayButton.val(bo.today); + } + if (bo == true || bo.prev != false) { + prevButton = $("") + .appendTo(buttons) + .click(prevMonth); + if (typeof bo.prev == 'string') prevButton.val(bo.prev); + } + if (bo == true || bo.next != false) { + nextButton = $("") + .appendTo(buttons) + .click(nextMonth); + if (typeof bo.next == 'string') nextButton.val(bo.next); + } + } monthElement = $("
").appendTo(this); @@ -122,7 +125,7 @@ - var tbody, glass, monthTitle; + var thead, tbody, glass, monthTitle; function render() { @@ -136,10 +139,10 @@ clearTime(date); start = cloneDate(date); - addDays(start, -start.getDay()); + addDays(start, -start.getDay() + weekStart); end = cloneDate(date); addMonths(end, 1); - addDays(end, (7 - end.getDay()) % 7); + addDays(end, (7 - end.getDay() + weekStart) % 7); numWeeks = Math.round((end.getTime() - start.getTime()) / 604800000); if (options.fixedWeeks != false) { addDays(end, (6 - numWeeks) * 7); @@ -156,32 +159,40 @@ } if (!tbody) { - - tbody = ""; - for (var i=0; i<7; i++) { - tbody += - "" + - (options.abbrevDayHeadings!=false ? dayAbbrevs[i] : dayNames[i]) + + + var table = $("").appendTo(monthElement); + + thead = ""; + for (var i=0; i<7; i++) { + var j = (i * dis + dit + weekStart) % 7; + thead += + ""; } + thead = $(thead + "").appendTo(table); + + tbody = ""; var d = cloneDate(start); for (var i=0; i"; + var tds = ""; for (var j=0; j<7; j++) { - tbody += - ""; + if (r2l) tds = s + tds; + else tds += s; addDays(d, 1); } - tbody += ""; + tbody += tds + ""; } - tbody += ""; - tbody = $(tbody) - .appendTo($("
" + + (options.abbrevDayHeadings!=false ? dayAbbrevs[j] : dayNames[j]) + "
" + d.getDate() + "
" + "
") - .appendTo(monthElement)); + tbody = $(tbody + "").appendTo(table); glass = $("
") .appendTo(monthElement) @@ -195,9 +206,9 @@ }else{ - var diff = numWeeks - (tbody.find('tr').length - 1); + var diff = numWeeks - tbody.find('tr').length; if (diff < 0) { - tbody.find('tr:gt(' + numWeeks + ')').remove(); + tbody.find('tr:gt(' + (numWeeks-1) + ')').remove(); } else if (diff > 0) { var trs = ""; @@ -205,7 +216,9 @@ trs += "
"; for (var j=0; j<7; j++) { trs += - ""; @@ -216,19 +229,22 @@ } var d = cloneDate(start); - tbody.find('td').each(function() { - if (d.getMonth() == month) { - $(this).removeClass('other-month'); - }else{ - $(this).addClass('other-month'); + tbody.find('tr').each(function() { + for (var i=0; i<7; i++) { + var td = this.childNodes[i * dis + dit]; + if (d.getMonth() == month) { + $(td).removeClass('other-month'); + }else{ + $(td).addClass('other-month'); + } + if (d.getTime() == today.getTime()) { + $(td).addClass('today'); + }else{ + $(td).removeClass('today'); + } + $(td.childNodes[0]).text(d.getDate()); + addDays(d, 1); } - if (d.getTime() == today.getTime()) { - $(this).addClass('today'); - }else{ - $(this).removeClass('today'); - } - $(this.childNodes[0]).text(d.getDate()); - addDays(d, 1); }); } @@ -240,7 +256,7 @@ var jsonOptions = {}; jsonOptions[options.startParam || 'start'] = Math.round(start.getTime() / 1000); jsonOptions[options.endParam || 'end'] = Math.round(end.getTime() / 1000); - jsonOptions['_t'] = (new Date()).getTime(); + jsonOptions[options.cacheParam || '_t'] = (new Date()).getTime(); $.getJSON(options.events, jsonOptions, function(data) { events = cleanEvents(data); renderEvents(events); @@ -345,7 +361,7 @@ function _renderEvents() { for (var i=0; i") .append("" + - (seg.isStart ? "") + (roundE ? "") .append("" + - (seg.isStart ? "") + (roundE ? "") .append("" + - (seg.isStart ? ""); + (roundE ? ""); buildEventText(element.find('td.c'), event, - typeof event.showTime == 'undefined' ? showTime : event.showTime); + typeof event.showTime == 'undefined' ? showTime : event.showTime, r2l); if (options.eventRender) { var res = options.eventRender(event, element); if (typeof res != 'undefined') { @@ -529,7 +559,7 @@ dayX0 = o.left; dayY0 = o.top; dayY = []; - tbody.find('tr:gt(0)').each(function() { + tbody.find('tr').each(function() { tr = $(this); dayY.push(tr.position().top); }); @@ -553,7 +583,7 @@ else if (!currTD || r != currR || c != currC) { currR = r; currC = c; - currTD = tbody.find('tr:eq('+(r+1)+') td:eq('+c+')').get(0); + currTD = tbody.find('tr:eq('+r+') td:eq('+c+')').get(0); currTDX = dayX[c]; currTDY = dayY[r]; currTDW = dayX[c+1] - currTDX; @@ -573,10 +603,14 @@ } function dayDelta(node1, node2) { - var i1, i2, tds = tbody.get(0).getElementsByTagName('td'); - for (var i=0; i") - .text((h%12 || 12) + (h<12 ? 'a' : 'p') + ' ')); + var timeText = (h%12 || 12) + (h<12 ? 'a' : 'p'); + if (r2l) timeText = ' ' + timeText; + else timeText += ' '; + element.append($("").text(timeText)); } } - element.append($("") - .text(event.title)); + var et = $("").text(event.title) + if (r2l) element.prepend(et); + else element.append(et); } From 0332bf1b72536fe12951259255eeada8346176ca Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Sun, 10 May 2009 19:48:37 +0000 Subject: [PATCH 011/185] ie6/opera9.25 bugfixes, added options, more --- examples/basic.html | 33 +++++++-- fullcalendar.css | 86 +++++++++++----------- fullcalendar.js | 171 ++++++++++++++++++++++++++++++++------------ 3 files changed, 194 insertions(+), 96 deletions(-) diff --git a/examples/basic.html b/examples/basic.html index c83ccb8..630c761 100644 --- a/examples/basic.html +++ b/examples/basic.html @@ -11,7 +11,7 @@ } #calendar { - width: 900px; + width: 75%; margin: 0 auto; } @@ -48,25 +48,44 @@ title: "Repeating Event", start: new Date(y, m, 9) }, - { + /*{ id: 3, title: "Meeting", start: new Date(y, m, 20, 9, 0) - }, + },*/ { id: 4, title: "Click for Facebook", - start: new Date(y, m, 27), + start: new Date(y, m, 27, 16), + end: new Date(y, m, 29), + url: "http://facebook.com/" + }, + { + id: 5, + title: "Click for Facebook", + start: new Date(y, m, 27, 8), + end: new Date(y, m, 29), + url: "http://facebook.com/" + }, + { + id: 6, + title: "Click for Facebook", + start: new Date(y, m, 27, 14), end: new Date(y, m, 29), url: "http://facebook.com/" } ], + //titleFormat: 'My', weekStart: 1, - rightToLeft: true, - fixedWeeks: false, - title: true, + //rightToLeft: true, + //fixedWeeks: false, + //title: true, + //timeFormat: 'xg', eventDrop: function(event, delta) { //alert(delta); + }, + resize: function() { + //alert(this); } }); diff --git a/fullcalendar.css b/fullcalendar.css index e97f71d..8704173 100644 --- a/fullcalendar.css +++ b/fullcalendar.css @@ -1,36 +1,20 @@ /* top area w/ month title and buttons */ - -.full-calendar-header { - } .full-calendar-title { float: left; - margin-top: 0; - } - -.r2l .full-calendar-title { - float: right; + margin: 0 0 1em; } .full-calendar-buttons { float: right; - margin-bottom: 1em; - } - -.r2l .full-calendar-buttons { - float: left; + margin: 0 0 1em; } .full-calendar-buttons input { vertical-align: middle; - font-size: 1.0em; - margin-left: 5px; - } - -.r2l .full-calendar-buttons input { - float: right; - margin: 0 5px 0 0; + margin: 0 0 0 5px; + font-size: 1em; } .full-calendar-prev, @@ -40,16 +24,16 @@ -/* table & borders */ +/* table layout & outer border */ + +.full-calendar-month-wrap { + clear: both; + border: 1px solid #ccc; /* outer border color & style */ + } .full-calendar-month { - clear: both; - overflow: hidden; /* - ^ prevents draggable events from leaving. - reason for our long-winded border css. - borders now look consistent across doctypes. */ - border: 1px solid #ccc; /* border color & style */ - text-align: left; + width: 100%; + overflow: hidden; } .full-calendar-month table { @@ -57,12 +41,16 @@ border-spacing: 0; } + + +/* cell styling */ + .full-calendar-month th, .full-calendar-month td.day { padding: 0; vertical-align: top; - border-style: solid; /* border style */ - border-color: #ccc; /* border color */ + border-style: solid; /* inner border style */ + border-color: #ccc; /* inner border color */ border-width: 1px 0 0 1px; } @@ -71,15 +59,11 @@ text-align: center; } -.full-calendar-month th.first, /* left edge? */ +.full-calendar-month th.first, .full-calendar-month td.first { border-left: 0; } - - -/* day styling */ - .full-calendar-month td.today { background: #FFFFCC; } @@ -89,10 +73,6 @@ padding: 0 2px; } -.r2l .full-calendar-month .day-number { - text-align: left; - } - .full-calendar-month .other-month .day-number { color: #bbb; } @@ -117,10 +97,6 @@ text-align: left; } -.r2l .full-calendar-month .event { - text-align: right; - } - .full-calendar-month .ui-draggable-dragging td { cursor: move; } @@ -161,3 +137,27 @@ filter: alpha(opacity=20); } + + +/* right-to-left support */ + +.r2l .full-calendar-title { + float: right; + } + +.r2l .full-calendar-buttons { + float: left; + } + +.r2l .full-calendar-buttons input { + margin: 0 5px 0 0; + } + +.r2l .full-calendar-month .day-number { + text-align: left; + } + +.r2l .full-calendar-month .event { + text-align: right; + } + diff --git a/fullcalendar.js b/fullcalendar.js index 57eb01e..8a51e7b 100644 --- a/fullcalendar.js +++ b/fullcalendar.js @@ -28,10 +28,6 @@ options = options || {}; - var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime; - var bo = typeof options.buttons == 'undefined' ? true : options.buttons; - var weekStart = (options.weekStart || 0) % 7; - var r2l = options.rightToLeft; var dis, dit; // day index sign / translate if (r2l) { @@ -43,6 +39,14 @@ dit = 0; } + var showTime = typeof options.showTime == 'undefined' ? 'guess' : options.showTime; + var bo = typeof options.buttons == 'undefined' ? true : options.buttons; + var weekStart = (options.weekStart || 0) % 7; + var timeFormat = options.timeFormat || 'gx'; + var titleFormat = options.titleFormat || (r2l ? 'Y F' : 'F Y'); + + var tdTopBug, trTopBug, tbodyTopBug, sniffBugs = true; + this.each(function() { var date = options.year ? new Date(options.year, options.month || 0, 1) : new Date(); @@ -89,7 +93,7 @@ - var titleElement, todayButton, monthElement; + var titleElement, todayButton, monthElement, monthElementWidth; var header = $("
").appendTo(this); if (options.title !== false) @@ -99,26 +103,30 @@ var buttons = $("
").appendTo(header); var prevButton, nextButton; if (bo == true || bo.today != false) { - todayButton = $("") - .appendTo(buttons) - .click(today); + todayButton = $("").click(today); if (typeof bo.today == 'string') todayButton.val(bo.today); } if (bo == true || bo.prev != false) { - prevButton = $("") - .appendTo(buttons) - .click(prevMonth); + prevButton = $("").click(prevMonth); if (typeof bo.prev == 'string') prevButton.val(bo.prev); } if (bo == true || bo.next != false) { - nextButton = $("") - .appendTo(buttons) - .click(nextMonth); + nextButton = $("").click(nextMonth); if (typeof bo.next == 'string') nextButton.val(bo.next); } + if (r2l) { + if (nextButton) nextButton.appendTo(buttons); + if (prevButton) prevButton.appendTo(buttons); + if (todayButton) todayButton.appendTo(buttons); + }else{ + if (todayButton) todayButton.appendTo(buttons); + if (prevButton) prevButton.appendTo(buttons); + if (nextButton) nextButton.appendTo(buttons); + } } - monthElement = $("
").appendTo(this); + monthElement = $("
") + .appendTo($("
").appendTo(this)); @@ -134,7 +142,7 @@ clearTime(date); var year = date.getFullYear(); var month = date.getMonth(); - monthTitle = monthNames[month] + ' ' + year; + monthTitle = formatTitle(date); if (titleElement) titleElement.text(monthTitle); clearTime(date); @@ -250,6 +258,17 @@ } resizeTable(); + + if (sniffBugs) { + var tr = tbody.find('tr'); + var td = tr.find('td'); + var trTop = tr.position().top; + var tdTop = td.position().top; + tdTopBug = tdTop < 0; + trTopBug = trTop != tdTop; + tbodyTopBug = tbody.position().top != trTop; + sniffBugs = false; + } if (typeof options.events == 'string') { if (options.loading) options.loading(true); @@ -320,7 +339,9 @@ }); } }); - segs.sort(function(a, b) { return b.msLength - a.msLength; }); + segs.sort(function(a, b) { + return (b.msLength - a.msLength) * 100 + (a.event.start - b.event.start); + }); var levels = []; $.each(segs, function(j, seg) { var l = 0; // level index @@ -362,8 +383,12 @@ for (var i=0; i" : '') + "
"); - buildEventText(element.find('td.c'), event, - typeof event.showTime == 'undefined' ? showTime : event.showTime, r2l); + buildEventText(event, element.find('td.c')); if (options.eventRender) { var res = options.eventRender(event, element); if (typeof res != 'undefined') { @@ -473,7 +497,7 @@ function draggableEvent(event, element) { element.draggable({ - zIndex: 3, + zIndex: 4, delay: 50, opacity: options.eventDragOpacity, revertDuration: options.eventRevertDuration, @@ -561,7 +585,8 @@ dayY = []; tbody.find('tr').each(function() { tr = $(this); - dayY.push(tr.position().top); + dayY.push(tr.position().top + + (trTopBug ? tbody.position().top : 0)); }); dayY.push(dayY[dayY.length-1] + tr.height()); dayX = []; @@ -621,11 +646,15 @@ function resizeTable() { - var cellw = Math.floor(tbody.width() / 7); + var tbodyw = tbody.width(); + var cellw = Math.floor(tbodyw / 7); var cellh = Math.round(cellw * .85); - thead.find('th:lt(6)').width(cellw); + thead.find('th') + .filter(':lt(6)').width(cellw).end() + .filter(':eq(6)').width(tbodyw - cellw*6); tbody.find('td').height(cellh); glass.height(monthElement.height()); + monthElementWidth = monthElement.width(); } function clearEvents() { @@ -633,17 +662,75 @@ eventElements[i][1].remove(); eventElements = []; } + + + + + + function buildEventText(event, element) { + $("") + .text(event.title) + .appendTo(element); + var st = typeof event.showTime == 'undefined' ? showTime : event.showTime; + if (st != false) { + var h = event.start.getHours(); + var m = event.start.getMinutes(); + if (st == true || st == 'guess' && (h || m || event.end.getHours() || event.end.getMinutes())) { + var s = ''; + for (var i=0; i"); + if (r2l) element.append(timeElement.text(' ' + s)); + else element.prepend(timeElement.text(s + ' ')); + } + } + } + + function formatTitle(d) { + var m = d.getMonth(); + var s = ''; + for (var i=0; i").text(timeText)); - } - } - var et = $("").text(event.title) - if (r2l) element.prepend(et); - else element.append(et); + function zeroPad(n) { + if (n < 10) return '0' + n; + return n; } - + + + + // event utils function cleanEvents(events) { $.each(events, function(i, event) { @@ -691,6 +769,7 @@ // date utils var monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December']; + var monthAbbrevs = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var dayNames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; var dayAbbrevs = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; From 5a21b55969c977b671d01c19be2d9dcddc212927 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Mon, 11 May 2009 02:58:02 +0000 Subject: [PATCH 012/185] changed makefile, updated examples --- Makefile | 7 ++++--- changelog.txt | 23 ++++++++++++++++++++ docs/index.txt | 51 +++++++++++++++++++++++++++++++++++++++++---- examples/basic.html | 36 +++++--------------------------- examples/gcal.html | 2 +- examples/json.html | 2 +- fullcalendar.js | 25 +++++++++++----------- version.txt | 2 +- 8 files changed, 94 insertions(+), 54 deletions(-) create mode 100644 changelog.txt diff --git a/Makefile b/Makefile index ac18794..825ed62 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,12 @@ FILES =\ *.js\ *.css\ jquery\ - examples + examples\ + changelog.txt VER = `cat version.txt` -DATE = `svn info . | grep Date: | sed 's/.*: //g'` -REV = `svn info . | grep Rev: | sed 's/.*: //g'` +DATE = `svn info fullcalendar.js | grep Date: | sed 's/.*: //g'` +REV = `svn info fullcalendar.js | grep Rev: | sed 's/.*: //g'` zip: @mkdir -p fullcalendar diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..0832f8b --- /dev/null +++ b/changelog.txt @@ -0,0 +1,23 @@ + +version 1.1 (5/10/09) + - Added the following options: + - weekStart + - rightToLeft + - titleFormat + - timeFormat + - cacheParam + - resize + - Fixed rendering bugs + - Opera 9.25 (events placement & window resizing) + - IE6 (window resizing) + - Optimized window resizing for ALL browsers + - Events on same day now sorted by start time (but first by timespan) + - Correct z-index when dragging + - Dragging contained in overflow DIV for IE6 + - Modified fullcalendar.css + - for right-to-left support + - for variable start-of-week + - for IE6 resizing bug + - for THEAD and TBODY (in 1.0, just used TBODY, restructured in 1.1) + - IF UPGRADING FROM FULLCALENDAR 1.0, YOU MUST UPGRADE FULLCALENDAR.CSS + !!!!!!!!!!! diff --git a/docs/index.txt b/docs/index.txt index 1c017dc..8d7481e 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -40,6 +40,13 @@ Options **fixedWeeks**: boolean, default:``true`` If ``true``, the calendar will always be 6 weeks tall. If ``false``, the calendar's height will vary per month. + + **weekStart**: integer, default:``0`` + The day-of-week each week begins. 0 = Sunday (default), + 1 = Monday (for UK users), 2 = Tuesday, etc. + + **rightToLeft**: boolean, default:``false`` + Displays the calendar right-to-left (for Arabic and Hebrew languages) **abbrevDayHeadings**: boolean, default:``true`` Whether to display "Sun" versus "Sunday" for days of the week. @@ -47,6 +54,18 @@ Options **title**: boolean, default:``true`` Determines whether a title such as "January 2009" will be displayed at the top of the calendar. + + **titleFormat**: string, default:``"Y F"`` + A string defining format of the title above the calendar. The default + "Y F" creates strings such as "January 2009". Use the following + codes in your format string (similar to the PHP's date function): + + * **F** - January through December + * **m** - 01 through 12 (leading zeros) + * **M** - Jan through Dec + * **n** - 1 through 12 + * **Y** - Examples: 1999 or 2003 + * **y** - Examples: 99 or 03 **buttons**: boolean/hash, default:``true`` Determines whether navigation buttons will be displayed at the top of the @@ -55,9 +74,23 @@ Options each button's text. **showTime**: boolean/ ``"guess"``, default:``"guess"`` - Determines if times such as "8a" or "1p" will be displayed before each - event's title. ``"guess"`` displays times only for events with non-zero - start or end times. + Determines if times will be displayed before each event's title. + ``"guess"`` displays times only for events with non-zero start or end times. + + **timeFormat**: string, default: ``"gx"`` + A string defining the format of dislayed of event times. The default "gx" + creates a string such as "9a". Use the following codes in your format + string (similar to PHP's date function): + + * **a** - am or pm + * **A** - AM or PM + * **x** - a or p + * **X** - A or P + * **g** - 1 through 12 (hour) + * **G** - 0 through 23 (hour, military time) + * **h** - 01 through 12 (hour, leading zeros) + * **H** - 00 through 23 (hour, military time and leading zeros) + * **i** - 00 to 59 (minute, leading zeros) **eventDragOpacity**: float The opacity of an event element while it is being dragged (0.0 - 1.0) @@ -118,7 +151,11 @@ Event Data Provider events from a JSON script (when ``event`` is a URL string). The value of this GET parameter will be a UNIX timestamp denoting the end of the last visible day (exclusive). - + + **cacheParam**: string, default:``"_"`` + When using a JSON url, a parameter of this name will + automatically be inserted into the URL to prevent the browser from + caching the response. The value will be the current millisecond time. .. _TriggeredEvents: @@ -134,6 +171,12 @@ Triggered Events Triggered with a ``true`` argument when the calendar begins fetching events via AJAX. Triggered with ``false`` when done. + **resize**: function() + Triggered after the calendar has recovered from a resize (due to the window + pane being resized). + + ``this`` is set to the main element + **dayClick**: function(dayDate) Triggered when the user clicks on a day. ``dayDate`` is a Date object with it's time set to 00:00:00. diff --git a/examples/basic.html b/examples/basic.html index 630c761..b0d421a 100644 --- a/examples/basic.html +++ b/examples/basic.html @@ -4,14 +4,14 @@ - + - + - - + + - + - - - - - - -
- - diff --git a/test/legacy/examples/gcal.html b/test/legacy/examples/gcal.html deleted file mode 100644 index e3669e5..0000000 --- a/test/legacy/examples/gcal.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - -
- - diff --git a/test/legacy/examples/json.html b/test/legacy/examples/json.html deleted file mode 100644 index 60c8a2c..0000000 --- a/test/legacy/examples/json.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - -
-

json_events.php needs to be running in the same directory.

- - diff --git a/test/legacy/examples/json_events.php b/test/legacy/examples/json_events.php deleted file mode 100644 index bf39ba6..0000000 --- a/test/legacy/examples/json_events.php +++ /dev/null @@ -1,25 +0,0 @@ - 1, - 'title' => "Event1", - 'start' => "$year-$month-10", - 'url' => "http://yahoo.com/" - ), - - array( - 'id' => 2, - 'title' => "Event2", - 'start' => "$year-$month-20", - 'end' => "$year-$month-22", - 'url' => "http://yahoo.com/" - ) - - )); - -?> From 2715c1ae74d7e3c8c3c614f211576abbdf5b4377 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Sun, 31 May 2009 20:56:02 +0000 Subject: [PATCH 023/185] add test suit. almost at 1.2 --- Makefile | 35 ++--- changelog.txt | 28 +++- docs/index.txt | 14 +- examples/basic.html | 20 --- fullcalendar/fullcalendar.css | 9 ++ fullcalendar/fullcalendar.js | 72 ++++++---- fullcalendar/gcal.js | 20 +-- test/actions.html | 134 +++++++++++++++++++ test/jgrowl/jgrowl.css | 119 +++++++++++++++++ test/jgrowl/jgrowl.js | 241 ++++++++++++++++++++++++++++++++++ test/locale.html | 89 +++++++++++++ test/methods.html | 108 +++++++++++++++ test/options.html | 95 ++++++++++++++ test/sources.html | 136 +++++++++++++++++++ 14 files changed, 1030 insertions(+), 90 deletions(-) create mode 100644 test/actions.html create mode 100755 test/jgrowl/jgrowl.css create mode 100755 test/jgrowl/jgrowl.js create mode 100644 test/locale.html create mode 100644 test/methods.html create mode 100644 test/options.html create mode 100644 test/sources.html diff --git a/Makefile b/Makefile index 41b7ce8..013e141 100644 --- a/Makefile +++ b/Makefile @@ -5,30 +5,33 @@ FILES =\ examples\ changelog.txt +VER = `cat version.txt` +VVER = `cat ../version.txt` +DATE = `svn info | grep Date: | sed 's/.*: //g'` +REV = `svn info | grep Rev: | sed 's/.*: //g'` + min: @java -jar build/yuicompressor-2.4.2.jar -o build/fullcalendar.min.js fullcalendar/fullcalendar.js - sed -i "s/* FullCalendar/& v`cat version.txt`/" build/fullcalendar.min.js - sed -i "s/* Date:/& `svn info fullcalendar/fullcalendar.js | grep Date: | sed 's/.*: //g'`/" build/fullcalendar.min.js - sed -i "s/* Revision:/& `svn info fullcalendar/fullcalendar.js | grep Rev: | sed 's/.*: //g'`/" build/fullcalendar.min.js zip: - @mkdir -p build/F - @cp -rt build/F ${FILES} .svn - @for f in build/F/fullcalendar/*.js; do\ - sed -i "s/* FullCalendar/& v`cat version.txt`/" $$f;\ - sed -i "s/* Date:/& `svn info $$f | grep Date: | sed 's/.*: //g'`/" $$f;\ - sed -i "s/* Revision:/& `svn info $$f | grep Rev: | sed 's/.*: //g'`/" $$f;\ - done - @rm -rf `find build/F -type d -name .svn` + @mkdir -p build/fullcalendar-${VER} + @cp -rt build/fullcalendar-${VER} ${FILES} @if [ -e build/fullcalendar.min.js ];\ - then cp build/fullcalendar.min.js build/F/fullcalendar;\ - else echo "WARNING: fullcalendar.js not yet minified.";\ + then cp build/fullcalendar.min.js build/fullcalendar-${VER}/fullcalendar;\ + else echo "\n!!! WARNING: fullcalendar.js not yet minified.\n";\ fi - @cd build/F; zip -r fullcalendar-`cat ../../version.txt`.zip * - @mv build/F/fullcalendar-*.zip dist - @rm -rf build/F + @rm -rf `find build -type d -name .svn` + @for f in build/fullcalendar-${VER}/fullcalendar/*.js; do\ + sed -i "s/* FullCalendar/& v${VER}/" $$f;\ + sed -i "s/* Date:/& ${DATE}/" $$f;\ + sed -i "s/* Revision:/& ${REV}/" $$f;\ + done + @cd build; zip -r fullcalendar-${VVER}.zip fullcalendar-${VVER} + @mv build/fullcalendar-${VER}.zip dist + @rm -rf build/fullcalendar-${VER} @rm -f build/fullcalendar.min.js clean: @rm -rf dist/* + @rm -rf build/fullcalendar-* @rm -f build/*.js diff --git a/changelog.txt b/changelog.txt index e0c766f..b8197f2 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,11 +1,25 @@ -version 1.2 - - cssClass attribute for CalEvents - - multiple event sources (using an array for the 'events' option) - - the 'events' option for fullCalendar() and gcalFullCalendar() is now optional - - bugs w/ month date formatting - - change behavior for parsing number strings (now as unix timestamp) - - allow multiple classes in cssClass +version 1.2 (5/31/09) + - expanded API + - 'className' CalEvent attribute + - 'source' CalEvent attribute + - dynamically get/add/remove/update events of current month + - locale improvements: change month/day name text + - better date formatting ($.fullCalendar.formatDate) + - multiple 'event sources' allowed + - dynamically add/remove event sources + - docs have been reworked (include addition of Google Calendar docs) + - changed behavior of parseDate for number strings + (now interpets as unix timestamp, not MS times) + - bugfixes + - rightToLeft month start bug + - off-by-one errors with month formatting commands + - events from previous months sticking when clicking prev/next quickly + - Google Calendar API changed to work w/ multiple event sources + - can also provide 'className' and 'draggable' options + - date utilties moved from $ properties to $.fullCalendar + - minified version of fullcalendar.js + - test suit (available from svn) version 1.1 (5/10/09) - Added the following options: diff --git a/docs/index.txt b/docs/index.txt index ea99077..bd0e0d1 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -2,7 +2,7 @@ Main Usage ========== -The following code initializes a FullCalendar within an element with ID 'calendar':: +The following code initializes a FullCalendar within an element of ID 'calendar':: $('#calendar').fullCalendar({ @@ -101,8 +101,9 @@ special happens: **eventRender**: function(calEvent, element) Triggered before an element is rendered for the given :ref:`CalEvent `. - ``element`` is the jQuery element that will be used by default. You can modify + ``element`` is the jQuery element that will be used by default. You may modify this element or return a brand new element that will be used instead. + Returning ``false`` will prevent the event from being rendered at all. This function is great for attaching other jQuery plugins to each event element, such as a `qTip `_ @@ -265,11 +266,10 @@ CalEvent: do this automatically. The following methods can be called on a FullCalendar that has already been -initialized. These methods get/add/remove/update the event elements that -currently reside on the calendar. Note that when you are using a JSON feed or custom -event source, your event is never *permanently* deleted, because it may be -refetched from the source at a later time. It is up to the developer to delete -the event(s) from any database. +initialized. These methods get/add/update/remove events on the current month. +For JSON and custom event sources, changes are never *permanent* because they +may be overwritten by a refetch. The developer is responsible for updating +any remote databases. **.fullCalendar(** ``'addEvent'``, **calEvent)** Add an event to the current month on-the-fly. ``calEvent`` is an object diff --git a/examples/basic.html b/examples/basic.html index 0a4382b..bac04af 100644 --- a/examples/basic.html +++ b/examples/basic.html @@ -25,25 +25,6 @@ $(document).ready(function() { - $('input').click(function() { - - /*$('#calendar').fullCalendar("addEvent", { - id: 999, - title: "Adam Shaw", - start: "2009-05-18" - });*/ - - //$('#calendar').fullCalendar("removeEvent", 3); - - var events = $('#calendar').fullCalendar('getEventsById', 2); - var ev1 = events[0]; - ev1.title = "yo"; - ev1.start = '2009-05-14'; - ev1.end = '2009-05-16'; - $('#calendar').fullCalendar('updateEvent', ev1); - - }); - var d = new Date(); var y = d.getFullYear(); var m = d.getMonth(); @@ -87,7 +68,6 @@ -
diff --git a/fullcalendar/fullcalendar.css b/fullcalendar/fullcalendar.css index 68ce1a4..3fb5720 100644 --- a/fullcalendar/fullcalendar.css +++ b/fullcalendar/fullcalendar.css @@ -126,6 +126,15 @@ font-weight: bold; } + /* To change the color of events on a per-class basis (such as with the + * "className" attribute of a CalEvent), do something like this: + * + * .full-calendar-month .myclass td { + * background: green; + * } + */ + + /* the rectangle that covers a day when dragging an event */ diff --git a/fullcalendar/fullcalendar.js b/fullcalendar/fullcalendar.js index e8bb061..8511349 100644 --- a/fullcalendar/fullcalendar.js +++ b/fullcalendar/fullcalendar.js @@ -218,7 +218,13 @@ addEventSource: function(src) { eventSources.push(src); + if (options.loading) { + options.loading(true); + } fetchEventSource(src, function() { + if (options.loading) { + options.loading(false); + } clearEventElements(); renderEvents(); }); @@ -530,7 +536,7 @@ }); } }); - segs.sort(segSort); + segs.sort(segCmp); var levels = []; $.each(segs, function(j, seg) { var l = 0; // level index @@ -784,14 +790,16 @@ events[i]._start = cloneDate(events[i].start); } } - if (options.eventDrop) + if (options.eventDrop) { options.eventDrop.call(this, event, delta, ev, ui); + } clearEventElements(); renderEvents(); } dayOverlay.hide(); - if (options.eventDragStop) + if (options.eventDragStop) { options.eventDragStop.call(this, event, ev, ui); + } } }); } @@ -880,12 +888,16 @@ // function dayDate(td) { - var i, tds = tbody.get(0).getElementsByTagName('td'); - for (i=0; i 0) { + var queued = eventSources.length; + var sourceDone = function() { + if (--queued == 0) { + if (options.loading) { + options.loading(false); + } + if (callback) { + callback(events); + } } + }; + if (options.loading) { + options.loading(true); + } + for (var i=0; i click the arrow next to your calendar's name - * -> click "Share this calendar" - * -> check "Make this calendar public" and then Save - * -> click the arrow again, then click "Calendar settings" - * -> in the "Calendar Address" section, click the XML rectangle - * -> the URL is displayed + * Visit http://arshaw.com/fullcalendar/docs/#google-calendar + * for docs and examples. * * Copyright (c) 2009 Adam Shaw * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html + * + * Date: + * Revision: */ (function($) { diff --git a/test/actions.html b/test/actions.html new file mode 100644 index 0000000..5ce6c2d --- /dev/null +++ b/test/actions.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + +
+ + diff --git a/test/jgrowl/jgrowl.css b/test/jgrowl/jgrowl.css new file mode 100755 index 0000000..ce3edc6 --- /dev/null +++ b/test/jgrowl/jgrowl.css @@ -0,0 +1,119 @@ + +div.jGrowl { + padding: 10px; + z-index: 9999; +} + +/** Special IE6 Style Positioning **/ +div.ie6 { + position: absolute; +} + +div.ie6.top-right { + right: auto; + bottom: auto; + left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); + top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' ); +} + +div.ie6.top-left { + left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); + top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' ); +} + +div.ie6.bottom-right { + left: expression( ( 0 - jGrowl.offsetWidth + ( document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth ) + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); + top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' ); +} + +div.ie6.bottom-left { + left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); + top: expression( ( 0 - jGrowl.offsetHeight + ( document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' ); +} + +div.ie6.center { + left: expression( ( 0 + ( ignoreMe2 = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft ) ) + 'px' ); + top: expression( ( 0 + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop ) ) + 'px' ); + width: 100%; +} + +/** Normal Style Positions **/ +body > div.jGrowl { + position: fixed; +} + +body > div.jGrowl.top-left { + left: 0px; + top: 0px; +} + +body > div.jGrowl.top-right { + right: 0px; + top: 0px; +} + +body > div.jGrowl.bottom-left { + left: 0px; + bottom: 0px; +} + +body > div.jGrowl.bottom-right { + right: 0px; + bottom: 0px; +} + +body > div.jGrowl.center { + top: 0px; + width: 50%; + left: 25%; +} + +/** Cross Browser Styling **/ +div.center div.jGrowl-notification, div.center div.jGrowl-closer { + margin-left: auto; + margin-right: auto; +} + +div.jGrowl div.jGrowl-notification, div.jGrowl div.jGrowl-closer { + background-color: #000; + color: #fff; + opacity: .85; + filter: alpha(opacity = 85); + zoom: 1; + width: 235px; + padding: 10px; + margin-top: 5px; + margin-bottom: 5px; + font-family: Tahoma, Arial, Helvetica, sans-serif; + font-size: 12px; + text-align: left; + display: none; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +div.jGrowl div.jGrowl-notification { + min-height: 40px; +} + +div.jGrowl div.jGrowl-notification div.header { + font-weight: bold; + font-size: 10px; +} + +div.jGrowl div.jGrowl-notification div.close { + float: right; + font-weight: bold; + font-size: 12px; + cursor: pointer; +} + +div.jGrowl div.jGrowl-closer { + height: 15px; + padding-top: 4px; + padding-bottom: 4px; + cursor: pointer; + font-size: 11px; + font-weight: bold; + text-align: center; +} diff --git a/test/jgrowl/jgrowl.js b/test/jgrowl/jgrowl.js new file mode 100755 index 0000000..352f81d --- /dev/null +++ b/test/jgrowl/jgrowl.js @@ -0,0 +1,241 @@ +/** + * jGrowl 1.2.0 + * + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * Written by Stan Lemon + * Last updated: 2009.05.11 + * + * jGrowl is a jQuery plugin implementing unobtrusive userland notifications. These + * notifications function similarly to the Growl Framework available for + * Mac OS X (http://growl.info). + * + * To Do: + * - Move library settings to containers and allow them to be changed per container + * + * Changes in 1.2.0 + * - Added message pooling to limit the number of messages appearing at a given time. + * - Closing a notification is now bound to the notification object and triggered by the close button. + * + * Changes in 1.1.2 + * - Added iPhone styled example + * - Fixed possible IE7 bug when determining if the ie6 class shoudl be applied. + * - Added template for the close button, so that it's content could be customized. + * + * Changes in 1.1.1 + * - Fixed CSS styling bug for ie6 caused by a mispelling + * - Changes height restriction on default notifications to min-height + * - Added skinned examples using a variety of images + * - Added the ability to customize the content of the [close all] box + * - Added jTweet, an example of using jGrowl + Twitter + * + * Changes in 1.1.0 + * - Multiple container and instances. + * - Standard $.jGrowl() now wraps $.fn.jGrowl() by first establishing a generic jGrowl container. + * - Instance methods of a jGrowl container can be called by $.fn.jGrowl(methodName) + * - Added glue preferenced, which allows notifications to be inserted before or after nodes in the container + * - Added new log callback which is called before anything is done for the notification + * - Corner's attribute are now applied on an individual notification basis. + * + * Changes in 1.0.4 + * - Various CSS fixes so that jGrowl renders correctly in IE6. + * + * Changes in 1.0.3 + * - Fixed bug with options persisting across notifications + * - Fixed theme application bug + * - Simplified some selectors and manipulations. + * - Added beforeOpen and beforeClose callbacks + * - Reorganized some lines of code to be more readable + * - Removed unnecessary this.defaults context + * - If corners plugin is present, it's now customizable. + * - Customizable open animation. + * - Customizable close animation. + * - Customizable animation easing. + * - Added customizable positioning (top-left, top-right, bottom-left, bottom-right, center) + * + * Changes in 1.0.2 + * - All CSS styling is now external. + * - Added a theme parameter which specifies a secondary class for styling, such + * that notifications can be customized in appearance on a per message basis. + * - Notification life span is now customizable on a per message basis. + * - Added the ability to disable the global closer, enabled by default. + * - Added callbacks for when a notification is opened or closed. + * - Added callback for the global closer. + * - Customizable animation speed. + * - jGrowl now set itself up and tears itself down. + * + * Changes in 1.0.1: + * - Removed dependency on metadata plugin in favor of .data() + * - Namespaced all events + */ +(function($) { + + /** jGrowl Wrapper - Establish a base jGrowl Container for compatibility with older releases. **/ + $.jGrowl = function( m , o ) { + // To maintain compatibility with older version that only supported one instance we'll create the base container. + if ( $('#jGrowl').size() == 0 ) $('
').addClass($.jGrowl.defaults.position).appendTo('body'); + // Create a notification on the container. + $('#jGrowl').jGrowl(m,o); + }; + + + /** Raise jGrowl Notification on a jGrowl Container **/ + $.fn.jGrowl = function( m , o ) { + if ( $.isFunction(this.each) ) { + var args = arguments; + + return this.each(function() { + var self = this; + + /** Create a jGrowl Instance on the Container if it does not exist **/ + if ( $(this).data('jGrowl.instance') == undefined ) { + $(this).data('jGrowl.instance', new $.fn.jGrowl()); + $(this).data('jGrowl.instance').startup( this ); + } + + /** Optionally call jGrowl instance methods, or just raise a normal notification **/ + if ( $.isFunction($(this).data('jGrowl.instance')[m]) ) { + $(this).data('jGrowl.instance')[m].apply( $(this).data('jGrowl.instance') , $.makeArray(args).slice(1) ); + } else { + $(this).data('jGrowl.instance').create( m , o ); + } + }); + }; + }; + + $.extend( $.fn.jGrowl.prototype , { + + /** Default JGrowl Settings **/ + defaults: { + pool: 0, + header: '', + group: '', + sticky: false, + position: 'top-right', // Is this still needed? + glue: 'after', + theme: 'default', + corners: '10px', + check: 250, + life: 3000, + speed: 'normal', + easing: 'swing', + closer: true, + closeTemplate: '×', + closerTemplate: '
[ close all ]
', + log: function(e,m,o) {}, + beforeOpen: function(e,m,o) {}, + open: function(e,m,o) {}, + beforeClose: function(e,m,o) {}, + close: function(e,m,o) {}, + animateOpen: { + opacity: 'show' + }, + animateClose: { + opacity: 'hide' + } + }, + + notifications: [], + + /** jGrowl Container Node **/ + element: null, + + /** Interval Function **/ + interval: null, + + /** Create a Notification **/ + create: function( message , o ) { + var o = $.extend({}, this.defaults, o); + + this.notifications[ this.notifications.length ] = { message: message , options: o }; + + o.log.apply( this.element , [this.element,message,o] ); + }, + + render: function( notification ) { + var self = this; + var message = notification.message; + var o = notification.options; + + var notification = $('
' + o.closeTemplate + '
' + o.header + '
' + message + '
') + .data("jGrowl", o).addClass(o.theme).children('div.close').bind("click.jGrowl", function() { + $(this).parent().trigger('jGrowl.close'); + }).parent(); + + ( o.glue == 'after' ) ? $('div.jGrowl-notification:last', this.element).after(notification) : $('div.jGrowl-notification:first', this.element).before(notification); + + /** Notification Actions **/ + $(notification).bind("mouseover.jGrowl", function() { + $(this).data("jGrowl").pause = true; + }).bind("mouseout.jGrowl", function() { + $(this).data("jGrowl").pause = false; + }).bind('jGrowl.beforeOpen', function() { + o.beforeOpen.apply( self.element , [self.element,message,o] ); + }).bind('jGrowl.open', function() { + o.open.apply( self.element , [self.element,message,o] ); + }).bind('jGrowl.beforeClose', function() { + o.beforeClose.apply( self.element , [self.element,message,o] ); + }).bind('jGrowl.close', function() { + $(this).trigger('jGrowl.beforeClose').animate(o.animateClose, o.speed, o.easing, function() { + $(this).remove(); + o.close.apply( self.element , [self.element,message,o] ); + }); + }).trigger('jGrowl.beforeOpen').animate(o.animateOpen, o.speed, o.easing, function() { + $(this).data("jGrowl").created = new Date(); + }).trigger('jGrowl.open'); + + /** Optional Corners Plugin **/ + if ( $.fn.corner != undefined ) $(notification).corner( o.corners ); + + /** Add a Global Closer if more than one notification exists **/ + if ( $('div.jGrowl-notification:parent', this.element).size() > 1 && $('div.jGrowl-closer', this.element).size() == 0 && this.defaults.closer != false ) { + $(this.defaults.closerTemplate).addClass('jGrowl-closer').addClass(this.defaults.theme).appendTo(this.element).animate(this.defaults.animateOpen, this.defaults.speed, this.defaults.easing).bind("click.jGrowl", function() { + $(this).siblings().children('div.close').trigger("click.jGrowl"); + + if ( $.isFunction( self.defaults.closer ) ) self.defaults.closer.apply( $(this).parent()[0] , [$(this).parent()[0]] ); + }); + }; + }, + + /** Update the jGrowl Container, removing old jGrowl notifications **/ + update: function() { + $(this.element).find('div.jGrowl-notification:parent').each( function() { + if ( $(this).data("jGrowl") != undefined && $(this).data("jGrowl").created != undefined && ($(this).data("jGrowl").created.getTime() + $(this).data("jGrowl").life) < (new Date()).getTime() && $(this).data("jGrowl").sticky != true && + ($(this).data("jGrowl").pause == undefined || $(this).data("jGrowl").pause != true) ) { + $(this).trigger('jGrowl.close'); + } + }); + + if ( this.notifications.length > 0 && (this.defaults.pool == 0 || $(this.element).find('div.jGrowl-notification:parent').size() < this.defaults.pool) ) { + this.render( this.notifications.shift() ); + } + + if ( $(this.element).find('div.jGrowl-notification:parent').size() < 2 ) { + $(this.element).find('div.jGrowl-closer').animate(this.defaults.animateClose, this.defaults.speed, this.defaults.easing, function() { + $(this).remove(); + }); + }; + }, + + /** Setup the jGrowl Notification Container **/ + startup: function(e) { + this.element = $(e).addClass('jGrowl').append('
'); + this.interval = setInterval( function() { + jQuery(e).data('jGrowl.instance').update(); + }, this.defaults.check); + + if ($.browser.msie && parseInt($.browser.version) < 7 && !window["XMLHttpRequest"]) $(this.element).addClass('ie6'); + }, + + /** Shutdown jGrowl, removing it and clearing the interval **/ + shutdown: function() { + $(this.element).removeClass('jGrowl').find('div.jGrowl-notification').remove(); + clearInterval( this.interval ); + } + }); + + /** Reference the Defaults Object for compatibility with older versions of jGrowl **/ + $.jGrowl.defaults = $.fn.jGrowl.prototype.defaults; + +})(jQuery); \ No newline at end of file diff --git a/test/locale.html b/test/locale.html new file mode 100644 index 0000000..6b26882 --- /dev/null +++ b/test/locale.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + +
+ + diff --git a/test/methods.html b/test/methods.html new file mode 100644 index 0000000..990d851 --- /dev/null +++ b/test/methods.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + +
+
+
+
+
+
+ +

+

+today   +prev   +next   +June 1986 +

+
+ + diff --git a/test/options.html b/test/options.html new file mode 100644 index 0000000..06a75af --- /dev/null +++ b/test/options.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + +
+ + diff --git a/test/sources.html b/test/sources.html new file mode 100644 index 0000000..c85992b --- /dev/null +++ b/test/sources.html @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+ +
+ + +
+ + From 589d7ff1810491a75a95c4781945d96af0fb6299 Mon Sep 17 00:00:00 2001 From: Adam Shaw Date: Mon, 1 Jun 2009 04:51:34 +0000 Subject: [PATCH 024/185] at version 1.2 --- Makefile | 3 +- changelog.txt | 7 +++- docs/index.txt | 32 +++++++++++++--- fullcalendar/fullcalendar.css | 37 ++++++++++++------ fullcalendar/fullcalendar.js | 72 +++++++++++++++++++++++++++-------- test/locale.html | 2 +- test/methods.html | 2 + test/options.html | 10 +++-- test/sources.html | 8 ++++ 9 files changed, 133 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 013e141..722b346 100644 --- a/Makefile +++ b/Makefile @@ -20,13 +20,14 @@ zip: then cp build/fullcalendar.min.js build/fullcalendar-${VER}/fullcalendar;\ else echo "\n!!! WARNING: fullcalendar.js not yet minified.\n";\ fi - @rm -rf `find build -type d -name .svn` + @rm -rf `find build/fullcalendar-* -type d -name .svn` @for f in build/fullcalendar-${VER}/fullcalendar/*.js; do\ sed -i "s/* FullCalendar/& v${VER}/" $$f;\ sed -i "s/* Date:/& ${DATE}/" $$f;\ sed -i "s/* Revision:/& ${REV}/" $$f;\ done @cd build; zip -r fullcalendar-${VVER}.zip fullcalendar-${VVER} + @mkdir -p dist @mv build/fullcalendar-${VER}.zip dist @rm -rf build/fullcalendar-${VER} @rm -f build/fullcalendar.min.js diff --git a/changelog.txt b/changelog.txt index b8197f2..b369c73 100644 --- a/changelog.txt +++ b/changelog.txt @@ -8,6 +8,7 @@ version 1.2 (5/31/09) - better date formatting ($.fullCalendar.formatDate) - multiple 'event sources' allowed - dynamically add/remove event sources + - options for prevYear and nextYear buttons - docs have been reworked (include addition of Google Calendar docs) - changed behavior of parseDate for number strings (now interpets as unix timestamp, not MS times) @@ -17,9 +18,13 @@ version 1.2 (5/31/09) - events from previous months sticking when clicking prev/next quickly - Google Calendar API changed to work w/ multiple event sources - can also provide 'className' and 'draggable' options - - date utilties moved from $ properties to $.fullCalendar + - date utilties moved from $ to $.fullCalendar + - more documentation in source code - minified version of fullcalendar.js - test suit (available from svn) + - top buttons now use
" + + "" + "
" + "
" + "
" : '') + + (roundW ? "" : '') + "" + - (seg.isEnd ? "" : '') + "
" : '') + "
" : '') + + (roundW ? "" : '') + "" + - (seg.isEnd ? "" : '') + "
" : '') + "
" : '') + + (roundW ? "" : '') + "" + - (seg.isEnd ? "" : '') + "
" : '') + "
" + (roundE ? "" : '') + "
").appendTo(monthElement); - - thead = ""; - for (var i=0; i<7; i++) { - var j = (i * dis + dit + weekStart) % 7; - thead += - ""; - } - thead = $(thead + "").appendTo(table); - - tbody = ""; - var d = cloneDate(start); - for (var i=0; i"; - var tds = ""; - for (var j=0; j<7; j++) { - var s = - ""; - if (r2l) tds = s + tds; - else tds += s; - addDays(d, 1); - } - tbody += tds + ""; - } - tbody = $(tbody + "").appendTo(table); - - // a protective coating over the TABLE - // intercepts mouse clicks and prevents text-selection - glass = $("
") - .appendTo(monthElement) - .click(function(ev, ui) { - if (options.dayClick) { - buildDayGrid(); - var td = dayTD(ev.pageX, ev.pageY); - if (td) return options.dayClick.call(td, dayDate(td)); - } - }); - - }else{ - - // NOT first time, reuse as many cells as possible - - var diff = numWeeks - tbody.find('tr').length; - if (diff < 0) { - // remove extra rows - tbody.find('tr:gt(' + (numWeeks-1) + ')').remove(); - } - else if (diff > 0) { - var trs = ""; - for (var i=0; i"; - for (var j=0; j<7; j++) { - trs += - "
"; - } - trs += ""; - } - if (trs) tbody.append(trs); - } - - // re-label and re-class existing cells - var d = cloneDate(start); - tbody.find('tr').each(function() { - for (var i=0; i<7; i++) { - var td = this.childNodes[i * dis + dit]; - if (d.getMonth() == month) { - $(td).removeClass('other-month'); - }else{ - $(td).addClass('other-month'); - } - if (d.getTime() == today.getTime()) { - $(td).addClass('today'); - }else{ - $(td).removeClass('today'); - } - $(td.childNodes[0]).text(d.getDate()); - addDays(d, 1); - } - }); - - } - - setCellSizes(); - - if (sniffBugs) { - // nasty bugs in opera 9.25 - // position() returning relative to direct parent - var tr = tbody.find('tr'); - var td = tr.find('td'); - var trTop = tr.position().top; - var tdTop = td.position().top; - tdTopBug = tdTop < 0; - trTopBug = trTop != tdTop; - tbodyTopBug = tbody.position().top != trTop; - sniffBugs = false; - } - - fetchEvents(renderEvents); - - ignoreResizes = false; - - if (options.monthDisplay) { - options.monthDisplay(date.getFullYear(), date.getMonth(), monthTitle); - } - - } - - - // - // Adjust dimensions of the cells, based on container's width - // - - function setCellSizes() { - var tbodyw = tbody.width(); - var cellw = Math.floor(tbodyw / 7); - var cellh = Math.round(cellw * .85); - thead.find('th') - .filter(':lt(6)').width(cellw).end() - .filter(':eq(6)').width(tbodyw - cellw*6); - tbody.find('td').height(cellh); - glass.height(monthElement.height()); - monthElementWidth = monthElement.width(); - } - - - - - - - /*******************************************************************/ - // - // Event Rendering - // - /*******************************************************************/ - - - // - // Render the 'events' array. First, break up into segments - // - - var eventMatrix = []; - - function renderEvents() { - eventMatrix = []; - var i = 0; - var ws = cloneDate(start); - var we = addDays(cloneDate(ws), 7); - while (ws.getTime() < end.getTime()) { - var segs = []; - $.each(events, function(j, event) { - if (event.end.getTime() > ws.getTime() && event.start.getTime() < we.getTime()) { - var ss, se, isStart, isEnd; - if (event.start.getTime() < ws.getTime()) { - ss = cloneDate(ws); - isStart = false; - }else{ - ss = cloneDate(event.start); - isStart = true; - } - if (event.end.getTime() > we.getTime()) { - se = cloneDate(we); - isEnd = false; - }else{ - se = cloneDate(event.end); - isEnd = true; - } - ss = clearTime(ss); - se = clearTime((se.getHours()==0 && se.getMinutes()==0) ? se : addDays(se, 1)); - segs.push({ - event: event, start: ss, end: se, - isStart: isStart, isEnd: isEnd, msLength: se - ss - }); - } - }); - segs.sort(segCmp); - var levels = []; - $.each(segs, function(j, seg) { - var l = 0; // level index - while (true) { - var collide = false; - if (levels[l]) { - for (var k=0; k levels[l][k].start.getTime() && - seg.start.getTime() < levels[l][k].end.getTime()) { - collide = true; - break; - } - } - } - if (collide) { - l++; - continue; - }else{ - break; - } - } - if (levels[l]) levels[l].push(seg); - else levels[l] = [seg]; - }); - eventMatrix[i] = levels; - addDays(ws, 7); - addDays(we, 7); - i++; - } - _renderEvents(); - } - - - // - // Do the REAL rendering of the segments - // - - var eventElements = []; // [[event, element], ...] - - function _renderEvents() { - for (var i=0; i") - .append("" + - (roundW ? "") - .append("" + - (roundW ? "") - .append("" + - (roundW ? ""); - buildEventText(event, element.find('td.c')); - if (options.eventRender) { - var res = options.eventRender(event, element); - if (typeof res != 'undefined') { - if (res === false) continue; - if (res !== true) element = $(res); - } - } - element - .css({ - position: 'absolute', - top: top, - left: left1, - width: left2 - left1, - 'z-index': 3 - }) - .appendTo(monthElement); - initEventElement(event, element); - var h = element.outerHeight({margin:true}); - if (h > maxh) maxh = h; - } - height += maxh; - top += maxh; - } - innerDiv.height(height); - } - } - - - // - // create the text-contents of an event segment - // - - function buildEventText(event, element) { - $("") - .text(event.title) - .appendTo(element); - var st = typeof event.showTime == 'undefined' ? showTime : event.showTime; - if (st != false) { - if (st == true || st == 'guess' && - (event.start.getHours() || event.start.getMinutes() || - event.end.getHours() || event.end.getMinutes())) { - var timeStr = $.fullCalendar.formatDate(event.start, timeFormat); - var timeElement = $(""); - if (r2l) element.append(timeElement.text(' ' + timeStr)); - else element.prepend(timeElement.text(timeStr + ' ')); - } - } - } - - - // - // Attach event handlers to an event segment - // - - function initEventElement(event, element) { - element.click(function(ev) { - if (!element.hasClass('ui-draggable-dragging')) { - if (options.eventClick) { - var res = options.eventClick.call(this, event, ev); - if (res === false) return false; - } - if (event.url) window.location.href = event.url; - } - }); - if (options.eventMouseover) - element.mouseover(function(ev) { - options.eventMouseover.call(this, event, ev); - }); - if (options.eventMouseout) - element.mouseout(function(ev) { - options.eventMouseout.call(this, event, ev); - }); - if (typeof event.draggable != 'undefined') { - if (event.draggable) - draggableEvent(event, element); - } - else if (options.draggable) { - draggableEvent(event, element); - } - eventElements.push([event, element]); - } - - - // - // Remove all event segments from DOM - // - - function clearEventElements() { - for (var i=0; i") - .appendTo(monthElement); - buildDayGrid(); - dragTD = dragStartTD = null; - eventDrag(this, ev, ui); - if (options.eventDragStart) - options.eventDragStart.call(this, event, ev, ui); - }, - drag: function(ev, ui) { - eventDrag(this, ev, ui); - }, - stop: function(ev, ui) { - if (!dragTD || dragTD == dragStartTD) { - // show all events - for (var i=0; i dayY0 + dayY[r+1]) r++; - while (c < cmax && x > dayX0 + dayX[c+1]) c++; - if (r < 0 || r >= rmax || c < 0 || c >= cmax) - return currTD = null; - else if (!currTD || r != currR || c != currC) { - currR = r; - currC = c; - currTD = tbody.find('tr:eq('+r+') td:eq('+c+')').get(0); - currTDX = dayX[c]; - currTDY = dayY[r]; - currTDW = dayX[c+1] - currTDX; - currTDH = dayY[r+1] - currTDY; - return currTD; - } - return currTD; - } - - // - // Get a TD's date - // - - function dayDate(td) { - var i, trs = tbody.get(0).getElementsByTagName('tr'); - for (i=0; i 0) { - var queued = eventSources.length; - var sourceDone = function() { - if (--queued == 0) { - popLoading(); - if (callback) { - callback(events); - } - } - }; - pushLoading(); - for (var i=0; i" + + "" + + ""; + dayCnt = 0; + for (d=cloneDate(start); d" + dayAbbrevs[d.getDay()] + ""; + if (+d == +today) { + todayI = dayCnt; + } + dayCnt++; + } + s += "" + + "" + + "" + + "" + + "" + + "
" + - (options.abbrevDayHeadings!=false ? dayAbbrevs[j] : dayNames[j]) + - "
" + d.getDate() + "
" + - "
" + - "
" + - "
" + - "
" : '') + - "" + - (roundE ? "" : '') + "
" : '') + - "" + - (roundE ? "" : '') + "
" : '') + - "" + - (roundE ? "" : '') + "
  
all day" + + "
 
"; + head = $(s).appendTo(element); + + // body & event panel + s = "
" + + ""; + d = getToday(); + dDay = d.getDay(); + for (i=0; d.getDay()==dDay; i++, addMinutes(d, options.slotMinutes)) { + dMinutes = d.getMinutes(); + s += ""; + } + s += "
" + + (!slotNormal || dMinutes==0 ? formatDate(d, options.agendaSideTimeFormat) : ' ') + + " 
"; + body = $("
") + .append(panel = $(s)) + .appendTo(element); + + // background stripes + s = "
" + + ""; + for (i=0; i
 
"; + } + s += "
"; + bg = $(s).appendTo(element); + + }else{ // skeleton already built, just modify it + + clearEvents(); + + // change classes of background stripes + todayI = Math.round((today - start) / msInDay); + bg.find('td').each(function(i) { + if (i == todayI) { + $(this).removeClass('fc-not-today') + .addClass('fc-today') + .addClass(tm + '-state-highlight'); + }else{ + $(this).addClass('fc-not-today') + .removeClass('fc-today') + .removeClass(tm + '-state-highlight'); + } + }); + + // if 1-day view, change day-of-week class and header text + if (dayCnt == 1) { + var th = head.find('th:eq(1)').html(dayAbbrevs[start.getDay()])[0], + td = bg.find('td')[0]; + th.className = th.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[start.getDay()]); + td.className = td.className.replace(/^fc-\w+(?= )/, 'fc-' + dayIDs[start.getDay()]); + } + + } + + updateSize(); + fetchEvents(renderEvents); + + }; + + + function updateSize() { + + // align first 'time' column + timeWidth = body.find('th:first').outerWidth(); + head.find('th:first').width(timeWidth); + + // set table width (100% in css wasn't working in IE) + var panelWidth = body[0].clientWidth || body.width(); // first time, there are no scrollbars!? for IE6? + body.find('table').width(panelWidth); + + // align spacer column to scrollbar width + setOuterWidth(head.find('th:last'), body.width() - panelWidth); + + // position background stripe container + bg.css({ + left: timeWidth, + width: panelWidth - timeWidth, + height: element.height() + }); + + // align other columns + dayWidth = Math.floor((panelWidth - timeWidth) / dayCnt); + var topCells = head.find('tr:first th:gt(0)'), + bgCells = bg.find('td'); + for (var i=0, len=bgCells.length-1; i view.start) { + if (event.hasTime) { + event._end = event.end || addMinutes(cloneDate(event.start), options.defaultEventMinutes); + slotEvents.push(event); + }else{ + event._end = addDays(cloneDate(event.end || event.start), 1); + dayEvents.push(event); + } + } + } + + cachedEvents = events; + cachedSlotSegs = compileSlotSegs(slotEvents, view.start, view.end); + cachedDaySegs = levelizeSegs(sliceSegs(dayEvents, view.start, view.end)); + + renderSlotSegs(cachedSlotSegs); + renderDaySegs(cachedDaySegs); + + } + + + function rerenderEvents(skipCompile) { + clearEvents(); + if (skipCompile) { + renderSlotSegs(cachedSlotSegs); + renderDaySegs(cachedDaySegs); + }else{ + renderEvents(cachedEvents); + } + } + + + function clearEvents() { + for (var i=0; i") + .append(anchorElement = $("") + .append(titleElement = $("") + .text(event.title))) + .css({ + position: 'absolute', + zIndex: 1000, + top: top, + left: left + }); + if (event.url) { + anchorElement.attr('href', event.url); + } + if (seg.isStart) { + eventElement.addClass('fc-corner-top'); + // add the time header + anchorElement + .prepend(timeElement = $("") + .text(formatDates(event.start, event.end, options.agendaEventTimeFormat))) + }else{ + timeElement = null; + } + if (seg.isEnd) { + eventElement.addClass('fc-corner-bottom'); + resizableSlotEvent(event, eventElement, timeElement); + } + eventElement.appendTo(panel); + setOuterWidth(eventElement, width, true); + setOuterHeight(eventElement, bottom-top, true); + if (timeElement && eventElement.height() - titleElement.position().top < 10) { + // event title doesn't have enough room, but next to the time + timeElement.text(formatDate(event.start, options.agendaEventTimeFormat) + ' - ' + event.title); + titleElement.remove(); + } + draggableSlotEvent(event, eventElement, timeElement); + reportEventElement(event, eventElement); + } + } + } + } + + + // renders 'all-day' events at the top + + function renderDaySegs(segRow) { + var td = head.find('td'); + var tdInner = td.find('div div'); + var top = tdInner.position().top, + rowHeight = 0, + i, len=segRow.length, level, + levelHeight, + j, seg, + event, left, right, + eventElement, anchorElement; + for (i=0; i") + .append(anchorElement = $("") + .append($("") + .text(event.title))) + .css({ + position: 'absolute', + top: top, + left: timeWidth + left + }); + if (seg.isStart) { + eventElement.addClass('fc-corner-left'); + } + if (seg.isEnd) { + eventElement.addClass('fc-corner-right'); + } + if (event.url) { + anchorElement.attr('href', event.url); + } + eventElement.appendTo(head); + setOuterWidth(eventElement, right-left, true); + draggableDayEvent(event, eventElement); + //resizableDayEvent(event, eventElement); + reportEventElement(event, eventElement); + levelHeight = Math.max(levelHeight, eventElement.outerHeight(true)); + } + top += levelHeight; + rowHeight += levelHeight; + } + tdInner.height(rowHeight); + //bg.height(element.height()); // tdInner might have pushed the body down, so resize + //updateSize(); + } + + + + /******************************************* draggable *****************************************/ + + + // when event starts out IN TIMESLOTS + + function draggableSlotEvent(event, eventElement, timeElement) { + var origPosition, origMarginTop, + prevSlotDelta, slotDelta, + matrix; + eventElement.draggable({ + zIndex: 1001, + scroll: false, + grid: [dayWidth, slotHeight], + axis: dayCnt==1 ? 'y' : false, + cancel: '.ui-resizable-handle', + opacity: .5, + start: function(ev, ui) { + if ($.browser.msie) { + eventElement.find('span.fc-event-bg').hide(); + } + origPosition = eventElement.position(); + origMarginTop = parseInt(eventElement.css('margin-top')) || 0; + prevSlotDelta = 0; + matrix = new HoverMatrix(function(cell) { + if (event.hasTime) { + // event is an original slot-event + if (cell && cell.row == 0) { + // but needs to convert to temporary full-day-event + var topDiff = panel.offset().top - head.offset().top; + eventElement.css('margin-top', origMarginTop + topDiff) + .appendTo(head); + // TODO: bug in IE8 w/ above technique, draggable ends immediately + event.hasTime = false; + if (timeElement) { + timeElement.hide(); + } + eventElement.draggable('option', 'grid', null); + } + }else{ + // event is a temporary full-day-event + if (cell && cell.row == 1) { + // but needs to convert to original slot-event + eventElement.css('margin-top', origMarginTop) + .appendTo(panel); + event.hasTime = true; + if (timeElement) { + timeElement.css('display', ''); // show() was causing display=inline + } + eventElement.draggable('option', 'grid', [dayWidth, slotHeight]); + } + } + if (cell && cell.row == 0) { + showDayOverlay(cell); + }else{ + hideDayOverlay(); + } + }); + matrix.row(head.find('td')); + bg.find('td').each(function() { + matrix.col(this); + }); + matrix.row(body); + matrix.start(); + hideSimilarEvents(event, eventElement); + }, + drag: function(ev, ui) { + slotDelta = Math.round((ui.position.top - origPosition.top) / slotHeight); + if (slotDelta != prevSlotDelta) { + if (timeElement && event.hasTime) { + // update time header + var newStart = addMinutes(cloneDate(event.start), slotDelta * options.slotMinutes), + newEnd; + if (event.end) { + newEnd = addMinutes(cloneDate(event.end), slotDelta * options.slotMinutes); + } + timeElement.text(formatDates(newStart, newEnd, options.agendaEventTimeFormat)); + } + prevSlotDelta = slotDelta; + } + matrix.mouse(ev.pageX, ev.pageY); + }, + stop: function(ev, ui) { + if (event.hasTime) { + if (matrix.cell) { + // over slots + var dayDelta = Math.round((ui.position.left - origPosition.left) / dayWidth); + reportEventMove(event, dayDelta, true, slotDelta * options.slotMinutes); + } + }else{ + // over full-days + if (!matrix.cell) { + // was being dragged over full-days, but finished over nothing, reset + event.hasTime = true; + }else{ + event.end = null; + reportEventMove(event, matrix.cell.colDelta); + } + } + hideDayOverlay(); + rerenderEvents(); + } + }); + + } + + + // when event starts out FULL-DAY + + function draggableDayEvent(event, eventElement) { + var origWidth, matrix; + eventElement.draggable({ + zIndex: 1001, + start: function() { + origWidth = eventElement.width(); + matrix = new HoverMatrix(function(cell) { + if (!cell) { + // mouse is outside of everything + hideDayOverlay(); + }else{ + if (cell.row == 0) { + // on full-days + if (event.hasTime) { + // and needs to be original full-day event + eventElement + .width(origWidth) + .height('') + .draggable('option', 'grid', null); + event.hasTime = false; + } + showDayOverlay(cell); + }else{ + // mouse is over bottom slots + if (!event.hasTime) { + // convert event to temporary slot-event + //if (+cloneDate(event.start, true) == +cloneDate(event._end, true)) { + // only change styles if a 1-day event + eventElement + .width(dayWidth - 10) // don't use entire width + .height(slotHeight * Math.round(options.defaultEventMinutes/options.slotMinutes) - 2); + //} + eventElement.draggable('option', 'grid', [dayWidth, 1]); + event.hasTime = true; + } + hideDayOverlay(); + } + } + }); + matrix.row(head.find('td')); + bg.find('td').each(function() { + matrix.col(this); + }); + matrix.row(body); + matrix.start(); + hideSimilarEvents(event, eventElement); + }, + drag: function(ev, ui) { + matrix.mouse(ev.pageX, ev.pageY); + }, + stop: function() { + var cell = matrix.cell; + if (!cell) { + // over nothing + if (event.hasTime) { + // event was on the slots before going out, convert back + event.hasTime = false; + } + }else{ + if (!event.hasTime) { + // event has been dropped on a full-day + reportEventMove(event, cell.colDelta); + }else{ + // event has been dropped on the slots + var slots = Math.floor((eventElement.offset().top - panel.offset().top) / slotHeight); + event.end = null; + reportEventMove(event, cell.colDelta, false, slots * options.slotMinutes); + } + } + hideDayOverlay(); + rerenderEvents(); + } + }); + } + + + // hover effect when dragging events over top days + + var dayOverlay; + + function showDayOverlay(props) { + if (!dayOverlay) { + dayOverlay = $("