diff --git a/AppKit/CPViewController.j b/AppKit/CPViewController.j new file mode 100644 index 000000000..98ca200d4 --- /dev/null +++ b/AppKit/CPViewController.j @@ -0,0 +1,165 @@ +/* + * CPViewController.j + * AppKit + * + * Created by Nicholas Small and Francisco Tolmasky. + * Copyright 2009, 280 North, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +@import + + +/*! @class CPViewController + The CPViewController class provides the fundamental view-management controller for Cappuccino applications. + The basic view controller class supports the presentation of an associated view in addition to basic support + for managing modal views and, in the future, animations. Subclasses such as CPNavigationController and + CPTabBarController provide additional behavior for managing complex hierarchies of view controllers and views. + + You use each instance of CPViewController to manage a single view (and hierarchy). For a simple view controller, + this entails managing the view hierarchy responsible for presenting your application content. + A typical view hierarchy consists of a root viewÑa reference to which is available in the view property of this classÑ + and one or more subviews presenting the actual content. In the case of navigation and tab bar controllers, the view + controller manages not only the high-level view hierarchy (which provides the navigation controls) but also one + or more additional view controllers that handle the presentation of the application content. + + Unlike UIViewController in Cocoa Touch, a CPViewController does not represent an entire screen of content. You + will add your root view to an existing view or window's content view. You can manage many view controllers + on screen at once. CPViewController is also the preferred way of working with Cibs. + + Subclasses can override -loadView to create their custom view hierarchy, or specify a cib name to be loaded automatically. + It has methods that are called when a view appears or disappears. + This class is also a good place for delegate & datasource methods, and other controller stuff. +*/ +@implementation CPViewController : CPResponder +{ + CPView _view; + + id _representedObject @accessors(property=representedObject); + CPString _title @accessors(property=title); + + CPString _cibName @accessors(property=cibName, readonly); + CPBundle _cibBundle @accessors(property=cibBundle, readonly); + CPDictionary _cibExternalNameTable @accessors(property=cibExternalNameTable, readonly); +} + +/*! + Convenience initializer calls -initWithCibName:bundle: with nil for both parameters. +*/ +- (id)init +{ + return [self initWithCibName:nil bundle:nil]; +} + +- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil +{ + return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:nil]; +} + +- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil owner:(id)anOwner +{ + return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]]; +} + +/*! + The designated initializer. If you subclass CPViewController, you must call the super implementation of this method, even if you aren't using a Cib. + In the specified Cib, the File's Owner proxy should have its class set to your view controller subclass, with the view outlet connected to the main view. + If you pass in a nil Cib name, then you must either call -setView: before -view is invoked, or override -loadView to set up your views. + + @param cibNameOrNil The path to the cib to load for the root view or nil to programmatically create views. + @param cibBundleOrNil The bundle that the cib is located in or nil for the main bundle. +*/ +- (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil externalNameTable:(CPDictionary)anExternalNameTable +{ + self = [super init]; + + if (self) + { + // Don't load the cib until someone actually requests the view. The user may just be intending to use setView:. + _cibName = aCibNameOrNil; + _cibBundle = aCibBundleOrNil || [CPBundle mainBundle]; + _cibExternalNameTable = anExternalNameTable || [CPDictionary dictionaryWithObject:self forKey:CPCibOwner]; + } + + return self; +} + +/*! + Programmatically creates the view that the controller manages. + You should never call this method directly. The view controller calls this method when the view property is requested but is nil. + + If you create your views manually, you must override this method and use it to create your view and assign it to the view property. + The default implementation for programmatic views is to create a plain view. You can invoke super to utilize this view. + + If you use Interface Builder to create your views and initialize the view controllerÑthat is, you initialize the view using the + initWithCibName:bundle: methodÑthen you must not override this method. The consequences risk shattering the space-time continuum. + + Note: The cib loading system is currently asynchronous. +*/ +- (void)loadView +{ + if (_view) + return; + +// if (_cibName) +// [CPException raise: reason:]; + + var cib = [[CPCib alloc] initWithContentsOfURL:[_cibBundle pathForResource:_cibName + @".cib"]]; + + [cib instantiateCibWithExternalNameTable:_cibExternalNameTable]; +} + +/*! + Returns the view that the controller manages. + If this property is nil, the controller sends loadView to itself to create the view that it manages. + Subclasses should override the loadView method to create any custom views. The default value is nil. + + Note: An error will not be thrown if after -loadView, the view property is still nil. -view will simply return nil, but will continue to call -loadView on subsequent calls. +*/ +- (CPView)view +{ + if (!_view) + { + var cibOwner = [_cibExternalNameTable objectForKey:CPCibOwner]; + + if ([cibOwner respondsToSelector:@selector(viewControllerWillLoadCib:)]) + [cibOwner viewControllerWillLoadCib:self]; + + [self loadView]; + + if (_view === nil && [cibOwner isKindOfClass:[CPDocument class]]) + [self setView:[cibOwner valueForKey:@"view"]]; + + if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)]) + [cibOwner viewControllerDidLoadCib:self]; + } + + return _view; +} + + +/*! + Manually sets the view that the controller manages. + Setting to nil will cause -loadView to be called on all subsequent calls of -view. + + @param aView The view this controller should represent. +*/ +- (void)setView:(CPView)aView +{ + _view = aView; +} + +@end diff --git a/Foundation/CPWebDAVManager.j b/Foundation/CPWebDAVManager.j new file mode 100644 index 000000000..3e0d16b66 --- /dev/null +++ b/Foundation/CPWebDAVManager.j @@ -0,0 +1,214 @@ + +var setURLResourceValuesForKeysFromProperties = function(aURL, keys, properties) +{ + var resourceType = [properties objectForKey:@"resourcetype"]; + + if (resourceType === CPWebDAVManagerCollectionResourceType) + { + [aURL setResourceValue:YES forKey:CPURLIsDirectoryKey]; + [aURL setResourceValue:NO forKey:CPURLIsRegularFileKey]; + } + else if (resourceType === CPWebDAVManagerNonCollectionResourceType) + { + [aURL setResourceValue:NO forKey:CPURLIsDirectoryKey]; + [aURL setResourceValue:YES forKey:CPURLIsRegularFileKey]; + } + + var displayName = [properties objectForKey:@"displayname"]; + + if (displayName !== nil) + { + [aURL setResourceValue:displayName forKey:CPURLNameKey]; + [aURL setResourceValue:displayName forKey:CPURLLocalizedNameKey]; + } +} + +CPWebDAVManagerCollectionResourceType = 1; +CPWebDAVManagerNonCollectionResourceType = 0; + +@implementation CPWebDAVManager : CPObject +{ + CPDictionary _blocksForConnections; +} + +- (id)init +{ + self = [super init]; + + if (self) + _blocksForConnections = [CPDictionary dictionary]; + + return self; +} + +- (CPArray)contentsOfDirectoryAtURL:(CPURL)aURL includingPropertiesForKeys:(CPArray)keys options:(CPDirectoryEnumerationOptions)aMask block:(Function)aBlock +{ + var properties = [], + count = [keys count]; + + while (count--) + properties.push(WebDAVPropertiesForURLKeys[keys[count]]); + + var makeContents = function(aURL, response) + { + var contents = [], + URLString = nil, + URLStrings = [response keyEnumerator]; + + while (URLString = [URLStrings nextObject]) + { + var URL = [CPURL URLWithString:URLString], + properties = [response objectForKey:URLString]; + + // FIXME: We need better way of comparing URLs. + if (![[URL absoluteString] isEqual:[aURL absoluteString]]) + { + contents.push(URL); + + setURLResourceValuesForKeysFromProperties(URL, keys, properties); + } + } + + return contents; + } + + if (!aBlock) + return makeContents(aURL, response); + + [self PROPFIND:aURL properties:properties depth:1 block:function(aURL, response) + { + aBlock(aURL, makeContents(aURL, response)); + }]; +} + +- (CPDictionary)PROPFIND:(CPURL)aURL properties:(CPDictionary)properties depth:(CPString)aDepth block:(Function)aBlock +{ + var request = [CPURLRequest requestWithURL:aURL]; + + [request setHTTPMethod:@"PROPFIND"]; + [request setValue:aDepth forHTTPHeaderField:@"Depth"]; + + var HTTPBody = [""], + index = 0, + count = properties.length; + + for (; index < count; ++index) + HTTPBody.push(""); + + HTTPBody.push(""); + + [request setHTTPBody:HTTPBody.join("")]; + + if (!aBlock) + return parsePROPFINDResponse([[CPURLConnection sendSynchronousRequest:request returningResponse:nil error:nil] string]); + + else + { + var connection = [CPURLConnection connectionWithRequest:request delegate:self]; + + [_blocksForConnections setObject:aBlock forKey:[connection UID]]; + } +} + +- (void)connection:(CPURLConnection)aURLConnection didReceiveData:(CPString)aString +{ + var block = [_blocksForConnections objectForKey:[aURLConnection UID]]; + + // FIXME: get the request... + block([aURLConnection._request URL], parsePROPFINDResponse(aString)); +} + +@end + +var WebDAVPropertiesForURLKeys = { }; + +WebDAVPropertiesForURLKeys[CPURLNameKey] = @"displayname"; +WebDAVPropertiesForURLKeys[CPURLLocalizedNameKey] = @"displayname"; +WebDAVPropertiesForURLKeys[CPURLIsRegularFileKey] = @"resourcetype"; +WebDAVPropertiesForURLKeys[CPURLIsDirectoryKey] = @"resourcetype"; +//CPURLIsSymbolicLinkKey = @"CPURLIsSymbolicLinkKey"; +//CPURLIsVolumeKey = @"CPURLIsVolumeKey"; +//CPURLIsPackageKey = @"CPURLIsPackageKey"; +//CPURLIsSystemImmutableKey = @"CPURLIsSystemImmutableKey"; +//CPURLIsUserImmutableKey = @"CPURLIsUserImmutableKey"; +//CPURLIsHiddenKey = @"CPURLIsHiddenKey"; +//CPURLHasHiddenExtensionKey = @"CPURLHasHiddenExtensionKey"; +//CPURLCreationDateKey = @"CPURLCreationDateKey"; +//CPURLContentAccessDateKey = @"CPURLContentAccessDateKey"; +//CPURLContentModificationDateKey = @"CPURLContentModificationDateKey"; +//CPURLAttributeModificationDateKey = @"CPURLAttributeModificationDateKey"; +//CPURLLinkCountKey = @"CPURLLinkCountKey"; +//CPURLParentDirectoryURLKey = @"CPURLParentDirectoryURLKey"; +//CPURLVolumeURLKey = @"CPURLVolumeURLKey"; +//CPURLTypeIdentifierKey = @"CPURLTypeIdentifierKey"; +//CPURLLocalizedTypeDescriptionKey = @"CPURLLocalizedTypeDescriptionKey"; +//CPURLLabelNumberKey = @"CPURLLabelNumberKey"; +//CPURLLabelColorKey = @"CPURLLabelColorKey"; +//CPURLLocalizedLabelKey = @"CPURLLocalizedLabelKey"; +//CPURLEffectiveIconKey = @"CPURLEffectiveIconKey"; +//CPURLCustomIconKey = @"CPURLCustomIconKey"; + +var XMLDocumentFromString = function(anXMLString) +{//console.log(anXMLString); + if (typeof window["ActiveXObject"] !== "undefined") + { + var XMLDocument = new ActiveXObject("Microsoft.XMLDOM"); + + XMLDocument.async = false; + XMLDocument.loadXML(anXMLString); + + return XMLDocument; + } + + return new DOMParser().parseFromString(anXMLString,"text/xml"); +} + +var parsePROPFINDResponse = function(anXMLString) +{ + var XMLDocument = XMLDocumentFromString(anXMLString), + responses = XMLDocument.getElementsByTagNameNS("*", "response"), + responseIndex = 0, + responseCount = responses.length; + + var propertiesForURLs = [CPDictionary dictionary]; + + for (; responseIndex < responseCount; ++responseIndex) + { + var response = responses[responseIndex], + elements = response.getElementsByTagNameNS("*", "prop").item(0).childNodes, + index = 0, + count = elements.length, + properties = [CPDictionary dictionary]; + + for (; index < count; ++index) + { + var element = elements[index]; + + if (element.nodeType === 8 || element.nodeType === 3) + continue; + + var nodeName = element.nodeName, + colonIndex = nodeName.lastIndexOf(':'); + + if (colonIndex > -1) + nodeName = nodeName.substr(colonIndex + 1); + + if (nodeName === @"resourcetype") + [properties setObject:element.firstChild ? CPWebDAVManagerCollectionResourceType : CPWebDAVManagerNonCollectionResourceType forKey:nodeName]; + + else + [properties setObject:element.firstChild.nodeValue forKey:nodeName]; + } + + var href = response.getElementsByTagNameNS("*", "href").item(0); + + [propertiesForURLs setObject:properties forKey:href.firstChild.nodeValue]; + } + + return propertiesForURLs; +} + +var mapURLsAndProperties = function(/*CPDictionary*/ properties, /*CPURL*/ ignoredURL) +{ + +}