diff options
author | James Lal <james@lightsofapollo.com> | 2012-06-27 16:58:05 +0200 |
---|---|---|
committer | James Lal <james@lightsofapollo.com> | 2012-06-27 16:58:05 +0200 |
commit | 5e731c14d9fca4e99ac73f020d69008431ef4f81 (patch) | |
tree | 297270d9a71a242ae368f2e4303fbcb63a50ed23 | |
parent | 24932d09294ab7a80b9c00ff505bc2823f1a65d1 (diff) | |
download | jsCalDAV-5e731c14d9fca4e99ac73f020d69008431ef4f81.tar.gz |
working calendar queries
-rw-r--r-- | lib/webcals/ical.js (renamed from lib/webcals/ics.js) | 43 | ||||
-rw-r--r-- | lib/webcals/request/calendar_query.js | 23 | ||||
-rw-r--r-- | lib/webcals/sax/dav_response.js | 21 | ||||
-rw-r--r-- | lib/webcals/templates/calendar_data.js | 6 | ||||
-rw-r--r-- | lib/webcals/templates/calendar_filter.js | 4 | ||||
-rw-r--r-- | samples/xml/propget.xml | 3 | ||||
-rw-r--r-- | samples/xml/req_calendar_query.xml | 15 | ||||
-rw-r--r-- | test/webcals/ical_test.js (renamed from test/webcals/ics_test.js) | 2 | ||||
-rw-r--r-- | test/webcals/request/calendar_query_test.js | 23 | ||||
-rw-r--r-- | test/webcals/request/propfind_test.js | 2 | ||||
-rw-r--r-- | test/webcals/sax/dav_response_test.js | 2 | ||||
-rw-r--r-- | test/webcals/templates/calendar_data_test.js | 9 | ||||
-rw-r--r-- | test/webcals/templates/calendar_filter_test.js | 8 |
13 files changed, 114 insertions, 47 deletions
diff --git a/lib/webcals/ics.js b/lib/webcals/ical.js index 91f549b..45cef83 100644 --- a/lib/webcals/ics.js +++ b/lib/webcals/ical.js @@ -1,29 +1,21 @@ -/** -@namespace -*/ (function(module, ns) { + // Credit: Andreas Gal - I removed the callback / xhr logic // Iterate over all entries if x is an array, otherwise just call fn on x. - function ForAll(x, fn) { - if (!(x instanceof Array)) { - fn(x); - return; - } - for (var n = 0; n < x.length; ++n) - fn(x[n]); - } /* Pattern for an individual entry: name:value */ var ENTRY = /^([A-Za-z0-9-]+)((?:;[A-Za-z0-9-]+=(?:"[^"]+"|[^";:,]+)(?:,(?:"[^"]+"|[^";:,]+))*)*):(.*)$/; + /* Pattern for an individual parameter: name=value[,value] */ var PARAM = /;([A-Za-z0-9-]+)=((?:"[^"]+"|[^";:,]+)(?:,(?:"[^"]+"|[^";:,]+))*)/g; + /* Pattern for an individual parameter value: value | "value" */ var PARAM_VALUE = /,?("[^"]+"|[^";:,]+)/g; // Parse a calendar in iCal format. - function ParseICal (text, success, error) { + function ParseICal(text) { // Parse the text into an object graph - var lines = text.replace('\r', '').split('\n'); + var lines = text.replace(/\r/g, '').split('\n'); var tos = Object.create(null); var stack = [tos]; @@ -64,15 +56,18 @@ var line = lines[n]; // check whether the line continues (next line stats with space or tab) var nextLine; - while ((nextLine = lines[n+1]) && (nextLine[0] == ' ' || nextLine[0] == '\t')) { + while ((nextLine = lines[n+1]) && (nextLine[0] === ' ' || nextLine[0] === '\t')) { line += nextLine.substr(1); ++n; continue; } // parse the entry, format is 'PROPERTY:VALUE' var matches = ENTRY.exec(line); - if (!matches) - return error('invalid format'); + + if (!matches) { + throw new Error('invalid format'); + } + var prop = matches[1].toLowerCase(); var params = matches[2]; var value = matches[3]; @@ -87,9 +82,11 @@ tos = stack[stack.length - 1]; if (stack.length == 1) { var cal = stack[0]; - if (typeof cal.vcalendar != 'object' || cal.vcalendar instanceof Array) - return error('single vcalendar object expected'); - return success(cal.vcalendar); + if (typeof cal.vcalendar !== 'object' || cal.vcalendar instanceof Array) { + throw new Error('single vcalendar object expected'); + } + + return cal.vcalendar; } break; default: @@ -97,7 +94,7 @@ break; } } - return error('unexpected end of file'); + throw new Error('unexpected end of file'); } function Value(v) { @@ -113,7 +110,7 @@ // Parse a time specification. function ParseDateTime(v) { var dt = Value(v); - if (Parameter(v, 'VALUE') == 'DATE') { + if (Parameter(v, 'VALUE') === 'DATE') { // 20081202 return new Date(dt.substr(0, 4), dt.substr(4, 2), dt.substr(6, 2)); } @@ -125,8 +122,9 @@ var hour = dt.substr(9, 2); var min = dt.substr(11, 2); var sec = dt.substr(13, 2); - if (dt[15] == 'Z') + if (dt[15] == 'Z') { return new Date(Date.UTC(year, month, day, hour, min, sec)); + } return new Date(year, month, day, hour, min, sec); } @@ -138,3 +136,4 @@ [Webcals('ics'), Webcals] : [module, require('./webcals')] )); + diff --git a/lib/webcals/request/calendar_query.js b/lib/webcals/request/calendar_query.js index b39853f..b9a350d 100644 --- a/lib/webcals/request/calendar_query.js +++ b/lib/webcals/request/calendar_query.js @@ -2,6 +2,7 @@ var Propfind = ns.require('request/propfind'); var CalendarData = ns.require('templates/calendar_data'); + var CalendarFilter = ns.require('templates/calendar_filter'); /** * Creates a calendar query request. @@ -17,16 +18,30 @@ this.xhr.headers['Depth'] = this.depth || 1; this.xhr.method = 'REPORT'; this.fields = new CalendarData(); - this.template.rootTag = 'calendar-query'; + this.filters = new CalendarFilter(); + + this.template.rootTag = ['caldav', 'calendar-query']; } CalendarQuery.prototype = { __proto__: Propfind.prototype, _createPayload: function() { - var props = this._props.join(''); - props += this.fields.render(this.template); - var content = this.template.tag('prop', props); + var content; + var props; + + props = this._props.join(''); + + if (this.fields) { + props += this.fields.render(this.template); + } + + content = this.template.tag('prop', props); + + if (this.filters) { + content += this.filters.render(this.template); + } + return this.template.render(content); } diff --git a/lib/webcals/sax/dav_response.js b/lib/webcals/sax/dav_response.js index 638951e..26d3453 100644 --- a/lib/webcals/sax/dav_response.js +++ b/lib/webcals/sax/dav_response.js @@ -3,6 +3,7 @@ var HTTP_STATUS = /([0-9]{3,3})/; var Base = ns.require('sax/base'); + var ParseICal = ns.require('ical'); var TextHandler = Base.create({ name: 'text', @@ -21,6 +22,24 @@ } }); + var CalendarDataHandler = Base.create({ + name: 'calendar data', + + //don't add text only elements + //to the stack as objects + onopentag: null, + onclosetag: null, + + //add the value to the parent + //value where key is local tag name + //and value is the text. + ontext: function(data) { + var handler = this.handler; + this.current[this.currentTag[handler.tagField]] = ParseICal(data); + } + }); + + var HrefHandler = Base.create({ name: 'href', @@ -108,6 +127,7 @@ 'DAV:/status': HttpStatusHandler, 'DAV:/resourcetype': ArrayHandler, 'DAV:/principal-URL': HrefHandler, + 'urn:ietf:params:xml:ns:caldav/calendar-data': CalendarDataHandler, 'DAV:/value': TextHandler, 'urn:ietf:params:xml:ns:caldav/calendar-home-set': HrefHandler, 'urn:ietf:params:xml:ns:caldav/calendar-user-address-set': HrefHandler @@ -186,4 +206,3 @@ [Webcals('sax/dav_response'), Webcals] : [module, require('../webcals')] )); - diff --git a/lib/webcals/templates/calendar_data.js b/lib/webcals/templates/calendar_data.js index a0c0938..a652303 100644 --- a/lib/webcals/templates/calendar_data.js +++ b/lib/webcals/templates/calendar_data.js @@ -1,6 +1,7 @@ (function(module, ns) { function CalendarData() { + this._hasItems = false; this.struct = {}; } @@ -18,6 +19,7 @@ */ select: function(type, list) { var struct = this.struct; + this._hasItems = true; if (!(type in struct)) { struct[type] = []; @@ -76,6 +78,10 @@ * @return {String} <calendardata /> xml output. */ render: function(template) { + if (!this._hasItems) { + return template.tag(['caldav', this.rootName]); + } + var struct = this.struct; var output = template.tag( ['caldav', this.rootName], diff --git a/lib/webcals/templates/calendar_filter.js b/lib/webcals/templates/calendar_filter.js index 15cfc8c..05bd742 100644 --- a/lib/webcals/templates/calendar_filter.js +++ b/lib/webcals/templates/calendar_filter.js @@ -10,7 +10,7 @@ __proto__: CalendarData.prototype, - filter: CalendarData.prototype.select, + add: CalendarData.prototype.select, compName: 'comp-filter', rootName: 'filter' @@ -21,7 +21,7 @@ }.apply( this, (this.Webcals) ? - [Webcals('templates/calendar_data'), Webcals] : + [Webcals('templates/calendar_filter'), Webcals] : [module, require('../webcals')] )); diff --git a/samples/xml/propget.xml b/samples/xml/propget.xml index c8487b2..2db1aa2 100644 --- a/samples/xml/propget.xml +++ b/samples/xml/propget.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<D:multistatus xmlns:D="DAV:"> +<D:multistatus xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav"> <D:response> @@ -40,5 +40,4 @@ </D:propstat> </D:response> - </D:multistatus> diff --git a/samples/xml/req_calendar_query.xml b/samples/xml/req_calendar_query.xml new file mode 100644 index 0000000..e983706 --- /dev/null +++ b/samples/xml/req_calendar_query.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" ?> +<C:calendar-query xmlns:D="DAV:" + xmlns:C="urn:ietf:params:xml:ns:caldav"> + <D:prop> + <D:getetag /> + </D:prop> + <C:filter> + <C:comp-filter name="VCALENDAR"> + <C:comp-filter name="VEVENT"> + <C:time-range start="20120626T125608Z" + end="20120926T125608Z"/> + </C:comp-filter> + </C:comp-filter> + </C:filter> +</C:calendar-query> diff --git a/test/webcals/ics_test.js b/test/webcals/ical_test.js index af1192e..ed9af4a 100644 --- a/test/webcals/ics_test.js +++ b/test/webcals/ical_test.js @@ -1,5 +1,5 @@ var fs = require('fs'), - ics = requireLib('ics'), + ics = requireLib('ical'), data = fs.readFileSync(__dirname + '/../../data/test.data', 'utf8'); suite('webcals/ics', function() { diff --git a/test/webcals/request/calendar_query_test.js b/test/webcals/request/calendar_query_test.js index f960e15..b1a167d 100644 --- a/test/webcals/request/calendar_query_test.js +++ b/test/webcals/request/calendar_query_test.js @@ -2,11 +2,12 @@ requireRequest(); requireLib('request/propfind'); requireLib('request/calendar_query'); -suite('webcals/request/propfind', function() { +suite('webcals/request/calendar_query', function() { var Propfind, - CalendarData, FakeXhr, + CalendarData, CalendarQuery, + CalendarFilter, Xhr, Template, oldXhrClass, @@ -18,6 +19,7 @@ suite('webcals/request/propfind', function() { suiteSetup(function() { Propfind = Webcals.require('request/propfind'); CalendarData = Webcals.require('templates/calendar_data'); + CalendarFilter = Webcals.require('templates/calendar_filter'); CalendarQuery = Webcals.require('request/calendar_query'); SaxResponse = Webcals.require('sax/dav_response'); FakeXhr = Webcals.require('support/fake_xhr'); @@ -44,13 +46,15 @@ suite('webcals/request/propfind', function() { assert.equal(subject.xhr.method, 'REPORT'); assert.instanceOf(subject.fields, CalendarData); + assert.instanceOf(subject.filters, CalendarFilter); }); test('#_createPayload', function() { subject.prop('getetag'); subject.fields.select('VEVENT', ['NAME']); + subject.filters.add('VEVENT', true); - var tags = [ + var props = [ '<N0:getetag />', '<N1:calendar-data>', '<N1:comp name="VCALENDAR">', @@ -61,12 +65,19 @@ suite('webcals/request/propfind', function() { '</N1:calendar-data>' ].join(''); + var filter = [ + '<N1:comp-filter name="VCALENDAR">', + '<N1:comp-filter name="VEVENT" />', + '</N1:comp-filter>' + ].join(''); + var expected = [ subject.template.doctype, - '<N0:calendar-query xmlns:N0="DAV:" ', + '<N1:calendar-query xmlns:N0="DAV:" ', 'xmlns:N1="urn:ietf:params:xml:ns:caldav">', - '<N0:prop>', tags, '</N0:prop>', - '</N0:calendar-query>' + '<N0:prop>', props, '</N0:prop>', + '<N1:filter>', filter, '</N1:filter>', + '</N1:calendar-query>' ].join(''); assert.equal(subject._createPayload(), expected); diff --git a/test/webcals/request/propfind_test.js b/test/webcals/request/propfind_test.js index 6d2dbe7..1f152b0 100644 --- a/test/webcals/request/propfind_test.js +++ b/test/webcals/request/propfind_test.js @@ -72,8 +72,6 @@ suite('webcals/request/propfind', function() { var result = subject._createPayload(); - console.log(result); - assert.equal(subject._createPayload(), expected); }); diff --git a/test/webcals/sax/dav_response_test.js b/test/webcals/sax/dav_response_test.js index 1279b84..f91fcef 100644 --- a/test/webcals/sax/dav_response_test.js +++ b/test/webcals/sax/dav_response_test.js @@ -1,6 +1,7 @@ requireLib('sax'); requireLib('sax/base'); requireLib('sax/dav_response'); +requireLib('ical'); suite('webcals/sax/dav_response', function() { @@ -61,6 +62,7 @@ suite('webcals/sax/dav_response', function() { value: {} } } + }; test('output', function(done) { diff --git a/test/webcals/templates/calendar_data_test.js b/test/webcals/templates/calendar_data_test.js index 5223f18..1828b39 100644 --- a/test/webcals/templates/calendar_data_test.js +++ b/test/webcals/templates/calendar_data_test.js @@ -52,11 +52,16 @@ suite('webcals/templates/calendar_data', function() { '</N0:calendar-data>' ].join(''); - setup(function() { - select(); + test('without items', function() { + var output = subject.render(template); + assert.equal( + output, + '<N0:calendar-data />' + ); }); test('output', function() { + select(); var output = subject.render(template); assert.equal(output, expected); }); diff --git a/test/webcals/templates/calendar_filter_test.js b/test/webcals/templates/calendar_filter_test.js index 517e78a..d79883b 100644 --- a/test/webcals/templates/calendar_filter_test.js +++ b/test/webcals/templates/calendar_filter_test.js @@ -1,7 +1,8 @@ requireRequest(); requireLib('templates/calendar_data'); +requireLib('templates/calendar_filter'); -suite('webcals/templates/calendar_data', function() { +suite('webcals/templates/calendar_filter', function() { var CalendarFilter; var Template; @@ -9,7 +10,7 @@ suite('webcals/templates/calendar_data', function() { var template; function filter() { - subject.filter('VEVENT', true); + subject.add('VEVENT', true); } suiteSetup(function() { @@ -44,9 +45,6 @@ suite('webcals/templates/calendar_data', function() { test('output', function() { var output = subject.render(template); - console.log(); - console.log(output) - console.log(); assert.equal(output, expected); }); }); |