?
+// # how to handle closure parameters?
+
+class EnvDescriptor {
+ String name
+ String version
+}
+
+class GaelykDescriptor {
+ String version
+}
+
+class AppDescriptor {
+ String id
+ String version
+ EnvDescriptor env
+ GaelykDescriptor gaelyk
+
+}
+
+def shortcutFor = { type ->
+ bind(clazz: currentType(type)) & enclosingScript(sourceFolderOfCurrentType('war/WEB-INF/groovy'))
+}
+
+(sourceFolderOfCurrentType('war/WEB-INF/groovy') & enclosingScript()).accept {
+ provider = 'Gaelyk Groovlet'
+ property name: 'datastore',
+ type: 'com.google.appengine.api.datastore.DatastoreService'
+
+ property name: 'request', type: 'javax.servlet.http.HttpServletRequest', doc: 'the HttpServletRequest object'
+ property name: 'response', type: 'javax.servlet.http.HttpServletResponse', doc: 'the HttpServletResponse object'
+ property name: 'context', type: 'javax.servlet.ServletContext', doc: 'the ServletContext object'
+ property name: 'application', type: 'javax.servlet.ServletContext', doc: 'same as context'
+ property name: 'session', type: 'javax.servlet.http.HttpSession', doc: 'shorthand for request.getSession(false) (can be null) which returns an HttpSession'
+ property name: 'params', type: Map, doc: 'map of all form parameters (can be empty)'
+ property name: 'headers', type: Map, doc: 'map of all request header fields'
+ property name: 'log', type: 'groovyx.gaelyk.logging.GroovyLogger', doc: 'a Groovy logger is available for logging messages through java.util.logging'
+
+ property name: 'out', type: PrintWriter, doc:'shorthand for response.getWriter() which returns a PrintWriter'
+ property name: 'sout', type: 'javax.servlet.ServletOutputStream', doc: 'shorthand for response.getOutputStream() which returns a ServletOutputStream'
+ property name: 'html', type: 'groovy.xml.MarkupBuilder', doc: 'shorthand for new MarkupBuilder(response.getWriter()) which returns a MarkupBuilder'
+
+ method name: 'forward', type: 'void', params: [path: 'java.lang.String'], doc: "forwards to given url, groovlet or template"
+ method name: 'include', type: 'void', params: [path: 'java.lang.String'], doc: "includes given template"
+ method name: 'redirect', type: 'void', params: [path: 'java.lang.String'], doc: "redirects to given url, groovlet or template"
+}
+
+((sourceFolderOfCurrentType('war/WEB-INF/groovy') & enclosingScript()) | annotatedBy('groovyx.gaelyk.GaelykBindings')).accept {
+ property name: 'logger', type: Map, doc: 'a logger accessor can be used to get access to any logger'
+ property name: 'datastore', type: 'com.google.appengine.api.datastore.DatastoreService', doc: 'the Datastore service'
+ property name: 'memcache', type: 'com.google.appengine.api.memcache.MemcacheService', doc: 'the Memcache service'
+ property name: 'urlFetch', type: 'com.google.appengine.api.urlfetch.URLFetchService', doc: 'the URL Fetch service'
+ property name: 'mail', type: 'com.google.appengine.api.mail.MailService', doc: 'the Mail service'
+
+ property name: 'images', type: 'groovyx.gaelyk.ImagesServiceWrapper', doc: 'the Images service (actually a convenient wrapper class combining both the methods of ImagesService and ImagesServiceFactory and implementing the ImagesService interface)'
+ property name: 'users', type: 'com.google.appengine.api.users.UserService', doc: 'the User service'
+ property name: 'user', type: 'com.google.appengine.api.users.User', doc: 'the currently logged in user (null if no user logged in)'
+ property name: 'defaultQueue', type: 'com.google.appengine.api.taskqueue.Queue', doc: 'the default queue'
+ property name: 'queues', type: Map, doc: 'a map-like object with which you can access the configured queues'
+ property name: 'xmpp', type: 'com.google.appengine.api.xmpp.XMPPService', doc: 'the Jabber/XMPP service.'
+ property name: 'blobstore', type: 'com.google.appengine.api.blobstore.BlobstoreService', doc: 'the Blobstore service.'
+ property name: 'oauth', type: 'com.google.appengine.api.oauth.OAuthService', doc: 'the OAuth service.'
+ property name: 'namespace', type: Class, doc: 'the Namespace manager'
+ property name: 'capabilities', type: 'com.google.appengine.api.capabilities.CapabilitiesService', doc: 'the Capabilities service'
+ property name: 'channel', type: 'com.google.appengine.api.channel.ChannelService', doc: 'the Channel service'
+ property name: 'files', type: 'com.google.appengine.api.files.FileService', doc: 'the File service'
+ property name: 'backends', type: 'com.google.appengine.api.backends.BackendService', doc: 'the Backend service'
+ property name: 'lifecycle', type: 'com.google.appengine.api.LifecycleManager', doc: 'the Lifecycle manager'
+ property name: 'localMode', type: Boolean, doc: 'a boolean variable which is true when the application is running in local development mode, and false when deployed on Google\'s cloud.'
+
+ property name: 'app', type: AppDescriptor, doc: 'map of all form parameters (can be empty)'
+}
+
+(sourceFolderOfCurrentFile('war/WEB-INF') & name("routes") & enclosingScript()).accept {
+ provider = 'Gaelyk'
+ method name: 'all',
+ useNamedArgs: true,
+ type: List,
+ params: [route: String, forward: String, redirect: String, validate: Closure, cache: int, ignore: boolean, namespace: String],
+ doc: 'Routes all HTTP methods.'
+ method name: 'get',
+ useNamedArgs: true,
+ type: List,
+ params: [route: String, forward: String, redirect: String, validate: Closure, cache: int, ignore: boolean, namespace: String],
+ doc: 'Routes GET HTTP method.'
+ method name: 'post',
+ useNamedArgs: true,
+ type: List,
+ params: [route: String, forward: String, redirect: String, validate: Closure, cache: int, ignore: boolean, namespace: String],
+ doc: 'Routes POST HTTP method.'
+ method name: 'put',
+ useNamedArgs: true,
+ type: List,
+ params: [route: String, forward: String, redirect: String, validate: Closure, cache: int, ignore: boolean, namespace: String],
+ doc: 'Routes PUT HTTP method.'
+ method name: 'delete',
+ useNamedArgs: true,
+ type: List,
+ params: [route: String, forward: String, redirect: String, validate: Closure, cache: int, ignore: boolean, namespace: String],
+ doc: 'Routes DELETE HTTP method.'
+ method name: 'email',
+ useNamedArgs: true,
+ type: List,
+ params: [to: String],
+ doc: 'Routes incoming email messages.'
+ method name: 'jabber',
+ useNamedArgs: true,
+ type: List,
+ params: [type: String, to: String],
+ doc: 'Routes incomming jabber messages and statuses.'
+}
+
+
+shortcutFor('com.google.appengine.api.taskqueue.Queue').accept {
+ provider = 'Gaelyk'
+ method name: 'add',
+ useNamedArgs: true,
+ type: 'com.google.appengine.api.taskqueue.TaskHandle',
+ params: [coundDownMillis: long, etaMillis: long, headers: Map, method: 'com.google.appengine.api.taskqueue.TaskOptions.Method', params: Map, payload: Object, taskName: String, url: String, retryOption: 'com.google.appengine.api.taskqueue.RetryOptions'],
+ doc: 'Add a new task on the queue using a map for holding the task attributes instead of a TaskOptions builder object.'
+ method name: 'getName',
+ type: 'java.lang.String',
+ doc: 'Shorcut to get the name of the Queue.'
+ property name: 'name',
+ type: 'java.lang.String',
+ doc: 'Shorcut to get the name of the Queue.'
+ method name: 'leftShift',
+ useNamedArgs: true,
+ type: 'com.google.appengine.api.taskqueue.TaskHandle',
+ params: [coundDownMillis: long, etaMillis: long, headers: Map, method: 'com.google.appengine.api.taskqueue.TaskOptions.Method', params: Map, payload: Object, taskName: String, url: String, retryOption: 'com.google.appengine.api.taskqueue.RetryOptions'],
+ doc: 'Add a new task on the queue using a map for holding the task attributes instead of a TaskOptions builder object.'
+}
+
+
+shortcutFor('com.google.appengine.api.datastore.Entity').accept {
+ provider = 'Gaelyk'
+
+ method name: 'get',
+ type: 'java.lang.Object',
+ params: [name: 'java.lang.String'],
+ doc: 'Provides a shortcut notation to get a property of an entity.'
+
+ method name: 'save',
+ type: 'com.google.appengine.api.datastore.Key',
+ doc: 'Save this entity in the data store.'
+
+ method name: 'set',
+ type: 'void',
+ params: [name: 'java.lang.String', property: 'java.lang.Object'],
+ doc: 'Provides a shortcut notation to set a property of an entity.'
+
+ method name: 'delete',
+ type: 'void',
+ doc: 'Delete this entity from the data store.'
+
+ method name: 'leftShift',
+ type: 'com.google.appengine.api.datastore.Entity',
+ params: [params: 'java.util.Map'],
+ doc: '''Set the Entity
properties with the key / value pairs of the map,
+using the leftshift operator as follows:
+entity << params
'''
+
+ method name: 'getAt',
+ type: 'java.lang.Object',
+ params: [name: 'java.lang.String'],
+ doc: 'Provides a shortcut notation to get a property of an entity.'
+
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [type: 'java.lang.Class'],
+ doc: '''Gaelyk supports a simplistic object/entity mapping, thanks to type coercion.
+ You can use this type coercion mechanism to coerce POJOs/POGOs and datastore Entities.
+ The Entity
kind will be the simple name of the POJO/POGO (same approach as Objectify).
+ So with this mechanism, you can do:
+
+
+class Person { String name, int age }
+
+ def e = new Entity("Person")
+ e.name = "Guillaume"
+ e.age = 33
+
+ def p = e as Person
+
+ assert e.name == p.name
+ assert e.age == p.age
+
'''
+
+ method name: 'setAt',
+ type: 'void',
+ params: [name: 'java.lang.String', property: 'java.lang.Object'],
+ doc: 'Provides a shortcut notation to set a property of an entity.'
+
+ method name: 'asyncSave',
+ type: 'java.util.concurrent.Future',
+ doc: 'Save this entity in the data store asynchronously.'
+
+ method name: 'asyncDelete',
+ type: 'java.util.concurrent.Future',
+ doc: 'Delete this entity from the data store.'
+}
+
+shortcutFor('com.google.appengine.api.memcache.MemcacheService').accept {
+ provider = 'Gaelyk'
+ method name: 'get',
+ type: 'java.lang.Object',
+ params: [key: 'java.lang.String'],
+ doc: 'Get an object from the cache with a String key'
+
+ method name: 'get',
+ type: 'java.lang.Object',
+ params: [key: 'groovy.lang.GString'],
+ doc: 'Get an object from the cache, with a GString key, coerced to a String.'
+
+ method name: 'put',
+ type: 'void',
+ params: [key: 'groovy.lang.GString', value: 'java.lang.Object'],
+ doc: 'Put an object in the cache under a GString key, coerced to a String.'
+
+ method name: 'put',
+ type: 'void',
+ params: [key: 'groovy.lang.GString', value: 'java.lang.Object', expiration: 'com.google.appengine.api.memcache.Expiration'],
+ doc: 'Put an object in the cache under a GString key, coerced to a String, with an expiration.'
+
+ method name: 'put',
+ type: 'void',
+ params: [key: 'groovy.lang.GString', value: 'java.lang.Object', expiration: 'com.google.appengine.api.memcache.Expiration', policy: 'com.google.appengine.api.memcache.MemcacheService$SetPolicy'],
+ doc: 'Put an object in the cache under a GString key, coerced to a String, with an expiration and a SetPolicy.'
+
+ method name: 'set',
+ type: 'void',
+ params: [key: 'java.lang.String', value: 'java.lang.Object'],
+ doc: 'Put an object in the cache under a String key.'
+
+ method name: 'isCase',
+ type: 'boolean',
+ params: [key: 'java.lang.Object'],
+ doc: '''Shortcut to check whether a key is contained in the cache using the in
operator:
+key in memcache
'''
+
+ method name: 'memoize',
+ type: 'groovy.lang.Closure',
+ params: [closure: 'groovy.lang.Closure'],
+ doc: '''Memoize a closure invocation in memcache.
+Closure call result are stored in memcache, retaining the closure hashCode and the argument values as key.
+The results are kept in memcache only up to the 30 seconds request time limit of Google App Engine.
+
+
+def countEntities = memcache.memoize { String kind -> datastore.prepare( new Query(kind) ).countEntities() }
+def totalPhotos = countEntities('photos')
+
+'''
+
+ method name: 'getAt',
+ type: 'java.lang.Object',
+ params: [key: 'java.lang.Object'],
+ doc: '''Get an object from the cache, identified by its key, using the subscript notation:
+def obj = memcache[key]
'''
+
+ method name: 'getAt',
+ type: 'java.lang.Object',
+ params: [key: 'java.lang.String'],
+ doc: '''Get an object from the cache, identified by its key, using the subscript notation:
+def obj = memcache[key]
'''
+
+ method name: 'putAt',
+ type: 'void',
+ params: [key: 'java.lang.String', value: 'java.lang.Object'],
+ doc: '''Put an object into the cache, identified by its key, using the subscript notation:
+memcache[key] = value
'''
+
+ method name: 'putAt',
+ type: 'void',
+ params: [key: 'java.lang.Object', value: 'java.lang.Object'],
+ doc: '''Put an object into the cache, identified by its key, using the subscript notation:
+memcache[key] = value
'''
+
+ method name: 'clearCacheForUri',
+ type: 'java.util.Set',
+ params: [uri: 'java.lang.String'],
+ doc: 'Clear the cached content for a given URI.'
+
+}
+
+
+shortcutFor('java.net.URL').accept {
+ provider = 'Gaelyk'
+ method name: 'get',
+ type: 'java.lang.Object',
+ useNamedArgs: true,
+ params: [allowTruncate: boolean, followRedirects: boolean, deadline: double, headers: Map, payload: byte[], params: Map, async: boolean ],
+ doc: 'Use the URLFetch Service to do a GET on the URL.'
+
+ method name: 'get',
+ type: 'java.lang.Object',
+ doc: 'Use the URLFetch Service to do a GET on the URL.'
+
+ method name: 'put',
+ type: 'java.lang.Object',
+ useNamedArgs: true,
+ params: [allowTruncate: boolean, followRedirects: boolean, deadline: double, headers: Map, payload: byte[], params: Map, async: boolean ],
+ doc: 'Use the URLFetch Service to do a PUT on the URL.'
+
+ method name: 'put',
+ type: 'java.lang.Object',
+ doc: 'Use the URLFetch Service to do a PUT on the URL.'
+
+ method name: 'delete',
+ type: 'java.lang.Object',
+ useNamedArgs: true,
+ params: [allowTruncate: boolean, followRedirects: boolean, deadline: double, headers: Map, payload: byte[], params: Map, async: boolean ],
+ doc: 'Use the URLFetch Service to do a DELETE on the URL.'
+
+ method name: 'delete',
+ type: 'java.lang.Object',
+ doc: 'Use the URLFetch Service to do a DELETE on the URL.'
+
+ method name: 'head',
+ type: 'java.lang.Object',
+ useNamedArgs: true,
+ params: [allowTruncate: boolean, followRedirects: boolean, deadline: double, headers: Map, payload: byte[], params: Map, async: boolean ],
+ doc: 'Use the URLFetch Service to do a HEAD on the URL.'
+
+ method name: 'head',
+ type: 'java.lang.Object',
+ doc: 'Use the URLFetch Service to do a HEAD on the URL.'
+
+ method name: 'asType',
+ type: 'com.google.appengine.api.datastore.Link',
+ params: [arg1: 'java.lang.Class'],
+ doc: 'Converter method for converting a URL into a Link instance.'
+
+ method name: 'post',
+ type: 'java.lang.Object',
+ useNamedArgs: true,
+ params: [allowTruncate: boolean, followRedirects: boolean, deadline: double, headers: Map, payload: byte[], params: Map, async: boolean ],
+ doc: 'Use the URLFetch Service to do a POST on the URL.'
+
+ method name: 'post',
+ type: 'java.lang.Object',
+ doc: 'Use the URLFetch Service to do a POST on the URL.'
+
+}
+
+shortcutFor('com.google.appengine.api.datastore.DatastoreService').accept {
+ provider = 'Gaelyk'
+ method name: 'getProperty',
+ type: 'com.google.appengine.api.datastore.Entity',
+ params: [kind: 'java.lang.String', property: 'java.lang.String'],
+ doc: 'Gets datastore kind property.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [options: 'com.google.appengine.api.datastore.FetchOptions'],
+ doc: 'Gets all datastore kinds and their properties.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [options: 'com.google.appengine.api.datastore.FetchOptions', closure: 'groovy.lang.Closure'],
+ doc: 'Gets all datastore kinds and their properties. The closure lets you apply additional filters to your query.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [kind: 'java.lang.String', options: 'com.google.appengine.api.datastore.FetchOptions'],
+ doc: 'Gets datastore kind properties.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [kind: 'java.lang.String', options: 'com.google.appengine.api.datastore.FetchOptions', closure: 'groovy.lang.Closure'],
+ doc: 'Gets datastore kind properties. The closure lets you apply additional filters to your query.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ doc: 'Gets all datastore kinds and their properties.'
+
+ property name: 'properties',
+ type: 'java.util.List',
+ doc: 'Gets all datastore kinds and their properties.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [closure: 'groovy.lang.Closure'],
+ doc: 'Gets all datastore kinds and their properties. The closure lets you apply additional filters to your query.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [kind: 'java.lang.String'],
+ doc: 'Gets datastore kind properties.'
+
+ method name: 'getProperties',
+ type: 'java.util.List',
+ params: [kind: 'java.lang.String', closure: 'groovy.lang.Closure'],
+ doc: 'Gets datastore kind properties. The closure lets you apply additional filters to your query.'
+
+ method name: 'withTransaction',
+ type: 'com.google.appengine.api.datastore.Transaction',
+ params: [closure: 'groovy.lang.Closure'],
+ doc: '''With this method, transaction handling is done transparently.
+The transaction is committed if the closure executed properly.
+The transaction is rollbacked if anything went wrong.
+You can use this method as follows:
+
+datastore.withTransaction { transaction ->
+ // do something in that transaction
+}
+
'''
+
+ method name: 'getAsync',
+ type: 'com.google.appengine.api.datastore.AsyncDatastoreService',
+ doc: 'The asynchronous datastore service.'
+
+ property name: 'async',
+ type: 'com.google.appengine.api.datastore.AsyncDatastoreService',
+ doc: 'The asynchronous datastore service.'
+
+ method name: 'getNamespaces',
+ type: 'java.util.List',
+ params: [options: 'com.google.appengine.api.datastore.FetchOptions'],
+ doc: 'Gets datastore namespaces.'
+
+ method name: 'getNamespaces',
+ type: 'java.util.List',
+ params: [options: 'com.google.appengine.api.datastore.FetchOptions', clsoure: 'groovy.lang.Closure'],
+ doc: 'Gets datastore namespaces. The closure lets you apply additional filters to your query.'
+
+ method name: 'getNamespaces',
+ type: 'java.util.List',
+ doc: 'Gets datastore namespaces.'
+
+ property name: 'namespaces',
+ type: 'java.util.List',
+ doc: 'Gets datastore namespaces.'
+
+ method name: 'getNamespaces',
+ type: 'java.util.List',
+ params: [arg1: 'groovy.lang.Closure'],
+ doc: 'Gets datastore namespaces. The closure lets you apply additional filters to your query.'
+
+ method name: 'getKinds',
+ type: 'java.util.List',
+ params: [options: 'com.google.appengine.api.datastore.FetchOptions'],
+ doc: 'Gets datastore kinds.'
+
+ method name: 'Gets datastore kinds. The closure lets you apply additional filters to your query.',
+ type: 'java.util.List',
+ params: [options: 'com.google.appengine.api.datastore.FetchOptions', closure: 'groovy.lang.Closure'],
+ doc: 'Gets datastore kinds. The closure lets you apply additional filters to your query.'
+
+ method name: 'getKinds',
+ type: 'java.util.List',
+ doc: 'Gets datastore kinds.'
+
+ property name: 'kinds',
+ type: 'java.util.List',
+ doc: 'Gets datastore kinds.'
+
+ method name: 'getKinds',
+ type: 'java.util.List',
+ params: [arg1: 'groovy.lang.Closure'],
+ doc: 'Gets datastore kinds. The closure lets you apply additional filters to your query.'
+
+}
+
+
+
+
+(enclosingCallName("transform") & inClosure() & enclosingCallDeclaringType('com.google.appengine.api.images.Image')).accept{
+ provider = 'Gaelyk'
+ method name: 'resize',
+ type: 'com.google.appengine.api.images.Image',
+ params: [width: 'int', height: 'int'],
+ doc: 'Resizes the image.'
+ method name: 'crop',
+ type: 'com.google.appengine.api.images.Image',
+ params: [leftX: 'double', topY: 'double', rightY: 'double', bottomY: 'double'],
+ doc: 'Croppes the image.'
+
+ method name: 'horizontal',
+ type: 'com.google.appengine.api.images.Image',
+ params: [flip: boolean],
+ doc: 'Applies the horizontal flip.'
+
+ method name: 'vertical',
+ type: 'com.google.appengine.api.images.Image',
+ params: [flip: boolean],
+ doc: 'Applies the vertical flip.'
+
+ method name: 'feeling',
+ type: 'com.google.appengine.api.images.Image',
+ params: [lucky: boolean],
+ doc: 'Applies the "I\'m feeling lucky" transformation.'
+
+ property name: 'lucky',
+ type: boolean,
+ doc: 'Use after the "feeling" keyword'
+
+ property name: 'flip',
+ type: boolean,
+ doc: 'Use after the "horizontal" or the "vertical" keyword'
+
+}
+
+shortcutFor('com.google.appengine.api.images.Image').accept {
+ provider = 'Gaelyk'
+
+ method name: 'transform',
+ type: 'com.google.appengine.api.images.Image',
+ params: [closure: 'groovy.lang.Closure'],
+ doc: '''Image transform DSL.
+
+bytes.image.transform {
+ resize 100, 100
+ crop 0.1, 0.1, 0.9, 0.9
+ flip horizontal
+ flip vertical
+ rotate 90
+ feeling lucky
+}
+
'''
+
+ method name: 'resize',
+ type: 'com.google.appengine.api.images.Image',
+ params: [width: 'int', height: 'int'],
+ doc: '''Create a new resized image.
+
+ def thumbnail = image.resize(100, 100)
+
'''
+
+ method name: 'rotate',
+ type: 'com.google.appengine.api.images.Image',
+ params: [degrees: 'int'],
+ doc: '''Create a new rotated image.
+
+
+ def rotated = image.rotate(90)
+
'''
+
+ method name: 'crop',
+ type: 'com.google.appengine.api.images.Image',
+ params: [leftX: 'double', topY: 'double', rightY: 'double', bottomY: 'double'],
+ doc: '''Create a new cropped image.
+
+
+ def cropped = image.crop(0.1, 0.1, 0.9, 0.9)
+
'''
+
+ method name: 'horizontalFlip',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Create a new image flipped horizontally.
+
+
+ def himage = image.horizontalFlip()
+
'''
+
+ method name: 'verticalFlip',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Create a new image flipped vertically.
+
+
+ def vimage = image.verticalFlip()
+
'''
+
+ method name: 'imFeelingLucky',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Create a new image applying the "I'm feeling lucky" transformation.
+
+
+ def adjusted = image.iAmFeelingLucky()
+
'''
+
+}
+
+shortcutFor('com.google.appengine.api.blobstore.BlobKey').accept {
+ provider = 'Gaelyk'
+ method name: 'getSize',
+ type: 'long',
+ doc: 'The size of the blob.'
+
+ property name: 'size',
+ type: 'long',
+ doc: 'The size of the blob.'
+
+ method name: 'delete',
+ type: 'void',
+ doc: 'Delete the blob associated with this blob key.'
+
+ method name: 'getContentType',
+ type: 'java.lang.String',
+ doc: 'The content-type of the blob.'
+
+ property name: 'contentType',
+ type: 'java.lang.String',
+ doc: 'The content-type of the blob.'
+
+ method name: 'withStream',
+ type: 'java.lang.Object',
+ params: [closure: 'groovy.lang.Closure'],
+ doc: '''Creates an InputStream
over the blob.
+The stream is passed as parameter of the closure.
+This methods takes care of properly opening and closing the stream.
+You can use this method as follows:
+
+blobKey.withStream { inputstream -> ... }
+
'''
+
+ method name: 'withReader',
+ type: 'java.lang.Object',
+ params: [encoding: 'java.lang.String', c: 'groovy.lang.Closure'],
+ doc: '''Creates a (buffered) Reader
over the blob with a specified encoding.
+The reader is passed as parameter of the closure.
+This methods takes care of properly opening and closing the reader and underlying stream.
+You can use this method as follows:
+
+blobKey.withReader("UTF-8") { reader -> ... }
+
'''
+
+ method name: 'withReader',
+ type: 'java.lang.Object',
+ params: [closure: 'groovy.lang.Closure'],
+ doc: '''Creates a (buffered) Reader
over the blob using UTF-8 as default encoding.
+The reader is passed as parameter of the closure.
+This methods takes care of properly opening and closing the reader and underlying stream.
+You can use this method as follows:
+
+ blobKey.withReader { reader -> ... }
+
'''
+
+ method name: 'getInfo',
+ type: 'com.google.appengine.api.blobstore.BlobInfo',
+ doc: '''Get the BlobInfo
associated with a blob key with:
+
+ blobKey.info
+
'''
+
+ property name: 'info',
+ type: 'com.google.appengine.api.blobstore.BlobInfo',
+ doc: '''Get the BlobInfo
associated with a blob key with:
+
+ blobKey.info
+
'''
+
+ method name: 'getFilename',
+ type: 'java.lang.String',
+ doc: 'The name of the file stored in the blob.'
+
+ property name: 'filename',
+ type: 'java.lang.String',
+ doc: 'The name of the file stored in the blob.'
+
+ method name: 'getCreation',
+ type: 'java.util.Date',
+ doc: 'The creation date of the file stored in the blob.'
+
+ property name: 'creation',
+ type: 'java.util.Date',
+ doc: 'The creation date of the file stored in the blob.'
+
+ method name: 'serve',
+ type: 'void',
+ params: [response: 'javax.servlet.http.HttpServletResponse', range: 'com.google.appengine.api.blobstore.ByteRange'],
+ doc: 'Serve a range of the blob to the response.'
+
+ method name: 'Serve a range of the blob to the response.',
+ type: 'void',
+ params: [response: 'javax.servlet.http.HttpServletResponse', range: 'groovy.lang.IntRange'],
+ doc: 'TODO'
+
+ method name: 'serve',
+ type: 'void',
+ params: [response: 'javax.servlet.http.HttpServletResponse'],
+ doc: 'Serve a range of the blob to the response.'
+
+ method name: 'fetchData',
+ type: 'byte[]',
+ params: [start: 'long', end: 'long'],
+ doc: 'Fetch a segment of a blob.'
+
+ method name: 'fetchData',
+ type: 'byte[]',
+ params: [range: 'groovy.lang.IntRange'],
+ doc: '''Fetch a segment of a blob
+
+ blobKey.fetchData 1000..2000
+
'''
+
+ method name: 'fetchData',
+ type: 'byte[]',
+ params: [range: 'com.google.appengine.api.blobstore.ByteRange'],
+ doc: '''Fetch a segment of a blob'''
+
+ method name: 'getImage',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Fetch an image stored in the blobstore.
+
+def image = blobKey.image
+// equivalent of ImagesServiceFactory.makeImageFromBlob(selfKey)
+
'''
+
+ property name: 'image',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Fetch an image stored in the blobstore.
+
+def image = blobKey.image
+// equivalent of ImagesServiceFactory.makeImageFromBlob(selfKey)
+
'''
+
+}
+
+
+// scaffolded
+
+shortcutFor('com.google.appengine.api.datastore.Key').accept {
+ provider = 'Gaelyk'
+ method name: 'delete',
+ type: 'void',
+ doc: '''Delete the entity represented by that key, from the data store.
+Usage: key.delete()
'''
+
+ method name: 'asyncDelete',
+ type: 'java.util.concurrent.Future',
+ doc: '''Delete the entity represented by that key, from the data store.
+Usage: key.asyncDelete()
'''
+
+}
+
+shortcutFor('com.google.appengine.api.files.AppEngineFile').accept {
+ provider = 'Gaelyk'
+ method name: 'delete',
+ type: 'void',
+ doc: 'Delete an AppEngineFile file from the blobstore.'
+
+ method name: 'withReader',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ useNamedArgs: true,
+ params: [encoding: String, locked: boolean, finalize: true, c: 'groovy.lang.Closure'],
+ doc: '''Method creating a reader for the AppEngineFile, read textual content from it, and closes it when done.
+
+
+ def file = files.fromPath(someStringPath)
+
+ // with default options
+ file.withReader { reader ->
+ log.info reader.text
+ }
+
+ // with specific options:
+ file.withReader(encoding: "US-ASCII", locked: true) { reader ->
+ log.info reader.text
+ }
+
'''
+
+ method name: 'withReader',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ params: [c: 'groovy.lang.Closure'],
+ doc: '''Method creating a reader for the AppEngineFile, read textual content from it, and closes it when done.
+
+
+ def file = files.fromPath(someStringPath)
+
+ // with default options
+ file.withReader { reader ->
+ log.info reader.text
+ }
+
+ // with specific options:
+ file.withReader(encoding: "US-ASCII", locked: true) { reader ->
+ log.info reader.text
+ }
+
'''
+
+ method name: 'withOutputStream',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ useNamedArgs: true,
+ params: [encoding: String, locked: boolean, finalize: true, c: 'groovy.lang.Closure'],
+ doc: '''Method creating an output stream for the AppEngineFile, writing bynary content to it, and closes it when done.
+
+
+ def file = files.createNewBlobFile("text/plain", "hello.txt")
+
+ // with default options
+ file.withOutputStream { stream ->
+ stream << "some content".bytes
+ }
+
+ // with specific options:
+ file.withOutputStream(locked: true, finalize: false) { writer ->
+ stream << "some content".bytes
+ }
+
'''
+
+ method name: 'withOutputStream',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ params: [c: 'groovy.lang.Closure'],
+ doc: '''Method creating an output stream for the AppEngineFile, writing bynary content to it, and closes it when done.
+
+
+ def file = files.createNewBlobFile("text/plain", "hello.txt")
+
+ // with default options
+ file.withOutputStream { stream ->
+ stream << "some content".bytes
+ }
+
+ // with specific options:
+ file.withOutputStream(locked: true, finalize: false) { writer ->
+ stream << "some content".bytes
+ }
+
'''
+
+ method name: 'withInputStream',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ useNamedArgs: true,
+ params: [encoding: String, locked: boolean, finalize: true, c: 'groovy.lang.Closure'],
+ doc: '''Method creating a buffered input stream for the AppEngineFile, read binary content from it, and closes it when done.
+
+
+ def file = files.fromPath(someStringPath)
+
+ // with default options
+ file.withInputStream { stream ->
+ // read from the buffered input stream
+ }
+
+ // with specific options:
+ file.withInputStream(locked: true) { stream ->
+ // read from the buffered input stream
+ }
+
'''
+
+ method name: 'withInputStream',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ params: [c: 'groovy.lang.Closure'],
+ doc: '''Method creating a buffered input stream for the AppEngineFile, read binary content from it, and closes it when done.
+
+
+ def file = files.fromPath(someStringPath)
+
+ // with default options
+ file.withInputStream { stream ->
+ // read from the buffered input stream
+ }
+
+ // with specific options:
+ file.withInputStream(locked: true) { stream ->
+ // read from the buffered input stream
+ }
+
'''
+
+ method name: 'withWriter',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ useNamedArgs: true,
+ params: [encoding: String, locked: boolean, finalize: true, c: 'groovy.lang.Closure'],
+ doc: '''Method creating a writer for the AppEngineFile, writing textual content to it, and closes it when done.
+
+
+ def file = files.createNewBlobFile("text/plain", "hello.txt")
+
+ // with default options
+ file.withWriter { writer ->
+ writer << "some content"
+ }
+
+ // with specific options:
+ file.withWriter(encoding: "US-ASCII", locked: true, finalize: false) { writer ->
+ writer << "some content
+ }
+
'''
+
+ method name: 'withWriter',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ params: [c: 'groovy.lang.Closure'],
+ doc: '''Method creating a writer for the AppEngineFile, writing textual content to it, and closes it when done.
+
+
+ def file = files.createNewBlobFile("text/plain", "hello.txt")
+
+ // with default options
+ file.withWriter { writer ->
+ writer << "some content"
+ }
+
+ // with specific options:
+ file.withWriter(encoding: "US-ASCII", locked: true, finalize: false) { writer ->
+ writer << "some content
+ }
+
'''
+
+ method name: 'getBlobKey',
+ type: 'com.google.appengine.api.blobstore.BlobKey',
+ doc: '''Retrieves the blob key associated with an App Engine file.
+
+ def file = files.createNewBlobFile("text/plain")
+ def key = file.blobKey
+ // equivalent of FileServiceFactory.fileService.getBlobKey(file)
+
'''
+
+ property name: 'blobKey',
+ type: 'com.google.appengine.api.blobstore.BlobKey',
+ doc: '''Retrieves the blob key associated with an App Engine file.
+
+ def file = files.createNewBlobFile("text/plain")
+ def key = file.blobKey
+ // equivalent of FileServiceFactory.fileService.getBlobKey(file)
+
'''
+
+}
+
+shortcutFor('com.google.appengine.api.mail.MailService').accept {
+ provider = 'Gaelyk'
+ method name: 'send',
+ type: 'void',
+ useNamedArgs: true,
+ params: [attachments: Collection, bcc: Collections, cc: Collection, htmlBody: String, replyTo: String, sender: String, textBody: String, to: Collection],
+ doc: '''Additional send()
method taking a map as parameter.
+The map can contain the normal properties of the
+MailService.Message
class.'''
+
+ method name: 'sendToAdmins',
+ type: 'void',
+ useNamedArgs: true,
+ params: [attachments: Collection, bcc: Collections, cc: Collection, htmlBody: String, replyTo: String, sender: String, textBody: String, to: Collection],
+ doc: '''Additional sendToAdmins()
method for sending emails to the application admins.
+This method is taking a map as parameter.
+The map can contain the normal properties of the
+MailService.Message
class.'''
+
+ method name: 'parseMessage',
+ type: 'javax.mail.internet.MimeMessage',
+ params: [request: 'javax.servlet.http.HttpServletRequest'],
+ doc: 'Parses an incoming email message coming from the request into a MimeMessage
'
+
+}
+
+shortcutFor('com.google.appengine.api.xmpp.XMPPService').accept {
+ provider = 'Gaelyk'
+ method name: 'send',
+ type: 'com.google.appengine.api.xmpp.SendResponse',
+ useNamedArgs: true,
+ params: [from: String, to: List, type: 'com.google.appengine.api.xmpp.MessageType', body: String, xml: Closure],
+ doc: '''Send an XMPP/Jabber message with the XMPP service using a map of attributes to build the message.
+
+Possible attributes are:
+
+from: the sender Jabber ID represented as a String
+to: a String or a list of String representing recepients' Jabber IDs
+type: an instance of the MessageType enum, or a String representation
+('CHAT', 'ERROR', 'GROUPCHAT', 'HEADLINE', 'NORMAL')
+body: a String representing the raw text to send
+xml: a closure representing the XML you want to send (serialized using StreamingMarkupBuilder)
+ '''
+
+ method name: 'sendInvitation',
+ type: 'void',
+ params: [jabberId: 'java.lang.String'],
+ doc: 'Send a chat invitation to a Jabber ID.'
+
+ method name: 'sendInvitation',
+ type: 'void',
+ params: [jabberId: 'java.lang.String', jabberIdFrom: 'java.lang.String'],
+ doc: 'Send a chat invitation to a Jabber ID from another Jabber ID.'
+
+ method name: 'getPresence',
+ type: 'com.google.appengine.api.xmpp.Presence',
+ params: [jabberId: 'java.lang.String'],
+ doc: 'Get the presence of a Jabber ID.'
+
+ method name: 'getPresence',
+ type: 'com.google.appengine.api.xmpp.Presence',
+ params:[jabberId: 'java.lang.String', jabberIdFrom: 'java.lang.String'],
+ doc: 'Get the presence of a Jabber ID.'
+
+ method name: 'parsePresence',
+ type: 'com.google.appengine.api.xmpp.Presence',
+ params: [request: 'javax.servlet.http.HttpServletRequest'],
+ doc: ''' Override the GAE SDK XMPPService#parsePresence as it hard-codes the path for the presence handler,
+thus preventing from using Gaelyk's routing to point at our own handler.'''
+
+ method name: 'parseSubscription',
+ type: 'com.google.appengine.api.xmpp.Subscription',
+ params: [request: 'javax.servlet.http.HttpServletRequest'],
+ doc: '''Override the GAE SDK XMPPService#parseSubscription as it hard-codes the path for the subscription handler,
+thus preventing from using Gaelyk's routing to point at our own handler.'''
+
+}
+
+shortcutFor('com.google.appengine.api.channel.ChannelService').accept {
+ provider = 'Gaelyk'
+ method name: 'send',
+ type: 'void',
+ params: [clientId: 'java.lang.String', message: 'java.lang.String'],
+ doc: 'Send a message through the Channel service.'
+
+}
+
+shortcutFor('javax.servlet.http.HttpServletResponse').accept {
+ provider = 'Gaelyk'
+ method name: 'getHeaders',
+ type: 'java.util.Map',
+ doc: '''Adds a fake getHeaders()
method to HttpServletResponse
.
+It allows the similar subscript notation syntax of request,
+but for setting or overriding a header on the response
+(ie. calling response.setHeader()
).
+It also allows the leftShift notation for adding a header to the response
+(ie. calling response.addHeader()
.
+
+
+ // sets or overrides the header 'a'
+ response.headers['a'] == 'b'
+
+ // adds an additional value to an existing header
+ // or sets a first value for a non-existant header
+ response.headers['a'] << 'b'
+
'''
+
+ property name: 'headers',
+ type: 'java.util.Map',
+ doc: '''Adds a fake getHeaders()
method to HttpServletResponse
.
+It allows the similar subscript notation syntax of request,
+but for setting or overriding a header on the response
+(ie. calling response.setHeader()
).
+It also allows the leftShift notation for adding a header to the response
+(ie. calling response.addHeader()
.
+
+
+ // sets or overrides the header 'a'
+ response.headers['a'] == 'b'
+
+ // adds an additional value to an existing header
+ // or sets a first value for a non-existant header
+ response.headers['a'] << 'b'
+
'''
+
+}
+
+shortcutFor('com.google.appengine.api.urlfetch.HTTPResponse').accept {
+ provider = 'Gaelyk'
+ method name: 'getText',
+ type: 'java.lang.String',
+ params: [encoding: 'java.lang.String'],
+ doc: 'Gets the text of the response.'
+
+ method name: 'getText',
+ type: 'java.lang.String',
+ doc: 'Gets the text of the response.'
+
+ property name: 'text',
+ type: 'java.lang.String',
+ doc: 'Gets the text of the response.'
+
+ method name: 'getStatusCode',
+ type: 'int',
+ doc: 'The HTTP status code (synonym of getResponseCode()
).'
+
+ property name: 'statusCode',
+ type: 'int',
+ doc: 'The HTTP status code (synonym of getResponseCode()
).'
+
+ method name: 'getHeadersMap',
+ type: 'java.util.Map',
+ doc: 'A convenient Map of HTTP Headers from the response.'
+
+ property name: 'headersMap',
+ type: 'java.util.Map',
+ doc: 'A convenient Map of HTTP Headers from the response.'
+
+}
+
+shortcutFor('com.google.appengine.api.images.CompositeTransform').accept {
+ provider = 'Gaelyk'
+ method name: 'rightShift',
+ type: 'com.google.appengine.api.images.CompositeTransform',
+ params: [transform: 'com.google.appengine.api.images.Transform'],
+ doc: '''Use the rightShift operator, >>, to "pre-concatenate" a transform to the composite transform.
+
+def cropTransform = ...
+def rotateTransform = ...
+
+croptTransform >> rotateTransform
+
'''
+
+ method name: 'leftShift',
+ type: 'com.google.appengine.api.images.CompositeTransform',
+ params: [transform: 'com.google.appengine.api.images.Transform'],
+ doc: '''Use the leftShift operator, <<, to concatenate a transform to the composite transform.
+
+def cropTransform = ...
+def rotateTransform = ...
+
+croptTransform << rotateTransform
+
'''
+
+}
+
+shortcutFor('com.google.appengine.api.xmpp.Message').accept {
+ provider = 'Gaelyk'
+ method name: 'getFrom',
+ type: 'java.lang.String',
+ doc: 'Get the sender Jabber ID of the message in the form of a String.'
+
+ property name: 'from',
+ type: 'java.lang.String',
+ doc: 'Get the sender Jabber ID of the message in the form of a String.'
+
+ method name: 'getXml',
+ type: 'groovy.util.slurpersupport.GPathResult',
+ doc: 'Get the XML content of this message (if it\'s an XML message) in the form of a DOM parsed with XmlSlurper.'
+
+ property name: 'xml',
+ type: 'groovy.util.slurpersupport.GPathResult',
+ doc: 'Get the XML content of this message (if it\'s an XML message) in the form of a DOM parsed with XmlSlurper..'
+
+ method name: 'getRecipients',
+ type: 'java.util.List',
+ doc: 'Gets the list of recipients of this message in the form of a list of Jabber ID strings.'
+
+ property name: 'recipients',
+ type: 'java.util.List',
+ doc: 'Gets the list of recipients of this message in the form of a list of Jabber ID strings.'
+
+}
+
+shortcutFor('com.google.appengine.api.capabilities.CapabilitiesService').accept {
+ provider = 'Gaelyk'
+ method name: 'getAt',
+ type: 'com.google.appengine.api.capabilities.CapabilityStatus',
+ params: [capability: 'com.google.appengine.api.capabilities.Capability'],
+ doc: '''Query the status of the various App Engine services.
+
+
+import static com.google.appengine.api.capabilities.Capability.*
+import static com.google.appengine.api.capabilities.CapabilityStatus.*
+
+capabilities[DATASTORE] == ENABLED
+
'''
+
+}
+
+shortcutFor('java.lang.String').accept {
+ provider = 'Gaelyk'
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [cls: 'java.lang.Class'],
+ doc: '''Converter method for converting strings into various GAE specific types
+
+ "foo@bar.com" as Email
+ "http://www.google.com" as Link
+ "+3361234543" as PhoneNumber
+ "50 avenue de la Madeleine, Paris" as PostalAddress
+ "groovy" as DatastoreCategory
+ "32" as Rating
+ "long text" as Text
+ "foobar" as BlobKey
+ "foo@gmail.com" as JID
+
'''
+
+}
+
+shortcutFor('java.lang.Object').accept {
+ provider = 'Gaelyk'
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [arg1: 'java.lang.Class'],
+ doc: '''Gaelyk supports a simplistic object/entity mapping, thanks to type coercion.
+You can use this type coercion mechanism to coerce POJOs/POGOs and datastore Entities.
+The Entity
kind will be the simple name of the POJO/POGO (same approach as Objectify).
+So with this mechanism, you can do:
+
+
+ class Person { String name, int age }
+
+ def p = new Person(name: "Guillaume", age: 33)
+ def e = p as Entity
+
+ assert p.name == e.name
+ assert p.age == e.age
+
'''
+
+}
+
+shortcutFor('java.lang.Integer').accept {
+ provider = 'Gaelyk'
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [cls: 'java.lang.Class'],
+ doc: '''Converter method for converting an integer into a Rating instance
+
+ 32 as Rating
+
'''
+
+}
+
+shortcutFor('byte[]').accept {
+ provider = 'Gaelyk'
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [cls: 'java.lang.Class'],
+ doc: '''Converter method for converting a byte array into a Blob or ShortBlob instance
+
+ "some byte".getBytes() as Blob
+ "some byte".getBytes() as ShortBlob
+
'''
+
+ method name: 'getImage',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Transform a byte array into an Image.
+
+
+def byteArray = ...
+def image = byteArray.image
+
'''
+
+ property name: 'image',
+ type: 'com.google.appengine.api.images.Image',
+ doc: '''Transform a byte array into an Image.
+
+
+def byteArray = ...
+def image = byteArray.image
+
'''
+
+}
+
+shortcutFor('java.util.List').accept {
+ provider = 'Gaelyk'
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [geoPtClass: 'java.lang.Class'],
+ doc: '''Converter method for converting a pair of numbers (in a list) into a GeoPt instance
+
+ [45.32, 54.54f] as GeoPt
+
'''
+
+}
+
+shortcutFor('groovy.lang.IntRange').accept {
+ provider = 'Gaelyk'
+ method name: 'asType',
+ type: 'java.lang.Object',
+ params: [byteRangeClass: 'java.lang.Class'],
+ doc: '''Converter method for converting an int range to a blobstore ByteRange
:
+
+ 300..400 as ByteRange
+
+Note that Groovy already allowed: [300, 400] as ByteRange
.'''
+
+}
+
+shortcutFor('com.google.appengine.api.capabilities.CapabilityStatus').accept {
+ provider = 'Gaelyk'
+ method name: 'asBoolean',
+ type: 'boolean',
+ doc: '''Coerces a capability status into a boolean.
+This mechanism is used by the "Groovy Truth".'''
+
+}
+
+
+(shortcutFor('java.lang.Class')| shortcutFor('com.google.appengine.api.NamespaceManager')).accept {
+ provider = 'Gaelyk'
+
+ method name: 'of',
+ type: 'void',
+ isStatic: true,
+ params: [namespace: 'java.lang.String', c: 'groovy.lang.Closure'],
+ doc: '''Use a namespace in the context of the excution of the closure.
+This method will save the original namespace and restore it afterwards.
+
+
+namespace.of('test') { ... }
+
'''
+
+}
+
+shortcutFor('java.util.Map').accept {
+ provider = 'Gaelyk'
+ method name: 'toQueryString',
+ type: 'java.lang.String',
+ doc: 'Transforms a map of key / value pairs into a properly URL encoded query string.'
+
+}
+
+shortcutFor('com.google.appengine.api.xmpp.SendResponse').accept {
+ provider = 'Gaelyk'
+ method name: 'isSuccessful',
+ type: 'boolean',
+ doc: 'Checks the status of the sending of the message was successful for all its recipients.'
+
+ property name: 'successful',
+ type: 'boolean',
+ doc: 'Checks the status of the sending of the message was successful for all its recipients.'
+
+}
+
+shortcutFor('javax.servlet.http.HttpServletRequest').accept {
+ provider = 'Gaelyk'
+ method name: 'parseXmppFormData',
+ type: 'java.util.Map',
+ doc: '''Parse the form-data from the Jabber requests,
+as it contains useful information like presence and subscription details, etc.'''
+
+}
+
+shortcutFor('java.io.File').accept {
+ provider = 'Gaelyk'
+ method name: 'getImage',
+ type: 'com.google.appengine.api.images.Image',
+ doc: 'Create an image from a file.'
+
+ property name: 'image',
+ type: 'com.google.appengine.api.images.Image',
+ doc: 'Create an image from a file.'
+
+}
+
+shortcutFor('com.google.appengine.api.files.FileService').accept {
+ provider = 'Gaelyk'
+ method name: 'fromPath',
+ type: 'com.google.appengine.api.files.AppEngineFile',
+ params: [path: 'java.lang.String'],
+ doc: '''Get a reference to an App Engine file from its path.
+
+ def path = "...some path..."
+ def file = files.fromPath(path)
+// equivalent of new AppEngineFile(path)
+
'''
+
+}
+
+shortcutFor('com.google.appengine.api.LifecycleManager').accept {
+ provider = 'Gaelyk'
+ method name: 'setShutdownHook',
+ type: 'void',
+ params: [hook: 'groovy.lang.Closure'],
+ doc: '''Shortcut to use closures as shutdown hooks.
+
+ lifecycle.shutdownHook = { ...shutdown logic... }
+
'''
+
+ property name: 'shutdownHook',
+ type: 'groovy.lang.Closure',
+ doc: '''Shortcut to use closures as shutdown hooks.
+
+ lifecycle.shutdownHook = { ...shutdown logic... }
+
'''
+
+}
+
diff --git a/src/gaelyk.gdsl b/src/gaelyk.gdsl
new file mode 100644
index 0000000..0013315
--- /dev/null
+++ b/src/gaelyk.gdsl
@@ -0,0 +1,58 @@
+def all = context(scope: scriptScope(), filetypes: ['groovy', 'gtpl'])
+
+def jabber = context(scope: scriptScope(name: 'jabber.groovy'))
+def mail = context(scope: scriptScope(name: 'email.groovy'))
+
+def routes = context(scope: scriptScope(name: 'routes.groovy'))
+
+contributor([all, jabber, mail]) {
+ property name: "request", type: "javax.servlet.http.HttpServletRequest"
+ property name: "response", type: "javax.servlet.http.HttpServletResponse"
+ property name: "context", type: "javax.servlet.ServletContext"
+ property name: "application", type: "javax.servlet.ServletContext"
+ property name: "session", type: "javax.servlet.http.HttpSession"
+ property name: "params", type: "java.util.Map"
+ property name: "headers", type: "java.util.Map"
+ property name: "out", type: "java.io.PrintWriter"
+ property name: "sout", type: "javax.servlet.ServletOutputStream"
+ property name: "html", type: "groovy.xml.MarkupBuilder"
+}
+
+contributor([all, jabber, mail, routes]) {
+ property name: "app", type: "java.util.Map"
+ property name: "queues", type: "com.google.appengine.api.labs.taskqueue.Queue"
+ property name: "defaultQueue", type: "com.google.appengine.api.labs.taskqueue.Queue"
+ property name: "mail", type: "com.google.appengine.api.mail.MailService"
+ property name: "namespace", type: "com.google.appengine.api.NamespaceManager"
+ property name: "logger", type: "groovyx.gaelyk.logging.LoggerAccessor"
+ property name: "log", type: "groovyx.gaelyk.logging.GroovyLogger"
+
+ property name: "datastore", type: "com.google.appengine.api.datastore.DatastoreService"
+ property name: "memcache", type: "com.google.appengine.api.memcache.MemcacheService"
+ property name: "urlFetch", type: "com.google.appengine.api.urlfetch.URLFetchService"
+ property name: "images", type: "groovyx.gaelyk.ImagesServiceWrapper"
+ property name: "users", type: "com.google.appengine.api.users.UserService"
+ property name: "user", type: "com.google.appengine.api.users.User"
+ property name: "xmpp", type: "com.google.appengine.api.xmpp.XMPPService"
+ property name: "blobstore", type: "com.google.appengine.api.blobstore.BlobstoreService"
+ property name: "files", type: "com.google.appengine.api.files.FileService"
+ property name: "oauth", type: "com.google.appengine.api.oauth.OAuthService"
+
+ property name: "localMode", type: "java.lang.Boolean"
+
+ method name: 'forward', type: 'void', params: [path: 'java.lang.String']
+ method name: 'include', type: 'void', params: [path: 'java.lang.String']
+ method name: 'redirect', type: 'void', params: [path: 'java.lang.String']
+}
+
+contributor([jabber]) {
+ property name: "message", type: "com.google.appengine.api.xmpp.Message"
+}
+
+contributor([mail]) {
+ property name: "message", type: "javax.mail.MimeMessage"
+}
+
+contributor([routes]) {
+
+}
\ No newline at end of file
diff --git a/src/main/bloogaey.iml b/src/main/bloogaey.iml
new file mode 100644
index 0000000..447f02f
--- /dev/null
+++ b/src/main/bloogaey.iml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ dffedfc3dfcddfd8dfc5dfdfdf9bdf93df9ddf9d
+ /Library/GoogleAppEngine
+ glaforge@gmail.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/groovy/bloogy/Utilities.groovy b/src/main/groovy/bloogy/Utilities.groovy
new file mode 100644
index 0000000..9d6ca09
--- /dev/null
+++ b/src/main/groovy/bloogy/Utilities.groovy
@@ -0,0 +1,19 @@
+package bloogy
+
+import java.text.Normalizer
+
+class Utilities {
+
+ /**
+ * Transform a post article into a simplified url-friendly string.
+ * "Cool! A groovy, very groovy, title :-)" is transformed into "cool-a-groovy-very-groovy-title
+ */
+ static String streamline(String title) {
+ Normalizer.normalize(title, Normalizer.Form.NFD)
+ .replaceAll("\\p{InCombiningDiacriticalMarks}+", "")
+ .replaceAll(/\W+/, '-')
+ .replaceAll(/(-+$|^-+)/, '')
+ .toLowerCase()
+ }
+}
+
diff --git a/war/.gradle/1.0-milestone-3/taskArtifacts/cache.bin b/war/.gradle/1.0-milestone-3/taskArtifacts/cache.bin
new file mode 100644
index 0000000..5eed77c
Binary files /dev/null and b/war/.gradle/1.0-milestone-3/taskArtifacts/cache.bin differ
diff --git a/war/.gradle/1.0-milestone-3/taskArtifacts/cache.properties b/war/.gradle/1.0-milestone-3/taskArtifacts/cache.properties
new file mode 100644
index 0000000..29fc9ea
--- /dev/null
+++ b/war/.gradle/1.0-milestone-3/taskArtifacts/cache.properties
@@ -0,0 +1 @@
+#Thu Jul 28 16:44:02 CEST 2011
diff --git a/war/WEB-INF/appengine-generated/4HnOt8EnYBQKeGPB3ltJLA b/war/WEB-INF/appengine-generated/4HnOt8EnYBQKeGPB3ltJLA
new file mode 100644
index 0000000..faee63f
Binary files /dev/null and b/war/WEB-INF/appengine-generated/4HnOt8EnYBQKeGPB3ltJLA differ
diff --git a/war/WEB-INF/appengine-generated/6TOQGgmWyurVSC6auwgFeg b/war/WEB-INF/appengine-generated/6TOQGgmWyurVSC6auwgFeg
new file mode 100644
index 0000000..7848a74
Binary files /dev/null and b/war/WEB-INF/appengine-generated/6TOQGgmWyurVSC6auwgFeg differ
diff --git a/war/WEB-INF/appengine-generated/9T-Ycn5C0iscyQxIm_hUvg b/war/WEB-INF/appengine-generated/9T-Ycn5C0iscyQxIm_hUvg
new file mode 100644
index 0000000..6050843
Binary files /dev/null and b/war/WEB-INF/appengine-generated/9T-Ycn5C0iscyQxIm_hUvg differ
diff --git a/war/WEB-INF/appengine-generated/Cx-ZB1zCP9i7a3WWH3HxsQ b/war/WEB-INF/appengine-generated/Cx-ZB1zCP9i7a3WWH3HxsQ
new file mode 100644
index 0000000..63da5f9
--- /dev/null
+++ b/war/WEB-INF/appengine-generated/Cx-ZB1zCP9i7a3WWH3HxsQ
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+ DO SPE
+
+
diff --git a/war/WEB-INF/appengine-generated/EUBolXRDEyMInhdeMiu-Qg b/war/WEB-INF/appengine-generated/EUBolXRDEyMInhdeMiu-Qg
new file mode 100644
index 0000000..819dace
Binary files /dev/null and b/war/WEB-INF/appengine-generated/EUBolXRDEyMInhdeMiu-Qg differ
diff --git a/war/WEB-INF/appengine-generated/Eu90pcmrEog-aSx5Gb_P_w b/war/WEB-INF/appengine-generated/Eu90pcmrEog-aSx5Gb_P_w
new file mode 100644
index 0000000..8783613
Binary files /dev/null and b/war/WEB-INF/appengine-generated/Eu90pcmrEog-aSx5Gb_P_w differ
diff --git a/war/WEB-INF/appengine-generated/Fy1qCGs5duw2iRRmug5VeA b/war/WEB-INF/appengine-generated/Fy1qCGs5duw2iRRmug5VeA
new file mode 100644
index 0000000..819dace
Binary files /dev/null and b/war/WEB-INF/appengine-generated/Fy1qCGs5duw2iRRmug5VeA differ
diff --git a/war/WEB-INF/appengine-generated/KhYgYI9EIYcnCljoTt_5GQ b/war/WEB-INF/appengine-generated/KhYgYI9EIYcnCljoTt_5GQ
new file mode 100644
index 0000000..819dace
Binary files /dev/null and b/war/WEB-INF/appengine-generated/KhYgYI9EIYcnCljoTt_5GQ differ
diff --git a/war/WEB-INF/appengine-generated/UPFJu4Y17iwgTTHmPS-BkQ b/war/WEB-INF/appengine-generated/UPFJu4Y17iwgTTHmPS-BkQ
new file mode 100644
index 0000000..eb32985
Binary files /dev/null and b/war/WEB-INF/appengine-generated/UPFJu4Y17iwgTTHmPS-BkQ differ
diff --git a/war/WEB-INF/appengine-generated/datastore-indexes-auto.xml b/war/WEB-INF/appengine-generated/datastore-indexes-auto.xml
new file mode 100644
index 0000000..510f376
--- /dev/null
+++ b/war/WEB-INF/appengine-generated/datastore-indexes-auto.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/war/WEB-INF/appengine-generated/local_db.bin b/war/WEB-INF/appengine-generated/local_db.bin
new file mode 100644
index 0000000..44014be
Binary files /dev/null and b/war/WEB-INF/appengine-generated/local_db.bin differ
diff --git a/war/WEB-INF/appengine-generated/pRaJU4gfyQUKQ1QSnD78xA b/war/WEB-INF/appengine-generated/pRaJU4gfyQUKQ1QSnD78xA
new file mode 100644
index 0000000..ec9039b
Binary files /dev/null and b/war/WEB-INF/appengine-generated/pRaJU4gfyQUKQ1QSnD78xA differ
diff --git a/war/WEB-INF/appengine-generated/xGOwZKjDjbsLKIAQoVe-OQ b/war/WEB-INF/appengine-generated/xGOwZKjDjbsLKIAQoVe-OQ
new file mode 100644
index 0000000..0485cce
Binary files /dev/null and b/war/WEB-INF/appengine-generated/xGOwZKjDjbsLKIAQoVe-OQ differ
diff --git a/war/WEB-INF/appengine-generated/yNCR8fJUDnV9_LWawbQiJg b/war/WEB-INF/appengine-generated/yNCR8fJUDnV9_LWawbQiJg
new file mode 100644
index 0000000..3ea1a88
Binary files /dev/null and b/war/WEB-INF/appengine-generated/yNCR8fJUDnV9_LWawbQiJg differ
diff --git a/war/WEB-INF/appengine-web.xml b/war/WEB-INF/appengine-web.xml
new file mode 100644
index 0000000..5e7036d
--- /dev/null
+++ b/war/WEB-INF/appengine-web.xml
@@ -0,0 +1,42 @@
+
+ glaforge
+
+ 1
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mail
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
diff --git a/war/WEB-INF/classes/bloogy/Utilities.class b/war/WEB-INF/classes/bloogy/Utilities.class
new file mode 100644
index 0000000..6bd3239
Binary files /dev/null and b/war/WEB-INF/classes/bloogy/Utilities.class differ
diff --git a/war/WEB-INF/groovy/__bootstrap_data__.groovy b/war/WEB-INF/groovy/__bootstrap_data__.groovy
new file mode 100644
index 0000000..88b52e8
--- /dev/null
+++ b/war/WEB-INF/groovy/__bootstrap_data__.groovy
@@ -0,0 +1,91 @@
+import com.google.appengine.api.datastore.Entity
+import com.google.appengine.api.datastore.Text
+
+['posts', 'categories', 'authors'].each { entityKind ->
+ datastore.execute { select keys from entityKind }*.delete()
+}
+
+new Entity('categories').with {
+ name = 'Groovy'
+ description = 'Groovy dynamic language for the JVM'
+ save()
+}
+
+new Entity('categories').with {
+ name = 'Java'
+ description = 'Articles about the Java ecosystem'
+ save()
+}
+
+new Entity('categories').with {
+ name = 'Blogosphere'
+ description = 'About OSS, geek stuff, interesting reads, and more'
+ save()
+}
+
+def posts = [
+ [
+ title: 'Groovy 1.8-final is out the door!',
+ urlTitle: 'groovy-18-final-is-out-the-door',
+ created: new Date() - 100,
+ categories: ['Groovy'],
+ content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent porttitor vulputate augue, ac vulputate purus lobortis non. Suspendisse suscipit elementum consectetur. Mauris adipiscing molestie sem sit amet ornare. Aenean non metus nec nulla sagittis blandit. In sollicitudin, massa quis ultrices ultrices, enim neque tristique purus, eu luctus mi mi eget lectus. Etiam at commodo sem. Maecenas quis rhoncus lacus. In id tempor arcu. Nulla rutrum, odio non ultrices cursus, sapien tortor interdum orci, eget rhoncus quam orci nec diam. Praesent ultrices, dui eu pellentesque tristique, massa sem consequat purus, nec posuere elit quam at purus. Donec volutpat leo quis purus.',
+ draft: false,
+ type: 'post'
+ ],
+ [
+ title: 'GR8Conf Europe 2011 - a conference dedicated to the Groovy Ecosystem!',
+ urlTitle: 'gr8conf-europe-2011-a-conference-dedicated-to-the-groovy-ecosystem',
+ created: new Date() - 150,
+ categories: ['Groovy', 'Java'],
+ content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent porttitor vulputate augue, ac vulputate purus lobortis non. Suspendisse suscipit elementum consectetur. Mauris adipiscing molestie sem sit amet ornare. Aenean non metus nec nulla sagittis blandit. In sollicitudin, massa quis ultrices ultrices, enim neque tristique purus, eu luctus mi mi eget lectus. Etiam at commodo sem. Maecenas quis rhoncus lacus. In id tempor arcu. Nulla rutrum, odio non ultrices cursus, sapien tortor interdum orci, eget rhoncus quam orci nec diam. Praesent ultrices, dui eu pellentesque tristique, massa sem consequat purus, nec posuere elit quam at purus. Donec volutpat leo quis purus.',
+ draft: false,
+ type: 'post'
+ ],
+ [
+ title: 'Gaelyk 0.7 is out!',
+ urlTitle: 'gaelyk-07-is-out',
+ created: new Date() - 1,
+ categories: ['Groovy'],
+ content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent porttitor vulputate augue, ac vulputate purus lobortis non. Suspendisse suscipit elementum consectetur. Mauris adipiscing molestie sem sit amet ornare. Aenean non metus nec nulla sagittis blandit. In sollicitudin, massa quis ultrices ultrices, enim neque tristique purus, eu luctus mi mi eget lectus. Etiam at commodo sem. Maecenas quis rhoncus lacus. In id tempor arcu. Nulla rutrum, odio non ultrices cursus, sapien tortor interdum orci, eget rhoncus quam orci nec diam. Praesent ultrices, dui eu pellentesque tristique, massa sem consequat purus, nec posuere elit quam at purus. Donec volutpat leo quis purus.',
+ draft: false,
+ type: 'post'
+ ],
+ [
+ title: 'Gaelyk 1.0 released!',
+ urlTitle: 'gaelyk-10-released',
+ created: new Date() + 10,
+ categories: ['Groovy'],
+ content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent porttitor vulputate augue, ac vulputate purus lobortis non. Suspendisse suscipit elementum consectetur. Mauris adipiscing molestie sem sit amet ornare. Aenean non metus nec nulla sagittis blandit. In sollicitudin, massa quis ultrices ultrices, enim neque tristique purus, eu luctus mi mi eget lectus. Etiam at commodo sem. Maecenas quis rhoncus lacus. In id tempor arcu. Nulla rutrum, odio non ultrices cursus, sapien tortor interdum orci, eget rhoncus quam orci nec diam. Praesent ultrices, dui eu pellentesque tristique, massa sem consequat purus, nec posuere elit quam at purus. Donec volutpat leo quis purus.',
+ draft: true,
+ type: 'post'
+ ],
+ [
+ title: 'About this site',
+ urlTitle: 'about-this-site',
+ created: new Date(),
+ categories: [],
+ content: new Text('About this site'),
+ draft: false,
+ type: 'page'
+ ],
+ [
+ title: 'Contact me',
+ urlTitle: 'contact-me',
+ created: new Date(),
+ categories: [],
+ content: new Text('Contact me'),
+ draft: false,
+ type: 'page'
+ ]
+]
+
+posts.each { item ->
+ def e = new Entity('posts')
+ item.each { prop, val ->
+ e."$prop" = val
+ }
+ e.save()
+}
+
+redirect '/'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/__testMailParsing2__.groovy b/war/WEB-INF/groovy/__testMailParsing2__.groovy
new file mode 100644
index 0000000..2453e83
--- /dev/null
+++ b/war/WEB-INF/groovy/__testMailParsing2__.groovy
@@ -0,0 +1,46 @@
+import javax.mail.internet.MimeMessage
+import javax.mail.Session
+import javax.mail.Authenticator
+import javax.mail.internet.MimeMultipart
+
+def msg = new MimeMessage(new Session(new Properties(), new Authenticator() {}), new FileInputStream('msg.txt'))
+
+def sb = new StringBuilder()
+
+def handleMultipart
+handleMultipart = { MimeMultipart mmp ->
+ for (int i = 0; i < mmp.count; i++) {
+ def part = mmp.getBodyPart(i)
+ if (part.content instanceof MimeMultipart) {
+ handleMultipart(part.content)
+ } else {
+ if (part.contentType.contains('text/plain')) {
+
+ } else if (part.contentType.contains('text/html')) {
+
+ } else if (part.contentType.contains('image/')) {
+
+ } else {
+
+ }
+ }
+ }
+}
+
+handleMultipart(msg.content)
+
+out << sb.toString()
+
+
+/*
+
+mixed
+ related
+ alternative
+ text/plain
+ text/html
+ image-png (inline)
+ image-png (attachment)
+
+
+*/
diff --git a/war/WEB-INF/groovy/__testMailParsing__.groovy b/war/WEB-INF/groovy/__testMailParsing__.groovy
new file mode 100644
index 0000000..2d3675b
--- /dev/null
+++ b/war/WEB-INF/groovy/__testMailParsing__.groovy
@@ -0,0 +1,45 @@
+import javax.mail.internet.MimeMessage
+import javax.mail.Session
+import javax.mail.Authenticator
+import javax.mail.internet.MimeMultipart
+
+def msg = new MimeMessage(new Session(new Properties(), new Authenticator() {}), new FileInputStream('msg.txt'))
+
+html.html {
+ head {
+ title "Parsed email from GMail"
+ }
+ body {
+ handleMultipart(msg.content)
+ }
+}
+
+def handleMultipart(MimeMultipart mmp) {
+ html.div "Parts: ${mmp.count}"
+ html.div "Content-type: ${mmp.contentType}"
+
+ for (int i = 0; i < mmp.count; i++) {
+ def part = mmp.getBodyPart(i)
+ html.h1 "Part #$i: ${part}"
+ html.h2 "Content ID: ${part.getHeader('Content-ID')}"
+ html.h2 "X-Attachment ID: ${part.getHeader('X-Attachment-Id')}"
+ html.h2 "Content-type: $part.contentType"
+ html.h2 "Content-disposition: $part.disposition"
+ html.h2 "File name: ${part.fileName}"
+ if (part.content instanceof MimeMultipart) {
+ html.blockquote {
+ handleMultipart(part.content)
+ }
+ } else {
+ if (part.contentType.contains('text/plain')) {
+ html.mkp.yieldUnescaped part.content
+ } else if (part.contentType.contains('text/html')) {
+ html.mkp.yieldUnescaped part.content
+ } else if (part.contentType.contains('image/')) {
+ html.img src:"data:${part.contentType.find('image/(.*);')}base64,${part.content.bytes.encodeBase64()}"
+ } else {
+ html.mkp.yieldUnescaped part.content.bytes.encodeBase64()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/adminCategories.groovy b/war/WEB-INF/groovy/admin/adminCategories.groovy
new file mode 100644
index 0000000..3ab248d
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/adminCategories.groovy
@@ -0,0 +1,15 @@
+
+request.categories = datastore.execute {
+ from categories
+ sort asc by name
+}
+
+request.categoriesCount = request.categories.collectEntries { category ->
+ [(category): datastore.execute {
+ select count
+ from posts
+ where categories == category.name
+ }]
+}
+
+forward '/WEB-INF/pages/admin/adminCategories.gtpl'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/adminMedia.groovy b/war/WEB-INF/groovy/admin/adminMedia.groovy
new file mode 100644
index 0000000..76e57ab
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/adminMedia.groovy
@@ -0,0 +1,2 @@
+
+forward '/WEB-INF/pages/admin/adminMedia.gtpl'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/adminPosts.groovy b/war/WEB-INF/groovy/admin/adminPosts.groovy
new file mode 100644
index 0000000..38f4eb2
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/adminPosts.groovy
@@ -0,0 +1,22 @@
+
+request.posts = datastore.execute {
+ from posts
+ sort desc by created
+ where draft == false
+ and type == 'post'
+}
+
+request.pages = datastore.execute {
+ from posts
+ sort desc by created
+ where draft == false
+ and type == 'page'
+}
+
+request.drafts = datastore.execute {
+ from posts
+ sort desc by created
+ where draft == true
+}
+
+forward '/WEB-INF/pages/admin/adminPosts.gtpl'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/categoryAdd.groovy b/war/WEB-INF/groovy/admin/categoryAdd.groovy
new file mode 100644
index 0000000..601b29d
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/categoryAdd.groovy
@@ -0,0 +1,12 @@
+import com.google.appengine.api.datastore.Entity
+
+def categoryName = params.categoryName
+def categoryDescription = params.categoryDescription
+
+new Entity('categories').with {
+ name = categoryName
+ description = categoryDescription
+ save()
+}
+
+redirect '/admin/categories'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/categoryDelete.groovy b/war/WEB-INF/groovy/admin/categoryDelete.groovy
new file mode 100644
index 0000000..e43314d
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/categoryDelete.groovy
@@ -0,0 +1,4 @@
+
+datastore.execute { select single from categories where name == params.categoryName }.delete()
+
+redirect '/admin/categories'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/clearCache.groovy b/war/WEB-INF/groovy/admin/clearCache.groovy
new file mode 100644
index 0000000..2af179c
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/clearCache.groovy
@@ -0,0 +1,31 @@
+
+memcache.clearCacheForUri '/'
+memcache.clearCacheForUri '/archives'
+
+100.times {
+ memcache.clearCacheForUri "/p${it}"
+ memcache.clearCacheForUri "/archives/p${it}"
+}
+
+memcache.clearCacheForUri '/search'
+memcache.clearCacheForUri '/social'
+
+memcache.clearCacheForUri '/feed/atom'
+
+datastore.execute {
+ from posts
+ sort desc by created
+ where draft == false
+}.each {
+ memcache.clearCacheForUri "/${it.type == 'post' ? 'article' : 'page'}/${it.urlTitle}"
+}
+
+datastore.execute {
+ from categories
+ sort asc by name
+}.each {
+ memcache.clearCacheForUri "/category/${it.name}"
+ memcache.clearCacheForUri "/feed/atom/${it.name}"
+}
+
+redirect '/admin/posts'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/email.groovy b/war/WEB-INF/groovy/admin/email.groovy
new file mode 100644
index 0000000..4f705ae
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/email.groovy
@@ -0,0 +1,54 @@
+import com.google.appengine.api.datastore.Entity
+import javax.mail.internet.MimeMultipart
+import javax.mail.BodyPart
+import javax.mail.internet.MimeMessage
+
+import static bloogy.Utilities.*
+
+def content = request.inputStream.text
+new Entity('email').with {
+ rawContent = content
+ save()
+}
+
+MimeMessage msg = mail.parseMessage(request)
+
+log.info "Subject ${msg.subject}, to ${msg.allRecipients.join(', ')}, from ${msg.from[0]}"
+
+String fullContent = ""
+
+msg.contentStream.withReader { Reader reader ->
+ fullContent = reader.text
+}
+
+def sb = new StringBuilder()
+
+if (msg.content instanceof String) {
+ log.info "Received a string"
+ sb << msg.content
+} else if (msg.content instanceof MimeMultipart) {
+ log.info "Received a mime multipart"
+ for (int i = 0; i < msg.content.count; i++) {
+ BodyPart part = msg.content.getBodyPart(i)
+ log.info("Part $i: $part")
+ log.info("Content: " + part.content)
+ sb << part.content
+ }
+}
+
+log.info "ALL: ${sb.toString()}"
+
+new Entity('posts').with {
+ title = msg.subject
+ urlTitle = streamline(msg.subject)
+ created = new Date()
+ categories = []
+ draft = false
+ type = 'post'
+
+ content = "$fullContent "
+
+ save()
+}
+
+
diff --git a/war/WEB-INF/groovy/admin/mediaAdd.groovy b/war/WEB-INF/groovy/admin/mediaAdd.groovy
new file mode 100644
index 0000000..93571cb
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/mediaAdd.groovy
@@ -0,0 +1,5 @@
+
+def blobs = blobstore.getUploadedBlobs(request)
+def blob = blobs["fileName"]
+
+redirect '/admin/media'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/mediaDelete.groovy b/war/WEB-INF/groovy/admin/mediaDelete.groovy
new file mode 100644
index 0000000..ef35ecd
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/mediaDelete.groovy
@@ -0,0 +1,10 @@
+import com.google.appengine.api.blobstore.BlobKey
+
+new BlobKey(params.blobKey).delete()
+
+def page = params.page.toInteger()
+
+def singleOnPage = params.singleOnPage == 'true'
+if (singleOnPage && page > 0) page -= 1
+
+redirect "/admin/media${page > 0 ? "/p${params.page}" : ''}#media-browser"
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/mediaSelector.groovy b/war/WEB-INF/groovy/admin/mediaSelector.groovy
new file mode 100644
index 0000000..2453647
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/mediaSelector.groovy
@@ -0,0 +1,21 @@
+import com.google.appengine.api.blobstore.BlobInfoFactory
+
+def blobstoreKind = com.google.appengine.api.blobstore.BlobInfoFactory.KIND
+
+def numberPerPage = 5
+
+def page = params.page ? params.page.toInteger() : 0
+def offsetParam = page * numberPerPage
+
+datastore.execute {
+ from blobstoreKind
+ offset offsetParam limit numberPerPage
+ sort desc by creation
+}.each { media ->
+ def info = new BlobInfoFactory().createBlobInfo(media)
+ def mediaUrl = "/media/${java.net.URLEncoder.encode(info.filename)}"
+ def croppedUrl = images.getServingUrl(info.blobKey) + '=s100-c'
+
+ html.img src: croppedUrl, fullsrc: mediaUrl
+}
+
diff --git a/war/WEB-INF/groovy/admin/postDelete.groovy b/war/WEB-INF/groovy/admin/postDelete.groovy
new file mode 100644
index 0000000..546b65e
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/postDelete.groovy
@@ -0,0 +1,6 @@
+import com.google.appengine.api.datastore.Key
+
+def key = ['posts', params.id.toLong()] as Key
+key.delete()
+
+redirect '/admin/posts'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/admin/postSave.groovy b/war/WEB-INF/groovy/admin/postSave.groovy
new file mode 100644
index 0000000..61a0e3e
--- /dev/null
+++ b/war/WEB-INF/groovy/admin/postSave.groovy
@@ -0,0 +1,38 @@
+import com.google.appengine.api.datastore.Entity
+
+import static bloogy.Utilities.*
+
+def id = params.id?.toLong()
+
+Entity postOrPage
+
+if (id) {
+ postOrPage = datastore.get('posts', id)
+} else {
+ postOrPage = new Entity('posts')
+ postOrPage.urlTitle = streamline(params.title)
+}
+
+postOrPage.title = params.title
+postOrPage.content = params.content
+postOrPage.created = Date.parse('yyyy/MM/dd HH:mm', params.created)
+postOrPage.draft = params.draft == 'draft' ?: false
+postOrPage.type = params.type
+
+if (params.categories == null) {
+ postOrPage.categories = null
+} else if (params.categories instanceof String) {
+ postOrPage.categories = [params.categories]
+} else if (params.categories) {
+ postOrPage.categories = params.categories.toList()
+}
+
+postOrPage.save()
+
+memcache.clearCacheForUri "/${postOrPage.type == 'page' ? 'page' : 'article'}/${postOrPage.urlTitle}"
+memcache.clearCacheForUri '/archives'
+memcache.clearCacheForUri '/'
+
+redirect "/live/${postOrPage.urlTitle}"
+
+
diff --git a/war/WEB-INF/groovy/archive.groovy b/war/WEB-INF/groovy/archive.groovy
new file mode 100644
index 0000000..3a49d76
--- /dev/null
+++ b/war/WEB-INF/groovy/archive.groovy
@@ -0,0 +1,21 @@
+// Max number of articles per page
+int pageSize = 10
+
+// Page number
+int page = params.page ? params.page.toInteger() : 0
+
+// Retrieve the latest 10 posts
+def posts = datastore.execute {
+ from posts
+ limit pageSize offset pageSize * page
+ where created < new Date()
+ and draft == false
+ and type == 'post'
+ sort desc by created
+}.groupBy { it.created.year + 1900 }
+
+request.page = page
+request.posts = posts
+
+forward '/WEB-INF/pages/archive.gtpl'
+
diff --git a/war/WEB-INF/groovy/article.groovy b/war/WEB-INF/groovy/article.groovy
new file mode 100644
index 0000000..686e920
--- /dev/null
+++ b/war/WEB-INF/groovy/article.groovy
@@ -0,0 +1,11 @@
+def title = params.title
+
+def post = datastore.execute { from posts where urlTitle == title }[0]
+
+if (post) {
+ request.post = post
+ forward '/WEB-INF/pages/article.gtpl'
+} else {
+ request.title = params.title
+ forward '/WEB-INF/pages/notFound.gtpl'
+}
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/category.groovy b/war/WEB-INF/groovy/category.groovy
new file mode 100644
index 0000000..965e474
--- /dev/null
+++ b/war/WEB-INF/groovy/category.groovy
@@ -0,0 +1,27 @@
+
+if (params.category) {
+
+ // Max number of articles per page
+ int pageSize = 10
+
+ // Page number
+ int page = params.page ? params.page.toInteger() : 0
+
+ def posts = datastore.execute {
+ from posts
+ limit pageSize offset pageSize * page
+ where created < new Date() + 1
+ and draft == false
+ and type == 'post'
+ and categories == params.category
+ sort desc by created
+ }.groupBy { it.created.year + 1900 }
+
+ request.page = page
+ request.posts = posts
+ request.category = params.category
+
+ forward '/WEB-INF/pages/archive.gtpl'
+} else {
+ forward '/WEB-INF/pages/notfound.gtpl'
+}
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/error.groovy b/war/WEB-INF/groovy/error.groovy
new file mode 100644
index 0000000..58df39c
--- /dev/null
+++ b/war/WEB-INF/groovy/error.groovy
@@ -0,0 +1,6 @@
+
+request.code = request.getAttribute("javax.servlet.error.status_code");
+request.ex = request.getAttribute("javax.servlet.error.exception");
+request.msg = request.getAttribute("javax.servlet.error.message");
+
+forward '/WEB-INF/pages/error.gtpl'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/feed.groovy b/war/WEB-INF/groovy/feed.groovy
new file mode 100644
index 0000000..b28196b
--- /dev/null
+++ b/war/WEB-INF/groovy/feed.groovy
@@ -0,0 +1,45 @@
+import java.text.SimpleDateFormat
+
+response.contentType = "text/xml;charset=utf-8"
+
+def isoTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
+
+def posts = datastore.execute {
+ from posts limit 10
+ where created < new Date()
+ and draft == false
+ and type == 'post'
+ if (params.category) {
+ and categories == params.category
+ }
+ sort desc by created
+}
+
+def serverRoot = "http://${request.serverName}${request.serverPort != 80 ? ":$request.serverPort" : ''}"
+
+html.feed(xmlns: "http://www.w3.org/2005/Atom") {
+ title "Guillaume Laforge's blog feed"
+ subtitle "On all things Groovy!"
+ link href: serverRoot, rel: "self"
+ updated isoTime.format(new Date())
+ generator(uri: "http://gaelyk.appspot.com", version: "1.0", "Gaelyk lightweight Groovy toolkit for Google App Engine")
+
+ posts.each { post ->
+ entry {
+ // create the summary to show in the Atom feed
+ int min = Math.min(post.content.size(), 1000)
+ def content = post.content[0..
+ sout << stream
+ }
+ } else {
+ blobInfo.blobKey.serve response
+ }
+} else {
+ response.contentType = 'image/png'
+ sout << new File('images/image-not-found.png').bytes
+}
diff --git a/war/WEB-INF/groovy/search.groovy b/war/WEB-INF/groovy/search.groovy
new file mode 100644
index 0000000..b0b8248
--- /dev/null
+++ b/war/WEB-INF/groovy/search.groovy
@@ -0,0 +1 @@
+forward '/WEB-INF/pages/search.gtpl'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/social.groovy b/war/WEB-INF/groovy/social.groovy
new file mode 100644
index 0000000..4e84121
--- /dev/null
+++ b/war/WEB-INF/groovy/social.groovy
@@ -0,0 +1,50 @@
+import java.text.SimpleDateFormat
+
+def delicious = "http://feeds.delicious.com/v2/rss/glaforge?count=15".toURL().get(async: true)
+def greader = "http://www.google.com/reader/public/atom/user%2F16866715143536937448%2Fstate%2Fcom.google%2Fbroadcast".toURL().get(async: true)
+def twitter = "http://search.twitter.com/search.atom?q=from%3Aglaforge&rpp=200".toURL().get(async: true)
+
+def items = []
+
+def slurper = new XmlSlurper()
+
+// date format: 2011-07-29T09:00:10Z
+def sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
+
+def twitterDoc = slurper.parseText(twitter.get().text)
+twitterDoc.entry.each { entry ->
+ items << [
+ origin: 'twitter',
+ title: entry.title.text(),
+ published: sdf.parse(entry.published.text()),
+ link: entry.link.find { it.@type == 'text/html' }.@href.text()
+ ]
+}
+
+def greaderDoc = slurper.parseText(greader.get().text)
+greaderDoc.entry.each { entry ->
+ items << [
+ origin: 'greader',
+ title: entry.title.text(),
+ published: sdf.parse(entry.published.text()),
+ link: entry.link.find { it.@type == 'text/html' }.@href.text()
+ ]
+}
+
+// date format: Thu, 28 Jul 2011 07:32:22 +0000
+sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US)
+
+def deliciousDoc = slurper.parseText(delicious.get().text)
+deliciousDoc.channel.item.each { entry ->
+ items << [
+ origin: 'delicious',
+ title: entry.title.text(),
+ published: sdf.parse(entry.pubDate.text()),
+ link: entry.link.text()
+ ]
+}
+
+
+request.items = items.sort { it.published }.reverse().each { it.published.clearTime() }.groupBy { it.published }
+
+forward '/WEB-INF/pages/social.gtpl'
\ No newline at end of file
diff --git a/war/WEB-INF/groovy/viewRawEmail.groovy b/war/WEB-INF/groovy/viewRawEmail.groovy
new file mode 100644
index 0000000..36de721
--- /dev/null
+++ b/war/WEB-INF/groovy/viewRawEmail.groovy
@@ -0,0 +1,5 @@
+def e = datastore.execute { select single from email }
+
+response.contentType = "text/plain"
+
+out << e.rawContent
\ No newline at end of file
diff --git a/war/WEB-INF/includes/bottom.gtpl b/war/WEB-INF/includes/bottom.gtpl
new file mode 100644
index 0000000..baf6c10
--- /dev/null
+++ b/war/WEB-INF/includes/bottom.gtpl
@@ -0,0 +1,18 @@
+
+
+
+
+
+ © 2011 Guillaume Laforge |
+ The views and opinions expressed here are mine and don't reflect the ones from my employer.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/includes/footer.gtpl b/war/WEB-INF/includes/footer.gtpl
new file mode 100644
index 0000000..aec3810
--- /dev/null
+++ b/war/WEB-INF/includes/footer.gtpl
@@ -0,0 +1,31 @@
+
\ No newline at end of file
diff --git a/war/WEB-INF/includes/header.gtpl b/war/WEB-INF/includes/header.gtpl
new file mode 100644
index 0000000..32bf677
--- /dev/null
+++ b/war/WEB-INF/includes/header.gtpl
@@ -0,0 +1,28 @@
+
\ No newline at end of file
diff --git a/war/WEB-INF/includes/left.gtpl b/war/WEB-INF/includes/left.gtpl
new file mode 100644
index 0000000..4615fbd
--- /dev/null
+++ b/war/WEB-INF/includes/left.gtpl
@@ -0,0 +1,80 @@
+
+
+
diff --git a/war/WEB-INF/includes/meta.gtpl b/war/WEB-INF/includes/meta.gtpl
new file mode 100644
index 0000000..c6fadf5
--- /dev/null
+++ b/war/WEB-INF/includes/meta.gtpl
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ <% if (!request.getAttribute('originalURI').contains('/admin')) { %>
+
+ <% } %>
diff --git a/war/WEB-INF/includes/navigation.gtpl b/war/WEB-INF/includes/navigation.gtpl
new file mode 100644
index 0000000..9f08ddd
--- /dev/null
+++ b/war/WEB-INF/includes/navigation.gtpl
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+ <%
+ def homeStyle = ''
+ def categoriesStyle = ''
+ def postsStyle = ''
+ def mediaStyle = ''
+ def archivesStyle = ''
+ def searchStyle = ''
+ def socialStyle = ''
+
+ def originalURI = request.getAttribute('originalURI')
+ if (originalURI.contains('posts')) {
+ postsStyle = 'current_page_item'
+ } else if (originalURI.contains('categories')) {
+ categoriesStyle = 'current_page_item'
+ } else if (originalURI.contains('media')) {
+ mediaStyle = 'current_page_item'
+ } else if (originalURI.contains('social')) {
+ socialStyle = 'current_page_item'
+ } else if (originalURI.contains('search')) {
+ searchStyle = 'current_page_item'
+ } else if (originalURI.contains('archives') || originalURI.contains('category')) {
+ archivesStyle = 'current_page_item'
+ } else {
+ homeStyle = 'current_page_item'
+ }
+
+ %>
+ Home
+ Archives
+ Social
+ Search
+ <% if (user && users.isUserLoggedIn() && users.isUserAdmin() && originalURI.contains('admin')) { %>
+ |
+ Posts
+ Categories
+ Media
+ <% } %>
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/includes/syntaxHighlighting.gtpl b/war/WEB-INF/includes/syntaxHighlighting.gtpl
new file mode 100644
index 0000000..65e039c
--- /dev/null
+++ b/war/WEB-INF/includes/syntaxHighlighting.gtpl
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/war/WEB-INF/lib/appengine-api-1.0-sdk-1.5.2.jar b/war/WEB-INF/lib/appengine-api-1.0-sdk-1.5.2.jar
new file mode 100644
index 0000000..93cc8dc
Binary files /dev/null and b/war/WEB-INF/lib/appengine-api-1.0-sdk-1.5.2.jar differ
diff --git a/war/WEB-INF/lib/appengine-api-labs-1.5.2.jar b/war/WEB-INF/lib/appengine-api-labs-1.5.2.jar
new file mode 100644
index 0000000..35aa061
Binary files /dev/null and b/war/WEB-INF/lib/appengine-api-labs-1.5.2.jar differ
diff --git a/war/WEB-INF/lib/gaelyk-1.0.jar b/war/WEB-INF/lib/gaelyk-1.0.jar
new file mode 100644
index 0000000..b08e57f
Binary files /dev/null and b/war/WEB-INF/lib/gaelyk-1.0.jar differ
diff --git a/war/WEB-INF/lib/groovy-all-1.8.1.jar b/war/WEB-INF/lib/groovy-all-1.8.1.jar
new file mode 100644
index 0000000..096db7a
Binary files /dev/null and b/war/WEB-INF/lib/groovy-all-1.8.1.jar differ
diff --git a/war/WEB-INF/lib/ocpsoft-pretty-time-1.0.7.jar b/war/WEB-INF/lib/ocpsoft-pretty-time-1.0.7.jar
new file mode 100644
index 0000000..327f38f
Binary files /dev/null and b/war/WEB-INF/lib/ocpsoft-pretty-time-1.0.7.jar differ
diff --git a/war/WEB-INF/logging.properties b/war/WEB-INF/logging.properties
new file mode 100644
index 0000000..4c9dd0c
--- /dev/null
+++ b/war/WEB-INF/logging.properties
@@ -0,0 +1,2 @@
+# Set the default logging level for all loggers to INFO
+.level = INFO
diff --git a/war/WEB-INF/pages/admin/adminCategories.gtpl b/war/WEB-INF/pages/admin/adminCategories.gtpl
new file mode 100644
index 0000000..fbdca2b
--- /dev/null
+++ b/war/WEB-INF/pages/admin/adminCategories.gtpl
@@ -0,0 +1,98 @@
+
+
+
+
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+ Administration -- Guillaume Laforge -- Groovy Blog
+
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Categories
+
+
+
+
+ <% request.categories.each { category -> %>
+
+ <% if (request.categoriesCount[category] == 0) { %>
+
+ <%}%>
+ ${category.name} —
+ ${category.description} —
+ (${request.categoriesCount[category]} posts )
+
+ <% } %>
+
+
+ Create a new category
+
+
+
+
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/admin/adminMedia.gtpl b/war/WEB-INF/pages/admin/adminMedia.gtpl
new file mode 100644
index 0000000..1bcb448
--- /dev/null
+++ b/war/WEB-INF/pages/admin/adminMedia.gtpl
@@ -0,0 +1,162 @@
+
+
+<% import com.google.appengine.api.blobstore.BlobInfoFactory %>
+
+
+ Media browser -- Guillaume Laforge -- Groovy Blog
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Media browser
+
+
+
+
+ Store new media
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/admin/adminPosts.gtpl b/war/WEB-INF/pages/admin/adminPosts.gtpl
new file mode 100644
index 0000000..a2eaf50
--- /dev/null
+++ b/war/WEB-INF/pages/admin/adminPosts.gtpl
@@ -0,0 +1,95 @@
+
+
+
+
+ Administration -- Guillaume Laforge -- Groovy Blog
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Drafts, posts, and pages
+
+
+
+
+
+
+ <%
+ ['Drafts': request.drafts, 'Posts': request.posts, 'Pages': request.pages].each { type, posts ->
+ %>
+
+
+ <% posts.each { post -> %>
+
+
+
+ <% if (post.type == 'page') { %>
+
+ <% } else if (post.type == 'post') { %>
+
+ <% } %>
+ ${post.title}
+
+
+ ${post.created.format('yyyy / MM / dd')}
+ <% if (post.categories) { %> — ${post.categories?.join(', ') ?: 'none'} <% } %>
+
+ <% } %>
+
+
+ <% } %>
+
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/admin/postEdit.gtpl b/war/WEB-INF/pages/admin/postEdit.gtpl
new file mode 100644
index 0000000..dcc9fa6
--- /dev/null
+++ b/war/WEB-INF/pages/admin/postEdit.gtpl
@@ -0,0 +1,143 @@
+
+
+<% import com.google.appengine.api.datastore.KeyFactory %>
+
+
+ Administration -- Guillaume Laforge -- Groovy Blog
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+<%
+ def id = params.id?.toLong()
+ def post = id ? KeyFactory.createKey('posts', id).get() : null
+%>
+
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/archive.gtpl b/war/WEB-INF/pages/archive.gtpl
new file mode 100644
index 0000000..1fc5a69
--- /dev/null
+++ b/war/WEB-INF/pages/archive.gtpl
@@ -0,0 +1,117 @@
+
+
+
+<%
+ int page = params.page ? params.page.toInteger() : 0
+
+ def originalURI = request.originalURI
+
+ def titlePrefix = ''
+ def origin = "archives"
+ if (originalURI.contains('category')) {
+ origin = "category/${params.category}"
+ titlePrefix = "Category ${params.category} -- "
+ } else {
+ titlePrefix = "Archives -- "
+ }
+%>
+
+
+ ${titlePrefix}Guillaume Laforge -- Groovy Blog
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+ <%
+ if (origin.startsWith("archives")) {
+ def date = [request.year, request.month, request.day].grep{ it }*.toString()*.padLeft(2, '0') %>
+
Archives ${date?'for':''} ${date.join('-')}
+ <% } else if (origin.startsWith("category")) { %>
+
Category ${params.category}
+ <% } %>
+
+ <%
+ request.posts.each { year, posts ->
+ %>
+
${year}
+
+ <%
+ posts.each { post ->
+ String day = post.created.date.toString().padLeft(2, '0')
+ String monthLetters = post.created.format('MMM').toUpperCase()
+ %>
+
+
+
+
+
${day}
+
${monthLetters}
+
+
+
+
+
+ <% if (post.categories) { %>
+ Posted in categories
+ <% } else { %>
+ No category
+ <% } %>
+ <% post.categories?.each { category -> %>
+
${category}
+ <% } %>
+
+
+
+
+
+
+
+
+ <%
+ }
+ }
+ %>
+
+
+ <% if (request.posts || page) { %>
+
+ <% } %>
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/article.gtpl b/war/WEB-INF/pages/article.gtpl
new file mode 100644
index 0000000..1653adb
--- /dev/null
+++ b/war/WEB-INF/pages/article.gtpl
@@ -0,0 +1,123 @@
+
+
+
+
+ ${request.post.title} -- Guillaume Laforge -- Groovy Blog
+ <% include '/WEB-INF/includes/syntaxHighlighting.gtpl' %>
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+ <% def post = request.post %>
+
${post.title}
+
+
+ <% if (user && users.isUserLoggedIn() && users.isUserAdmin()) { %>
+
+
+
+ <% } %>
+ Posted on ${post.created.format('dd MMMM, yyyy')} (${post.created.pretty()})
+
+
+ <% if (request.getAttribute('originalURI').contains('article')) { %>
+
+
+
+
+
+ <% } %>
+
+
+ ${post.content}
+
+
+ <% if (post.categories) { %>
+
+ In categories:
+ <% post.categories.each { category -> %>
+
${category}
+ <% } %>
+
+ <% } %>
+
+
+
+ <% if (request.getAttribute('originalURI').contains('article')) { %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <% } %>
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/error.gtpl b/war/WEB-INF/pages/error.gtpl
new file mode 100644
index 0000000..d799416
--- /dev/null
+++ b/war/WEB-INF/pages/error.gtpl
@@ -0,0 +1,53 @@
+
+
+
+
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+ An error occured -- Guillaume Laforge -- Groovy Blog
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
An error occured
+
+
+ <% if (request.getAttribute('code')) { %>
+
Error code:
+ ${request.getAttribute('code')}
+ <% } else if (request.getAttribute('ex')) { %>
+ Exception:
+ ${request.getAttribute('ex')}
+ <% } %>
+ Message:
+ ${request.getAttribute('msg')}
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/home.gtpl b/war/WEB-INF/pages/home.gtpl
new file mode 100644
index 0000000..43f3fe5
--- /dev/null
+++ b/war/WEB-INF/pages/home.gtpl
@@ -0,0 +1,101 @@
+
+
+
+
+ Guillaume Laforge -- Groovy Blog
+ <% include '/WEB-INF/includes/syntaxHighlighting.gtpl' %>
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+ <%
+ // no articles to display
+ if (!request.posts) {
+ %>
+
No more posts
+ <%
+ }
+ %>
+ <%
+ request.posts.each { post ->
+ %>
+
+
+
+
+
+
+ <% if (user && users.isUserLoggedIn() && users.isUserAdmin()) { %>
+
+
+
+ <% } %>
+ Posted on ${post.created.format('dd MMMM, yyyy')} (${post.created.pretty()})
+
+
+
+ ${post.content}
+
+
+ <% if (post.categories) { %>
+
+ In categories:
+ <% post.categories.each { category -> %>
+
${category}
+ <% } %>
+
+ <% } %>
+
+
+
+ <%
+ }
+ %>
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/notAllowed.gtpl b/war/WEB-INF/pages/notAllowed.gtpl
new file mode 100644
index 0000000..3997f6a
--- /dev/null
+++ b/war/WEB-INF/pages/notAllowed.gtpl
@@ -0,0 +1,47 @@
+
+
+
+
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+ Not allowed -- Guillaume Laforge -- Groovy Blog
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Access not allowed
+
+
+
You must be administrator to be able to administer this weblog.
+
+
Please sign-in back with an administrator account
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/notFound.gtpl b/war/WEB-INF/pages/notFound.gtpl
new file mode 100644
index 0000000..003417b
--- /dev/null
+++ b/war/WEB-INF/pages/notFound.gtpl
@@ -0,0 +1,48 @@
+
+
+
+
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+ Page not found -- Guillaume Laforge -- Groovy Blog
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Page not found
+
+
+
The page you were looking for couldn't be found.
+
+
Otherwise, please one of the links in the navigation bars
+ to find your way in this website.
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/search.gtpl b/war/WEB-INF/pages/search.gtpl
new file mode 100644
index 0000000..a248e19
--- /dev/null
+++ b/war/WEB-INF/pages/search.gtpl
@@ -0,0 +1,56 @@
+
+
+
+
+ Search this site -- Guillaume Laforge -- Groovy Blog
+
+
+
+
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Search this site
+
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/pages/social.gtpl b/war/WEB-INF/pages/social.gtpl
new file mode 100644
index 0000000..ccd264a
--- /dev/null
+++ b/war/WEB-INF/pages/social.gtpl
@@ -0,0 +1,66 @@
+
+
+
+
+ Social (twitter, delicious, google reader) -- Guillaume Laforge -- Groovy Blog
+
+ <% include '/WEB-INF/includes/meta.gtpl' %>
+
+
+
+
+<% include '/WEB-INF/includes/header.gtpl' %>
+<% include '/WEB-INF/includes/navigation.gtpl' %>
+
+
+
+
+
+
+
+
+
+
+
+
Social
+
+
+
+
+ This page gathers:
+
+
+
+ <% request.items.each { date, entries -> %>
+
${date.format('yyyy / MM / dd')}
+
+ <% } %>
+
+
+
+
+
+
+
+ <% include '/WEB-INF/includes/left.gtpl' %>
+
+
+
+
+
+<% include '/WEB-INF/includes/footer.gtpl' %>
+<% include '/WEB-INF/includes/bottom.gtpl' %>
+
+
+
\ No newline at end of file
diff --git a/war/WEB-INF/plugins.groovy b/war/WEB-INF/plugins.groovy
new file mode 100644
index 0000000..8942d3b
--- /dev/null
+++ b/war/WEB-INF/plugins.groovy
@@ -0,0 +1 @@
+install prettyTime
\ No newline at end of file
diff --git a/war/WEB-INF/plugins/prettyTime.groovy b/war/WEB-INF/plugins/prettyTime.groovy
new file mode 100644
index 0000000..ddeca2e
--- /dev/null
+++ b/war/WEB-INF/plugins/prettyTime.groovy
@@ -0,0 +1,9 @@
+import com.ocpsoft.pretty.time.PrettyTime
+
+categories PrettyTimeCategory
+
+class PrettyTimeCategory {
+ static String pretty(Date date) {
+ new PrettyTime().format(date)
+ }
+}
\ No newline at end of file
diff --git a/war/WEB-INF/routes.groovy b/war/WEB-INF/routes.groovy
new file mode 100644
index 0000000..1cdd4b5
--- /dev/null
+++ b/war/WEB-INF/routes.groovy
@@ -0,0 +1,50 @@
+def cache = localMode ? 0 : 1.hour
+
+get "/", forward: '/home.groovy', cache: cache
+get "/p@p", forward: '/home.groovy?page=@p', validate: { p ==~ /\d+/ }, cache: cache
+
+get "/article/@title", forward: '/article.groovy?title=@title', cache: cache
+get "/page/@title", forward: '/article.groovy?title=@title', cache: cache
+get "/live/@title", forward: '/article.groovy?title=@title'
+
+get "/archives/p@p", forward: '/archive.groovy?page=@p', validate: { p ==~ /\d+/ }, cache: cache
+get "/archives", forward: '/archive.groovy', cache: cache
+
+get "/category/@cat/p@p", forward: '/category.groovy?category=@cat&page=@p', validate: { p ==~ /\d+/ }, cache: cache
+get "/category/@cat", forward: '/category.groovy?category=@cat', cache: cache
+
+get "/media/@file.@ext", forward: '/media.groovy?fileName=@file.@ext', cache: cache
+
+get "/feed/atom/@cat", forward: '/feed.groovy?category=@cat', cache: cache
+get "/feed/atom", forward: '/feed.groovy', cache: cache
+
+get "/social", forward: '/social.groovy', cache: cache
+
+get "/search", forward: '/search.groovy', cache: 24.hours
+
+get "/admin/categories", forward: '/admin/adminCategories.groovy'
+get "/admin/posts", forward: '/admin/adminPosts.groovy'
+get "/admin/posts/create", forward: '/WEB-INF/pages/admin/postEdit.gtpl'
+get "/admin/media", forward: '/admin/adminMedia.groovy'
+get "/admin/media/p@p", forward: '/admin/adminMedia.groovy?page=@p'
+get "/admin/mediaSelector", forward: '/admin/mediaSelector.groovy'
+get "/admin/clearCache", forward: '/admin/clearCache.groovy'
+
+post "/admin/media/add", forward: '/admin/mediaAdd.groovy'
+post "/admin/media/delete/@bk", forward: '/admin/mediaDelete.groovy?blobKey=@bk'
+post "/admin/posts/delete/@id", forward: '/admin/postDelete.groovy?id=@id'
+post "/admin/posts/edit/@id", forward: '/WEB-INF/pages/admin/postEdit.gtpl?id=@id'
+post "/admin/posts/save", forward: '/admin/postSave.groovy'
+post "/admin/categories/add", forward: '/admin/categoryAdd.groovy'
+post "/admin/categories/delete/@cat", forward: '/admin/categoryDelete.groovy?categoryName=@cat'
+
+// route for 404 errors defined in web.xml
+get "/not-found", forward: '/WEB-INF/pages/notFound.gtpl', cache: 24.hours
+// route for 403 errors defined in web.xml
+get "/not-allowed", forward: '/WEB-INF/pages/notAllowed.gtpl', cache: 24.hours
+// when some error happens
+get "/error", forward: '/WEB-INF/pages/error.gtpl'
+
+get "/favicon.ico", redirect: "/images/gaelyk-favicon.png"
+
+email to: "/admin/email.groovy"
diff --git a/war/WEB-INF/web.xml b/war/WEB-INF/web.xml
new file mode 100644
index 0000000..f852aa4
--- /dev/null
+++ b/war/WEB-INF/web.xml
@@ -0,0 +1,106 @@
+
+
+ groovyx.gaelyk.GaelykServletContextListener
+
+
+
+ GroovletServlet
+ groovyx.gaelyk.GaelykServlet
+
+ verbose
+
+ false
+
+
+ 1
+
+
+ TemplateServlet
+ groovyx.gaelyk.GaelykTemplateServlet
+
+
+ generated.by
+ false
+
+
+ verbose
+
+ false
+
+
+ 1
+
+
+
+ RoutesFilter
+ groovyx.gaelyk.routes.RoutesFilter
+
+
+
+ GroovletServlet
+ *.groovy
+
+
+ TemplateServlet
+ *.gtpl
+
+
+
+ RoutesFilter
+ /*
+ FORWARD
+ REQUEST
+
+ ERROR
+
+
+
+
+ /admin/*
+
+
+ admin
+
+
+
+
+ /_ah/mail/*
+
+
+ admin
+
+
+
+
+
+
+ 404
+
+ /not-found
+
+
+ 403
+ /not-allowed
+
+
+ 500
+ /error
+
+
+ java.lang.Throwable
+ /error
+
+
+
+
+
\ No newline at end of file
diff --git a/war/css/chosen.css b/war/css/chosen.css
new file mode 100644
index 0000000..59fe7cf
--- /dev/null
+++ b/war/css/chosen.css
@@ -0,0 +1,310 @@
+div.chzn-container {
+ font-size: 13px;
+ position: relative;
+}
+
+div.chzn-container input {
+ background: #fff;
+ background: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+ background: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+ border: 1px solid #aaa;
+ font-family: sans-serif;
+ font-size: 1em;
+ margin: 0px;
+ padding: 4px 5px;
+ outline: none;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ -o-border-radius: 3px;
+ -ms-border-radius: 3px;
+ -khtml-border-radius: 3px;
+ border-radius: 3px;
+}
+div.chzn-container textarea:focus {
+ border-color: #058cf5;
+ -moz-box-shadow: 0px 0px 3px #aaa;
+ -webkit-box-shadow: 0px 0px 3px #aaa;
+ box-shadow: 0px 0px 3px #aaa;
+}
+
+
+div.chzn-container div.chzn-drop {
+ background: #FFF;
+ border: 1px solid #aaa;
+ border-width: 0 1px 1px;
+ left: 0;
+ position: absolute;
+ top: 29px;
+ -webkit-box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.15);
+ -moz-box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.15);
+ box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15);
+ z-index: 20;
+}
+div.chzn-container-single div.chzn-drop {
+ -moz-border-radius: 0 0 4px 4px;
+ -webkit-border-radius: 0 0 4px 4px;
+ -o-border-radius: 0 0 4px 4px;
+ -ms-border-radius: 0 0 4px 4px;
+ -khtml-border-radius: 0 0 4px 4px;
+ border-radius: 0 0 4px 4px;
+}
+
+
+/* SINGLE */
+div.chzn-container a.chzn-single {
+ background: #FFF;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
+ background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+ border: 1px solid #aaa;
+ display: block;
+ overflow: hidden;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ -o-border-radius: 4px;
+ -ms-border-radius: 4px;
+ -khtml-border-radius: 4px;
+ border-radius: 4px;
+ height: 25px;
+ color: #444;
+ line-height: 26px;
+ padding: 0px 0px 0px 8px;
+ position: relative;
+ text-decoration: none;
+ z-index: 19;
+ white-space: nowrap;
+}
+div.chzn-container a.chzn-single span {
+ display: block;
+ margin-right: 26px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+div.chzn-container a.chzn-single div {
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-right-radius: 4px;
+ -o-border-top-right-radius: 4px;
+ -ms-border-top-right-radius: 4px;
+ -khtml-border-top-right-radius: 4px;
+ border-top-right-radius: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -webkit-border-bottom-right-radius: 42px;
+ -o-border-bottom-right-radius: 4px;
+ -ms-border-bottom-right-radius: 4px;
+ -khtml-border-bottom-right-radius: 4px;
+ border-bottom-right-radius: 4px;
+ background: #ccc;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+ background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+ border-left: 1px solid #aaa;
+ display: block;
+ height: 100%;
+ position: absolute;
+ right: 0;
+ top: 0;
+ width: 18px;
+}
+div.chzn-container a.chzn-single div b {
+ background: url('/images/chosen-sprite.png') no-repeat 0 1px;
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+div.chzn-container div.chzn-search {
+ padding: 3px 4px;
+ margin: 0px;
+ white-space: nowrap;
+}
+div.chzn-container div.chzn-search input {
+ background: url('/images/chosen-sprite.png') no-repeat 97% -35px, #ffffff;
+ background: url('/images/chosen-sprite.png') no-repeat 97% -35px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+ background: url('/images/chosen-sprite.png') no-repeat 97% -35px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+ -moz-border-radius: 0px;
+ -webkit-border-radius: 0px;
+ -o-border-radius: 0px;
+ -ms-border-radius: 0px;
+ -khtml-border-radius: 0px;
+ border-radius: 0px;
+ margin: 1px 0;
+ outline: 0;
+}
+
+
+/* Multi */
+div.chzn-container ul.chzn-choices {
+ background: #fff;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+ background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+ margin: 0;
+ cursor: text;
+ border: 1px solid #aaa;
+ overflow: hidden;
+ height: auto !important;
+ height: 1%;
+ padding: 0;
+ position: relative;
+}
+div.chzn-container ul.chzn-choices:focus {
+ border-color: #058cf5;
+ -moz-box-shadow: 0px 0px 5px #999;
+ -webkit-box-shadow: 0px 0px 5px #999;
+ box-shadow: 0px 0px 5px #999;
+}
+div.chzn-container ul.chzn-choices li {
+ float: left;
+ list-style-type: none;
+ margin: 0px;
+}
+div.chzn-container ul.chzn-choices li.search-field {
+ margin: 0px;
+ white-space: nowrap;
+ padding: 0px;
+}
+div.chzn-container ul.chzn-choices li.search-field input {
+ color: #666;
+ background: transparent !important;
+ border: 0px !important;
+ padding: 5px;
+ margin: 1px 0;
+ outline: 0;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+}
+div.chzn-container ul.chzn-choices li.search-field input.default {
+ color: #999;
+}
+div.chzn-container ul.chzn-choices li.search-choice {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: #e4e4e4;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee));
+ background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
+ color: #333;
+ border: 1px solid #b4b4b4;
+ line-height: 13px;
+ padding: 3px 19px 3px 6px;
+ position: relative;
+ margin: 3px 0px 3px 5px;
+}
+div.chzn-container ul.chzn-choices li.search-choice span {
+ cursor: default;
+}
+div.chzn-container ul.chzn-choices li.search-choice.search-choice-focus {
+ background: #d4d4d4;
+}
+div.chzn-container ul.chzn-choices li.search-choice a.search-choice-close {
+ position: absolute;
+ right: 5px;
+ top: 6px;
+ display: block;
+ width: 8px;
+ height: 9px;
+ font-size: 1px;
+ background: url(/images/chosen-sprite.png) right top no-repeat;
+}
+div.chzn-container ul.chzn-choices li.search-choice a.search-choice-close:hover {
+ background-position: right -9px;
+}
+div.chzn-container ul.chzn-choices li.search-choice.search-choice-focus a.search-choice-close {
+ background-position: right -9px;
+}
+
+
+/* Results */
+div.chzn-container ul.chzn-results {
+ margin: 0 4px 4px 0;
+ max-height: 190px;
+ padding: 0 0 0 4px;
+ position: relative;
+ overflow-x: hidden;
+ overflow-y: auto;
+ z-index: 20;
+}
+div.chzn-container-multi ul.chzn-results {
+ margin: -1px 0 0;
+ padding: 0;
+}
+div.chzn-container-multi ul.chzn-results li {
+ border-left: 0px !important;
+ border-right: 0px !important;
+}
+div.chzn-container ul.chzn-results li {
+ line-height: 80%;
+ padding: 7px 7px 8px;
+ z-index: 22;
+ margin: 0;
+ list-style-type: none;
+}
+div.chzn-container ul.chzn-results li.active-result {
+ cursor: pointer;
+}
+div.chzn-container ul.chzn-results li em {
+ font-style: normal;
+ background: #FEFFDC;
+}
+div.chzn-container ul.chzn-results li.highlighted {
+ background: #3875d7;
+ color: #fff;
+}
+div.chzn-container ul.chzn-results li.highlighted em {
+ background: transparent;
+}
+div.chzn-container ul.chzn-results li.no-results {
+ background: #F4F4F4;
+}
+div.chzn-container ul.chzn-results li.group-result {
+ cursor: default;
+ color: #999;
+ font-weight: bold;
+}
+div.chzn-container ul.chzn-results li.group-option {
+ padding-left: 20px;
+}
+
+div.chzn-container-multi div.chzn-drop li.result-selected {
+ display: none;
+}
+
+
+
+/* Active */
+div.chzn-container-active a.chzn-single {
+ -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ border: 1px solid #5897fb;
+}
+div.chzn-container-active a.chzn-single-with-drop {
+ border: 1px solid #aaa;
+ border-width: 1px 1px 1px;
+ -moz-box-shadow: 0px 1px 0px #FFF inset;
+ -webkit-box-shadow: 0px 1px 0px #FFF inset;
+ box-shadow: 0px 1px 0px #FFF inset;
+ background: #EEE;
+ background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
+ background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+ -webkit-border-bottom-left-radius: 0px;
+ -webkit-border-bottom-right-radius: 0px;
+ -moz-border-radius-bottomleft: 0px;
+ -moz-border-radius-bottomright: 0px;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+}
+div.chzn-container-active a.chzn-single-with-drop div {
+ background: transparent;
+ border-left: none;
+}
+div.chzn-container-active a.chzn-single-with-drop div b {
+ background-position: -18px 1px;
+}
+div.chzn-container-active ul.chzn-choices {
+ z-index: 21;
+ -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ -moz-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ border: 1px solid #5897fb;
+}
+div.chzn-container-active ul.chzn-choices input {
+ color: #111 !important;
+}
diff --git a/war/css/google-search.css b/war/css/google-search.css
new file mode 100644
index 0000000..37853e3
--- /dev/null
+++ b/war/css/google-search.css
@@ -0,0 +1,183 @@
+.gsc-control-cse {
+ font-family: Arial, sans-serif;
+ border-color: #FFFFFF;
+ background-color: #FFFFFF;
+}
+
+input.gsc-input {
+ border-color: #DFDFDA;
+
+ height: 28px;
+
+ -moz-box-shadow: 2px 1px 3px #AAA;
+ -webkit-box-shadow: 2px 1px 3px #AAA;
+ box-shadow: 2px 1px 3px #AAA;
+
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+}
+
+input.gsc-search-button {
+ border-color: #666666;
+ background-color: #CECECE;
+}
+
+.gsc-tabHeader.gsc-tabhInactive {
+ border-color: #E9E9E9;
+ background-color: #E9E9E9;
+}
+
+.gsc-tabHeader.gsc-tabhActive {
+ border-top-color: #905010;
+ border-left-color: #E9E9E9;
+ border-right-color: #E9E9E9;
+ background-color: #FFFFFF;
+}
+
+.gsc-tabsArea {
+ border-color: #E9E9E9;
+}
+
+.gsc-webResult.gsc-result,
+.gsc-results .gsc-imageResult {
+ border-color: #FFFFFF;
+ background-color: #FFFFFF;
+}
+
+.gsc-webResult.gsc-result:hover,
+.gsc-imageResult:hover {
+ border-color: #FFFFFF;
+ background-color: #FFFFFF;
+}
+
+.gs-webResult.gs-result a.gs-title:link,
+.gs-webResult.gs-result a.gs-title:link b,
+.gs-imageResult a.gs-title:link,
+.gs-imageResult a.gs-title:link b {
+ color: #204060;
+}
+
+.gs-webResult.gs-result a.gs-title:visited,
+.gs-webResult.gs-result a.gs-title:visited b,
+.gs-imageResult a.gs-title:visited,
+.gs-imageResult a.gs-title:visited b {
+ color: #204060;
+}
+
+.gs-webResult.gs-result a.gs-title:hover,
+.gs-webResult.gs-result a.gs-title:hover b,
+.gs-imageResult a.gs-title:hover,
+.gs-imageResult a.gs-title:hover b {
+ color: #204060;
+}
+
+.gs-webResult.gs-result a.gs-title:active,
+.gs-webResult.gs-result a.gs-title:active b,
+.gs-imageResult a.gs-title:active,
+.gs-imageResult a.gs-title:active b {
+ color: #204060;
+}
+
+.gsc-cursor-page {
+ color: #204060;
+}
+
+a.gsc-trailing-more-results:link {
+ color: #204060;
+}
+
+.gs-webResult .gs-snippet,
+.gs-imageResult .gs-snippet {
+ color: #000000;
+}
+
+.gs-webResult div.gs-visibleUrl,
+.gs-imageResult div.gs-visibleUrl {
+ color: #905010;
+}
+
+.gs-webResult div.gs-visibleUrl-short {
+ color: #905010;
+}
+
+.gs-webResult div.gs-visibleUrl-short {
+ display: none;
+}
+
+.gs-webResult div.gs-visibleUrl-long {
+ display: block;
+}
+
+.gsc-cursor-box {
+ border-color: #FFFFFF;
+ text-align: center;
+}
+
+.gsc-results .gsc-cursor-box .gsc-cursor-page,
+.gsc-results .gsc-cursor-box .gsc-cursor-current-page {
+ margin: 5px;
+ padding: 5px;
+ border: 1px solid #E9E9E9;
+
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+}
+
+.gsc-results .gsc-cursor-box .gsc-cursor-page {
+ border-color: #E9E9E9;
+ background-color: #FFFFFF;
+ color: #204060;
+}
+
+.gsc-results .gsc-cursor-box .gsc-cursor-current-page {
+ border-color: #905010;
+ background-color: #F7F7F2;
+ color: #204060;
+}
+
+.gs-promotion {
+ border-color: #204060;
+ background-color: #FFFFFF;
+}
+
+.gs-promotion a.gs-title:link,
+.gs-promotion a.gs-title:link *,
+.gs-promotion .gs-snippet a:link {
+ color: #204060;
+}
+
+.gs-promotion a.gs-title:visited,
+.gs-promotion a.gs-title:visited *,
+.gs-promotion .gs-snippet a:visited {
+ color: #204060;
+}
+
+.gs-promotion a.gs-title:hover,
+.gs-promotion a.gs-title:hover *,
+.gs-promotion .gs-snippet a:hover {
+ color: #204060;
+}
+
+.gs-promotion a.gs-title:active,
+.gs-promotion a.gs-title:active *,
+.gs-promotion .gs-snippet a:active {
+ color: #204060;
+}
+
+.gs-promotion .gs-snippet,
+.gs-promotion .gs-title .gs-promotion-title-right,
+.gs-promotion .gs-title .gs-promotion-title-right * {
+ color: #000000;
+}
+
+.gs-promotion .gs-visibleUrl,
+.gs-promotion .gs-visibleUrl-short {
+ color: #905010;
+}
+
+.gs-no-results-result .gs-snippet, .gs-error-result .gs-snippet {
+ background-color: #F7F7F2;
+ border-color: #E6E6E6;
+}
diff --git a/war/css/idea.css b/war/css/idea.css
new file mode 100644
index 0000000..3d8baab
--- /dev/null
+++ b/war/css/idea.css
@@ -0,0 +1,118 @@
+/*
+
+Intellij Idea-like styling (c) Vasily Polovnyov
+
+*/
+
+pre code {
+ display: block; padding: 0.5em;
+ color: #000;
+ background: #fff;
+}
+
+pre .subst,
+pre .title {
+ font-weight: normal;
+ color: #000;
+}
+
+pre .comment,
+pre .template_comment,
+pre .javadoc,
+pre .diff .header {
+ color: #808080;
+ font-style: italic;
+}
+
+pre .annotation,
+pre .decorator,
+pre .preprocessor,
+pre .doctype,
+pre .pi,
+pre .chunk,
+pre .shebang,
+pre .apache .cbracket,
+pre .input_number {
+ color: #808000;
+}
+
+pre .tag,
+pre .pi {
+ background: #efefef;
+}
+
+pre .tag .title,
+pre .id,
+pre .attr_selector,
+pre .pseudo,
+pre .literal,
+pre .keyword,
+pre .hexcolor,
+pre .css .function,
+pre .ini .title,
+pre .css .class,
+pre .list .title,
+pre .tex .command {
+ font-weight: bold;
+ color: #000080;
+}
+
+pre .attribute,
+pre .rules .keyword,
+pre .number,
+pre .date,
+pre .regexp,
+pre .tex .special {
+ font-weight: bold;
+ color: #0000ff;
+}
+
+pre .number,
+pre .regexp {
+ font-weight: normal;
+}
+
+pre .string,
+pre .value,
+pre .filter .argument,
+pre .css .function .params,
+pre .apache .tag {
+ color: #008000;
+ font-weight: bold;
+}
+
+pre .symbol,
+pre .ruby .symbol .string,
+pre .ruby .symbol .keyword,
+pre .ruby .symbol .keymethods,
+pre .char,
+pre .tex .formula {
+ color: #000;
+ background: #d0eded;
+ font-style: italic;
+}
+
+pre .phpdoc,
+pre .yardoctag,
+pre .javadoctag {
+ text-decoration: underline;
+}
+
+pre .variable,
+pre .envvar,
+pre .apache .sqbracket,
+pre .nginx .built_in {
+ color: #660e7a;
+}
+
+pre .addition {
+ background: #baeeba;
+}
+
+pre .deletion {
+ background: #ffc8bd;
+}
+
+pre .diff .change {
+ background: #bccff9;
+}
diff --git a/war/css/jquery.cleditor.css b/war/css/jquery.cleditor.css
new file mode 100755
index 0000000..0302a72
--- /dev/null
+++ b/war/css/jquery.cleditor.css
@@ -0,0 +1,112 @@
+.cleditorMain {
+ border: 1px solid #999;
+ padding: 0 1px 1px;
+ background-color: white
+}
+
+.cleditorMain iframe {
+ border: none;
+ margin: 0;
+ padding: 0
+}
+
+.cleditorMain textarea {
+ border: none;
+ margin: 0;
+ padding: 0;
+ overflow-y: scroll;
+ font: 10pt Arial, Verdana;
+ resize: none;
+ outline: none /* webkit grip focus */
+}
+
+.cleditorToolbar {
+ background: url('/images/toolbar.gif') repeat
+}
+
+.cleditorGroup {
+ float: left;
+ height: 26px
+}
+
+.cleditorButton {
+ float: left;
+ width: 24px;
+ height: 24px;
+ margin: 1px 0 1px 0;
+ background: url('/images/buttons.gif')
+}
+
+.cleditorDisabled {
+ opacity: 0.3;
+ filter: alpha(opacity = 30)
+}
+
+.cleditorDivider {
+ float: left;
+ width: 1px;
+ height: 23px;
+ margin: 1px 0 1px 0;
+ background: #CCC
+}
+
+.cleditorPopup {
+ border: solid 1px #999;
+ background-color: white;
+ position: absolute;
+ font: 10pt Arial, Verdana;
+ cursor: default;
+ z-index: 10000
+}
+
+.cleditorList div {
+ padding: 2px 4px 2px 4px
+}
+
+.cleditorList p,
+.cleditorList h1,
+.cleditorList h2,
+.cleditorList h3,
+.cleditorList h4,
+.cleditorList h5,
+.cleditorList h6,
+.cleditorList font {
+ padding: 0;
+ margin: 0;
+ background-color: Transparent
+}
+
+.cleditorColor {
+ width: 150px;
+ padding: 1px 0 0 1px
+}
+
+.cleditorColor div {
+ float: left;
+ width: 14px;
+ height: 14px;
+ margin: 0 1px 1px 0
+}
+
+.cleditorPrompt {
+ background-color: #F6F7F9;
+ padding: 4px;
+ font-size: 8.5pt
+}
+
+.cleditorPrompt input,
+.cleditorPrompt textarea {
+ font: 8.5pt Arial, Verdana;
+ margin: 4px;
+}
+
+.cleditorPrompt input {
+ padding: 2px;
+}
+
+.cleditorMsg {
+ background-color: #FDFCEE;
+ width: 150px;
+ padding: 4px;
+ font-size: 8.5pt
+}
diff --git a/war/css/stabs.css b/war/css/stabs.css
new file mode 100644
index 0000000..004040c
--- /dev/null
+++ b/war/css/stabs.css
@@ -0,0 +1,105 @@
+/*
+.tabs, .tabs li {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ float: left;
+}
+
+.tabs {
+ width: 100%;
+ border-bottom: 0;
+}
+
+.tabs li {
+ margin-left: 4px;
+}
+
+.tabs a {
+
+ border: 1px solid #ddd;
+ background: #f5f5f5;
+ padding: 0 50px;
+ line-height: 30px;
+ display: block;
+ text-decoration: none;
+ margin-bottom: -1px
+}
+
+.tabs a.active {
+ background: #fff;
+ font-weight: bold;
+ border-bottom: 1px solid white;
+}
+
+.tabs a:hover {
+ background: #fff
+}
+
+.tab {
+ border-top: 1px solid #ddd;
+ clear: both;
+ position: relative;
+ padding: 10px
+}
+
+.tab h2 {
+ margin-top: 0
+}
+*/
+
+
+.tabs, .tabs li {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ float: left
+}
+
+.tabs {
+ width: 100%;
+ border-bottom: 1px solid #ddd;
+}
+
+.tabs li {
+ margin-left: 4px;
+}
+
+.tabs a {
+ -webkit-border-top-left-radius: 10px;
+ -webkit-border-top-right-radius: 10px;
+ -moz-border-radius-topleft: 10px;
+ -moz-border-radius-topright: 10px;
+ border-top-left-radius: 10px;
+ border-top-right-radius: 10px;
+
+ border: 1px solid #ddd;
+ background: #f5f5f5;
+ padding: 0 50px;
+ line-height: 25px;
+ display: block;
+ height: 24px;
+ text-decoration: none;
+ margin-bottom: -1px;
+}
+
+.tabs a.active {
+ background: #fff;
+ border-bottom: 1px solid white;
+ font-weight: bold;
+}
+
+.tabs a:hover {
+ background: #f6e5d6;
+ text-decoration: none;
+ color: black;
+}
+
+.tab {
+ border-top: 0;
+ clear: both;
+ position: relative;
+ padding: 10px;
+ margin-bottom: 10px;
+}
+
diff --git a/war/css/style.css b/war/css/style.css
new file mode 100755
index 0000000..213a80b
--- /dev/null
+++ b/war/css/style.css
@@ -0,0 +1,1165 @@
+/*
+ Template name: Freshmade Software
+ Template URI: http://templates.arcsin.se/freshmade-software-website-template/
+ Release date: 2009-06-21
+ Last updated: 2009-08-10
+ Description: A software company styled template in light colors of white, orange and brown.
+ Author: Viktor Persson
+ Author URI: http://arcsin.se/
+
+ This template is licensed under a Creative Commons Attribution 2.5 License:
+ http://templates.arcsin.se/license/
+*/
+
+/*
+ Reset
+------------------------------------------------------------------- */
+
+html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p,
+blockquote, pre, a, abbr, acronym, address, code, del, dfn, em,
+img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table,
+caption, tbody, tfoot, thead, tr, th, td, textarea, input, select {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+}
+
+table, td, th {
+ vertical-align: middle;
+}
+
+pre, blockquote, fieldset {
+ background: #F7F7F2 url('/images/box.gif') no-repeat left top;
+}
+
+pre, blockquote, fieldset, .framed {
+ border: 10px solid white;
+
+ -moz-box-shadow: 3px 2px 8px #AAA;
+ -webkit-box-shadow: 3px 2px 8px #AAA;
+ box-shadow: 3px 2px 8px #AAA;
+
+ -webkit-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+}
+
+blockquote {
+ display: block;
+ padding: 10px 60px;
+}
+
+blockquote:after {
+ color: #ffe0bf;
+ display: block;
+ font-size: 700%;
+ width: 50px;
+ content: '\201C';
+ height: 0;
+ margin-left: -0.6em;
+}
+
+div.cleditorPopup.cleditorList blockquote {
+ width: 100px;
+}
+
+a img {
+ border: none;
+}
+
+:focus {
+ outline: 0;
+}
+
+/*
+ General
+------------------------------------------------------------------- */
+
+html {
+ height: 100%;
+ padding-bottom: 1px; /* force scrollbars */
+}
+
+body {
+ background: #A9A9A9;
+ color: #444;
+ font: normal 75% sans-serif;
+ line-height: 1.5;
+}
+
+/*
+ Typography
+------------------------------------------------------------------- */
+
+/* Headings */
+
+h1, h2, h3, h4, h5, h6 {
+ color: #444;
+ font-weight: normal;
+ line-height: 1;
+ margin-bottom: 0.3em;
+}
+
+h4, h5, h6 {
+ font-weight: bold;
+}
+
+h1 {
+ font-size: 2.6em;
+}
+
+h2 {
+ font-size: 2em;
+}
+
+h3 {
+ font-size: 1.5em;
+}
+
+h4 {
+ font-size: 1.25em;
+}
+
+h5 {
+ font-size: 1.1em;
+}
+
+h6 {
+ font-size: 1em;
+}
+
+h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {
+ margin: 0;
+}
+
+/* Text elements */
+
+p {
+ margin-bottom: 1em;
+}
+
+a:focus, a:hover {
+ color: #002;
+}
+
+a {
+ color: #246;
+ text-decoration: underline;
+}
+
+abbr, acronym {
+ border-bottom: 1px dotted #666;
+}
+
+address {
+ margin-bottom: 1.5em;
+}
+
+blockquote {
+ margin: 1.2em;
+ line-height: 1.4em;
+}
+
+blockquote span {
+ font-size: 1.4em;
+ color: #000;
+}
+
+del {
+ color: #666;
+}
+
+em, dfn, blockquote, address {
+ font-style: italic;
+}
+
+strong, dfn {
+ font-weight: bold;
+}
+
+sup, sub {
+ line-height: 0;
+}
+
+cite {
+ color: #666;
+}
+
+pre {
+ margin: 1.2em;
+ white-space: pre;
+}
+
+pre, code, tt {
+ font: 1.1em monospace;
+ line-height: 1.5;
+
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+pre code {
+ background: #F7F7F2 url('/images/box.gif') no-repeat left top;
+}
+
+/* Lists */
+
+li ul, li ol {
+ margin-left: 1.5em;
+}
+
+ul, ol {
+ margin: 0 1.5em 1.5em 1.5em;
+}
+
+ul {
+ list-style-type: disc;
+}
+
+ol {
+ list-style-type: decimal;
+}
+
+dl {
+ margin: 0 0 1.5em 0;
+}
+
+dl dt {
+ font-weight: bold;
+}
+
+dd {
+ margin-left: 1.5em;
+}
+
+/* Tables */
+
+table {
+ margin-bottom: 1.4em;
+ width: 100%;
+}
+
+th {
+ font-weight: bold;
+}
+
+thead th {
+ background: #C3D9FF;
+}
+
+th, td, caption {
+ padding: 4px 10px 4px 5px;
+}
+
+tr.even td {
+ background: #F2F6FA;
+}
+
+tfoot {
+ font-style: italic;
+}
+
+caption {
+ background: #EEE;
+}
+
+table.data-table {
+ border: 1px solid #EEE;
+ margin-bottom: 2em;
+ width: 100%;
+}
+
+table.data-table th {
+ background: #EEE;
+ border: 1px solid #DDD;
+ color: #555;
+ text-align: left;
+}
+
+table.data-table tr {
+ border-bottom: 1px solid #EEE;
+}
+
+table.data-table td, table th {
+ padding: 8px 10px;
+}
+
+table.data-table td {
+ background: #F6F6F6;
+ border: 1px solid #E6E6E6;
+}
+
+table.data-table tr.even td {
+ background: #FCFCFC;
+}
+
+/* Misc classes */
+
+.small {
+ font-size: 0.9em;
+}
+
+.smaller {
+ font-size: 0.8em;
+}
+
+.smallest {
+ font-size: 0.7em;
+}
+
+.large {
+ font-size: 1.1em;
+}
+
+.larger {
+ font-size: 1.2em;
+}
+
+.largest {
+ font-size: 1.3em;
+}
+
+.hidden {
+ display: none;
+}
+
+.quiet {
+ color: #666;
+}
+
+.loud {
+ color: #000;
+}
+
+.highlight {
+ background: #FF0;
+}
+
+.text-left {
+ text-align: left;
+}
+
+.text-right {
+ text-align: right;
+}
+
+.text-center {
+ text-align: center;
+}
+
+.error, .notice, .success {
+ border: 1px solid #DDD;
+ padding: 5px;
+ margin: 5px;
+}
+
+.error {
+ background: #FBE3E4;
+ color: #8A1F11;
+ border-color: #FBC2C4;
+}
+
+.error a {
+ color: #8A1F11;
+}
+
+.notice {
+ background: #FFF6BF;
+ color: #514721;
+ border-color: #FFD324;
+}
+
+.notice a {
+ color: #514721;
+}
+
+.success {
+ background: #E6EFC2;
+ color: #264409;
+ border-color: #C6D880;
+}
+
+.success a {
+ color: #264409;
+}
+
+div.more {
+ padding-top: 6px;
+}
+
+.more a, a.more {
+ color: #666;
+}
+
+.more a:hover, a.more:hover {
+ color: #002;
+}
+
+/*
+ Forms
+------------------------------------------------------------------- */
+
+label {
+ font-weight: bold;
+ cursor: pointer;
+}
+
+fieldset {
+ padding: 1.4em;
+ margin: 0 0 1.5em 0;
+ border: 5px solid white;
+}
+
+legend {
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+textarea {
+ overflow: auto;
+}
+
+input.text, textarea, select {
+ background: #FCFCFC;
+ border: 1px inset #AAA;
+ margin: 0.5em 0;
+ padding: 4px 5px;
+}
+
+input.text:focus, textarea:focus, select:focus {
+ background: #FFFFF5;
+}
+
+input.button, input.gsc-search-button {
+ border: 1px outset #AAA;
+ padding: 4px 5px;
+
+ height: 30px;
+ width: 150px;
+
+ -moz-box-shadow: 2px 1px 3px #AAA;
+ -webkit-box-shadow: 2px 1px 3px #AAA;
+ box-shadow: 2px 1px 3px #AAA;
+
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+
+ background-image: -webkit-gradient(
+ linear,
+ left bottom,
+ left top,
+ color-stop(0.26, rgb(199,199,199)),
+ color-stop(0.68, rgb(240,240,240))
+ );
+ background-image: -moz-linear-gradient(
+ center bottom,
+ rgb(199,199,199) 26%,
+ rgb(240,240,240) 68%
+ );
+}
+
+input.button:active, input.gsc-search-button:active {
+ border-style: inset;
+ padding: 7px 8px;
+
+ background-image: -webkit-gradient(
+ linear,
+ left bottom,
+ left top,
+ color-stop(0.16, rgb(199,199,199)),
+ color-stop(0.58, rgb(240,240,240))
+ );
+ background-image: -moz-linear-gradient(
+ center bottom,
+ rgb(199,199,199) 16%,
+ rgb(240,240,240) 58%
+ );
+}
+
+.form-error {
+ border-color: #F00;
+}
+
+/*
+ Alignment
+------------------------------------------------------------------- */
+
+/* General */
+
+.center, .aligncenter {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+/* Images */
+
+img.bordered, img.alignleft, img.alignright, img.aligncenter {
+ background-color: #FFF;
+ border: 1px solid #DDD;
+ padding: 3px;
+}
+
+img.alignleft, img.left {
+ margin: 0 1.5em 1em 0;
+}
+
+img.alignright, img.right {
+ margin: 0 0 1em 1.5em;
+}
+
+/* Floats */
+
+.left, .alignleft {
+ float: left;
+}
+
+.right, .alignright {
+ float: right;
+}
+
+.clear, .clearer {
+ clear: both;
+}
+
+.clearer {
+ display: block;
+ font-size: 0;
+ line-height: 0;
+}
+
+/* Columns */
+
+.col2 {
+ width: 47%;
+}
+
+/*
+ Separators
+------------------------------------------------------------------- */
+
+.content-separator, .archive-separator {
+ background: #EEE;
+ clear: both;
+ color: #FFE;
+ display: block;
+ font-size: 0;
+ height: 1px;
+ line-height: 0;
+ margin: 12px 0 24px;
+}
+
+.archive-separator {
+ margin: 0 0 14px;
+}
+
+.separator-vertical {
+ background: url('/images/separator-vertical.gif') repeat-y center top;
+}
+
+.archive-year {
+ background: #F7F7F2 url('/images/box.gif') no-repeat left top;
+ padding: 5px;
+ text-align: center;
+ margin-top: 20px;
+ margin-bottom: 20px;
+}
+
+/*
+ Posts
+------------------------------------------------------------------- */
+
+.post {
+ margin-bottom: 24px;
+}
+
+.post a {
+ text-decoration: none;
+}
+
+.post a:hover, .post-body a {
+ text-decoration: underline;
+}
+
+.post-title {
+ border-bottom: 1px solid #E5E5E5;
+}
+
+.post-date {
+ color: #777;
+ font-size: 0.9em;
+ padding: 8px 0 0;
+}
+
+.post-date a {
+ color: #444;
+}
+
+.post-body {
+ padding-top: 12px;
+}
+
+.post-meta {
+ background: #FCFCFC;
+ border: 1px solid #ECECEC;
+ color: #777;
+ font-size: 0.9em;
+ margin-top: 16px;
+ padding: 6px 10px;
+}
+
+.post-meta a {
+ color: #345;
+}
+
+.post-meta a:hover {
+ color: #001;
+}
+
+ul.nice-list {
+ border-top: 1px solid #F0F0F0;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+ul.nice-list li {
+ border-bottom: 1px solid #F0F0F0;
+ list-style: none;
+ padding: 6px 5px;
+}
+
+ul.nice-list li a {
+ text-decoration: none;
+}
+
+ul.nice-list li a:hover {
+ text-decoration: underline;
+}
+
+ul.nice-list span {
+ color: #666;
+}
+
+ul.nice-list img {
+ margin-left: 3px;
+ margin-right: 3px;
+}
+
+ul.nice-list input[type=image] {
+ margin-left: 3px;
+ margin-right: 3px;
+}
+
+#sidebar ul.nice-list li, #sidebar ul.nice-list {
+ border-color: #E0E0E0;
+}
+
+/* Archives */
+.archive-pagination {
+ margin-bottom: 1.6em;
+}
+
+.archive-pagination a {
+ text-decoration: none;
+}
+
+.archive-pagination a:hover {
+ text-decoration: underline;
+}
+
+.archive-post {
+ margin-bottom: 14px;
+}
+
+.archive-post-date {
+ background: #F5F5F5;
+
+ border: 4px solid white;
+
+ -moz-box-shadow: 3px 2px 8px #AAA;
+ -webkit-box-shadow: 3px 2px 8px #AAA;
+ box-shadow: 3px 2px 8px #AAA;
+
+ float: left;
+ margin-right: 16px;
+ padding: 2px 0 5px;
+ text-align: center;
+ width: 46px;
+}
+
+.archive-post .post-date {
+ border: none;
+ padding: 0;
+}
+
+.archive-post-day {
+ font: normal 1.6em Georgia, serif;
+}
+
+.archive-post .post-date, .archive-post-title {
+ padding-top: 3px;
+}
+
+/*
+ Thumbnails
+------------------------------------------------------------------- */
+
+.thumbnails {
+ margin: 0 0 1em -8px;
+}
+
+.thumbnails a.thumb {
+ background: #DAD6D0;
+ display: block;
+ float: left;
+ margin: 0 0 8px 8px;
+ padding: 1px;
+}
+
+.thumbnails a.thumb:hover {
+ background: #AAA;
+}
+
+.thumbnails .thumb img {
+ display: block;
+}
+
+/*
+ Box
+------------------------------------------------------------------- */
+
+.box {
+ background: url('/images/box.gif') no-repeat left top;
+ border-bottom: 1px solid #DFDFDA;
+ border-top: 1px solid #FFF;
+ padding: 16px 20px 0;
+}
+
+.box a {
+ color: #951;
+}
+
+.box a:hover {
+ color: #310;
+}
+
+.box-title {
+ color: #665;
+ font: normal 1.6em sans-serif;
+}
+
+.box-content {
+ padding: 8px 0;
+}
+
+/*
+ Layout
+------------------------------------------------------------------- */
+
+/* Common */
+.center-wrapper {
+ width: 960px;
+ margin: 0 auto;
+}
+
+/* Header */
+
+#header-wrapper {
+ background: #2F2F2F url('/images/header.gif') repeat-x left top;
+}
+
+#header-wrapper-2 {
+ background: url('/images/header-wrapper-2.gif') repeat-y center top;
+}
+
+#header {
+ background: #333 url('/images/header.gif') repeat-x left top;
+ padding: 32px 32px 0 46px;
+}
+
+#help-wrapper {
+ background: url('/images/help.gif') no-repeat left top;
+ height: 39px;
+ margin: -8px 0 0 auto;
+ width: 290px;
+}
+
+#help {
+ color: #666;
+ font-size: 1.2em;
+ padding: 10px 28px 0 32px;
+}
+
+#help a {
+ color: #999;
+ text-decoration: none;
+}
+
+#help a:hover {
+ color: #DDD;
+}
+
+#help span.text-separator {
+ color: #444;
+ padding: 0 7px;
+}
+
+#logo {
+ color: #FFF;
+}
+
+#site-title a {
+ text-decoration: none;
+}
+
+#site-title, #site-title a {
+ color: #FFF;
+}
+
+#site-title span, #site-title a span {
+ color: #DDD;
+}
+
+#site-slogan {
+ color: #B58361;
+ font-size: 1.3em;
+}
+
+/* Navigation */
+
+ul.tabbed {
+ display: inline;
+ margin: 0;
+ padding: 0;
+}
+
+ul.tabbed li {
+ float: left;
+ list-style: none;
+}
+
+ul.tabbed a {
+ text-decoration: none;
+}
+
+#navigation-wrapper {
+ background: url('/images/navigation.gif') repeat-x left top;
+}
+
+#navigation-wrapper-2 {
+ background: url('/images/navigation-wrapper-2.gif') no-repeat center top;
+}
+
+#navigation {
+ background: url('/images/navigation.gif') repeat-x left top;
+ min-height: 46px;
+ padding: 0 8px;
+}
+
+#navigation a {
+ color: #EAE6DD;
+ display: block;
+ font-weight: bold;
+ font-size: 1.3em;
+ padding: 12px 12px 11px;
+}
+
+#navigation a:hover {
+ color: #FFF;
+}
+
+#navigation li.current_page_item a, #navigation li.current_page_parent a {
+ color: #FFE;
+}
+
+#navigation li.current_page_item a {
+ background: url('/images/navigation-arrow-2.gif') no-repeat center bottom;
+}
+
+#navigation li.current_page_parent a {
+ background: url('/images/navigation-arrow.gif') no-repeat center bottom;
+}
+
+/* Subnav */
+
+#subnav-wrapper {
+ background: #E9E9E9 url('/images/subnav-wrapper.gif') repeat-x left bottom;
+}
+
+#subnav-wrapper-2 {
+ background: url('/images/subnav-wrapper-2.gif') no-repeat center bottom;
+}
+
+#subnav {
+ padding: 0 8px;
+}
+
+#subnav a {
+ color: #666;
+ display: block;
+ font: bold 1.1em sans-serif;
+ padding: 12px 12px;
+}
+
+#subnav a:hover {
+ color: #111;
+}
+
+#subnav li.current_page_item a {
+ color: #333;
+}
+
+/* Content */
+
+.content {
+ font-size: 1.1em;
+}
+
+#content-wrapper {
+ background: #F6F6F6 url('/images/content-wrapper.gif') repeat-y center top;
+}
+
+.content#content-two-columns {
+ background: url('/images/content-two-columns.gif') repeat-y left top;
+}
+
+/* Main */
+
+#main-wrapper {
+ float: right;
+ width: 698px;
+}
+
+#main {
+ padding: 24px;
+}
+
+#main .text-separator, #featured .text-separator {
+ color: #AAA;
+ padding: 0 5px;
+}
+
+/* Sidebar */
+
+#sidebar-wrapper {
+ float: left;
+ width: 260px;
+}
+
+/* Footer */
+
+#footer-wrapper {
+ background: url('/images/footer-wrapper.gif') no-repeat center bottom;
+ border-top: 1px solid #999;
+ padding-bottom: 20px;
+}
+
+#footer {
+ background: #808080;
+ font-size: 1.1em;
+ padding: 12px 16px;
+}
+
+#footer a {
+ color: #DDD;
+ text-decoration: none;
+}
+
+#footer a:hover {
+ color: #FFF;
+}
+
+#footer span.text-separator {
+ color: #999;
+ padding: 0 3px;
+}
+
+/* Bottom */
+
+#bottom {
+ color: #666;
+ padding: 0 6px 12px;
+ margin-top: -6px;
+}
+
+#bottom a {
+ color: #555;
+ text-decoration: none;
+}
+
+#bottom a:hover, #bottom .right a:hover {
+ color: #002;
+}
+
+#bottom span.text-separator {
+ color: #888;
+ padding: 0 3px;
+}
+
+#bottom .right, #bottom .right a {
+ color: #777;
+}
+
+/* Featured Box */
+#featured-wrapper {
+ background: url('/images/gradient-light.gif') repeat-x left bottom;
+ border-bottom: 1px solid #E0E0E0;
+ padding-bottom: 22px;
+}
+
+#featured {
+ padding: 42px 124px 12px 48px;
+}
+
+#featured h2 {
+ font: normal 3em sans-serif;
+ margin: 0;
+}
+
+#featured h2 span {
+ color: #43B16C;
+}
+
+/*
+ Media Browser
+*/
+
+#media-browser .centered {
+ display: inline-block;
+ text-align: center
+}
+
+#media-browser .caption {
+ padding: 4px;
+ font-size: 0.8em;
+ border-bottom: 1px dotted black;
+}
+
+#media-browser .thumbnail-container {
+ margin: 10px 20px 10px 17px;
+ text-align: center
+}
+
+#media-browser #prevNext {
+ text-align: center;
+}
+
+/*
+ Misc overriding classes
+------------------------------------------------------------------- */
+
+/* Border */
+
+.noborder {
+ border: 0;
+}
+
+.notborder {
+ border-top: 0;
+}
+
+.norborder {
+ border-right: 0;
+}
+
+.nobborder {
+ border-bottom: 0;
+}
+
+.nolborder {
+ border-left: 0;
+}
+
+/* Margin */
+
+.nomargin {
+ margin: 0;
+}
+
+.notmargin {
+ margin-top: 0;
+}
+
+.normargin {
+ margin-right: 0;
+}
+
+.nobmargin {
+ margin-bottom: 0;
+}
+
+.nolmargin {
+ margin-left: 0;
+}
+
+/* Padding */
+
+.nopadding {
+ padding: 0;
+}
+
+.notpadding {
+ padding-top: 0;
+}
+
+.norpadding {
+ padding-right: 0;
+}
+
+.nobpadding {
+ padding-bottom: 0;
+}
+
+.nolpadding {
+ padding-left: 0;
+}
+
+/* Single Line IE Fixes */
+* html .separator-vertical, * html .content {
+ height: 0.01%;
+ min-height: 0.01%;
+}
+
+/* misc */
+#placeHolder, #imgs {
+ border: 2px inset white;
+ height: 120px;
+}
+
+#placeHolder img {
+ margin: 4px;
+ border: 4px solid white;
+
+ -moz-box-shadow: 3px 2px 8px #AAA;
+ -webkit-box-shadow: 3px 2px 8px #AAA;
+ box-shadow: 3px 2px 8px #AAA;
+}
+
+#imgs {
+ border: 2px inset white;
+ height: 100px;
+}
+
+.post #Drafts, .post #Posts, .post #Pages {
+ margin-top: 40px;
+ max-height: 700px;
+ overflow-y: auto;
+}
+
+
diff --git a/war/css/wysiwyg.css b/war/css/wysiwyg.css
new file mode 100644
index 0000000..8b04558
--- /dev/null
+++ b/war/css/wysiwyg.css
@@ -0,0 +1,9 @@
+@import '/css/style.css';
+
+body {
+ background-color: white;
+}
+
+blockquote {
+ width: 70%;
+}
\ No newline at end of file
diff --git a/war/images/add.png b/war/images/add.png
new file mode 100755
index 0000000..6332fef
Binary files /dev/null and b/war/images/add.png differ
diff --git a/war/images/atom.gif b/war/images/atom.gif
new file mode 100644
index 0000000..e6bc11f
Binary files /dev/null and b/war/images/atom.gif differ
diff --git a/war/images/box.gif b/war/images/box.gif
new file mode 100755
index 0000000..1a361c0
Binary files /dev/null and b/war/images/box.gif differ
diff --git a/war/images/buttons.gif b/war/images/buttons.gif
new file mode 100755
index 0000000..2e464d0
Binary files /dev/null and b/war/images/buttons.gif differ
diff --git a/war/images/chosen-sprite.png b/war/images/chosen-sprite.png
new file mode 100644
index 0000000..6ec9bbf
Binary files /dev/null and b/war/images/chosen-sprite.png differ
diff --git a/war/images/clipboard.png b/war/images/clipboard.png
new file mode 100755
index 0000000..08647f1
Binary files /dev/null and b/war/images/clipboard.png differ
diff --git a/war/images/content-two-columns.gif b/war/images/content-two-columns.gif
new file mode 100755
index 0000000..3ca2f15
Binary files /dev/null and b/war/images/content-two-columns.gif differ
diff --git a/war/images/content-wrapper.gif b/war/images/content-wrapper.gif
new file mode 100755
index 0000000..298bbf8
Binary files /dev/null and b/war/images/content-wrapper.gif differ
diff --git a/war/images/cross.png b/war/images/cross.png
new file mode 100755
index 0000000..1514d51
Binary files /dev/null and b/war/images/cross.png differ
diff --git a/war/images/feed.png b/war/images/feed.png
new file mode 100755
index 0000000..315c4f4
Binary files /dev/null and b/war/images/feed.png differ
diff --git a/war/images/footer-wrapper.gif b/war/images/footer-wrapper.gif
new file mode 100755
index 0000000..1a35caf
Binary files /dev/null and b/war/images/footer-wrapper.gif differ
diff --git a/war/images/gaelyk-favicon.png b/war/images/gaelyk-favicon.png
new file mode 100644
index 0000000..dd3f4bc
Binary files /dev/null and b/war/images/gaelyk-favicon.png differ
diff --git a/war/images/gradient-light.gif b/war/images/gradient-light.gif
new file mode 100755
index 0000000..2675f60
Binary files /dev/null and b/war/images/gradient-light.gif differ
diff --git a/war/images/header-wrapper-2.gif b/war/images/header-wrapper-2.gif
new file mode 100755
index 0000000..a8c593b
Binary files /dev/null and b/war/images/header-wrapper-2.gif differ
diff --git a/war/images/header.gif b/war/images/header.gif
new file mode 100755
index 0000000..73ad174
Binary files /dev/null and b/war/images/header.gif differ
diff --git a/war/images/help.gif b/war/images/help.gif
new file mode 100755
index 0000000..61434a4
Binary files /dev/null and b/war/images/help.gif differ
diff --git a/war/images/icon-delicious.png b/war/images/icon-delicious.png
new file mode 100644
index 0000000..eba3519
Binary files /dev/null and b/war/images/icon-delicious.png differ
diff --git a/war/images/icon-greader.png b/war/images/icon-greader.png
new file mode 100644
index 0000000..31b53ca
Binary files /dev/null and b/war/images/icon-greader.png differ
diff --git a/war/images/icon-twitter.png b/war/images/icon-twitter.png
new file mode 100644
index 0000000..9d82663
Binary files /dev/null and b/war/images/icon-twitter.png differ
diff --git a/war/images/image-not-found.png b/war/images/image-not-found.png
new file mode 100644
index 0000000..cd1172f
Binary files /dev/null and b/war/images/image-not-found.png differ
diff --git a/war/images/images.png b/war/images/images.png
new file mode 100755
index 0000000..184860d
Binary files /dev/null and b/war/images/images.png differ
diff --git a/war/images/images2.png b/war/images/images2.png
new file mode 100755
index 0000000..cdb3e00
Binary files /dev/null and b/war/images/images2.png differ
diff --git a/war/images/navigation-arrow-2.gif b/war/images/navigation-arrow-2.gif
new file mode 100755
index 0000000..b94beeb
Binary files /dev/null and b/war/images/navigation-arrow-2.gif differ
diff --git a/war/images/navigation-arrow.gif b/war/images/navigation-arrow.gif
new file mode 100755
index 0000000..06dd8c5
Binary files /dev/null and b/war/images/navigation-arrow.gif differ
diff --git a/war/images/navigation-wrapper-2.gif b/war/images/navigation-wrapper-2.gif
new file mode 100755
index 0000000..894950a
Binary files /dev/null and b/war/images/navigation-wrapper-2.gif differ
diff --git a/war/images/navigation.gif b/war/images/navigation.gif
new file mode 100755
index 0000000..fc4fdcb
Binary files /dev/null and b/war/images/navigation.gif differ
diff --git a/war/images/newspaper.png b/war/images/newspaper.png
new file mode 100755
index 0000000..6a2ecce
Binary files /dev/null and b/war/images/newspaper.png differ
diff --git a/war/images/page_white_text.png b/war/images/page_white_text.png
new file mode 100755
index 0000000..813f712
Binary files /dev/null and b/war/images/page_white_text.png differ
diff --git a/war/images/pencil.png b/war/images/pencil.png
new file mode 100755
index 0000000..0bfecd5
Binary files /dev/null and b/war/images/pencil.png differ
diff --git a/war/images/sample-gravatar.jpg b/war/images/sample-gravatar.jpg
new file mode 100755
index 0000000..2e76b12
Binary files /dev/null and b/war/images/sample-gravatar.jpg differ
diff --git a/war/images/separator-vertical.gif b/war/images/separator-vertical.gif
new file mode 100755
index 0000000..dc482c3
Binary files /dev/null and b/war/images/separator-vertical.gif differ
diff --git a/war/images/subnav-wrapper-2.gif b/war/images/subnav-wrapper-2.gif
new file mode 100755
index 0000000..d28e555
Binary files /dev/null and b/war/images/subnav-wrapper-2.gif differ
diff --git a/war/images/subnav-wrapper.gif b/war/images/subnav-wrapper.gif
new file mode 100755
index 0000000..439ec43
Binary files /dev/null and b/war/images/subnav-wrapper.gif differ
diff --git a/war/images/toolbar.gif b/war/images/toolbar.gif
new file mode 100755
index 0000000..e6eb2da
Binary files /dev/null and b/war/images/toolbar.gif differ
diff --git a/war/images/transparent.png b/war/images/transparent.png
new file mode 100644
index 0000000..7c0bd7e
Binary files /dev/null and b/war/images/transparent.png differ
diff --git a/war/js/chosen.jquery.min.js b/war/js/chosen.jquery.min.js
new file mode 100644
index 0000000..da2e632
--- /dev/null
+++ b/war/js/chosen.jquery.min.js
@@ -0,0 +1,9 @@
+/*
+Chosen, a Select Box Enhancer for jQuery and Protoype
+by Patrick Filler for Harvest, http://getharvest.com
+
+Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License
+
+Copyright (c) 2011 by Harvest
+*/
+(function(){var $,Chosen,SelectParser,get_side_border_padding,root;var __bind=function(fn,me){return function(){return fn.apply(me,arguments);};};root=typeof exports!=="undefined"&&exports!==null?exports:this;$=jQuery;$.fn.extend({chosen:function(data,options){return $(this).each(function(input_field){if(!($(this)).hasClass("chzn-done")){return new Chosen(this,data,options);}});}});Chosen=(function(){function Chosen(elmn){this.set_default_values();this.form_field=elmn;this.form_field_jq=$(this.form_field);this.is_multiple=this.form_field.multiple;this.default_text_default=this.form_field.multiple?"Select one or more options":"Select an option";this.set_up_html();this.register_observers();this.form_field_jq.addClass("chzn-done");}Chosen.prototype.set_default_values=function(){this.click_test_action=__bind(function(evt){return this.test_active_click(evt);},this);this.active_field=false;this.mouse_on_container=false;this.results_showing=false;this.result_highlighted=null;this.result_single_selected=null;return this.choices=0;};Chosen.prototype.set_up_html=function(){var container_div,dd_top,dd_width,sf_width;this.container_id=this.form_field.id+"_chzn";this.f_width=this.form_field_jq.width();this.default_text=this.form_field_jq.attr("title")?this.form_field_jq.attr("title"):this.default_text_default;container_div=$("
",{id:this.container_id,"class":"chzn-container",style:"width: "+this.f_width+"px;"});if(this.is_multiple){container_div.html('');}else{container_div.html(''+this.default_text+'
');}this.form_field_jq.hide().after(container_div);this.container=$("#"+this.container_id);this.container.addClass("chzn-container-"+(this.is_multiple?"multi":"single"));this.dropdown=this.container.find("div.chzn-drop").first();dd_top=this.container.height();dd_width=this.f_width-get_side_border_padding(this.dropdown);this.dropdown.css({width:dd_width+"px",top:dd_top+"px"});this.search_field=this.container.find("input").first();this.search_results=this.container.find("ul.chzn-results").first();this.search_field_scale();this.search_no_results=this.container.find("li.no-results").first();if(this.is_multiple){this.search_choices=this.container.find("ul.chzn-choices").first();this.search_container=this.container.find("li.search-field").first();}else{this.search_container=this.container.find("div.chzn-search").first();this.selected_item=this.container.find(".chzn-single").first();sf_width=dd_width-get_side_border_padding(this.search_container)-get_side_border_padding(this.search_field);this.search_field.css({width:sf_width+"px"});}this.results_build();return this.set_tab_index();};Chosen.prototype.register_observers=function(){this.container.click(__bind(function(evt){return this.container_click(evt);},this));this.container.mouseenter(__bind(function(evt){return this.mouse_enter(evt);},this));this.container.mouseleave(__bind(function(evt){return this.mouse_leave(evt);},this));this.search_results.click(__bind(function(evt){return this.search_results_click(evt);},this));this.search_results.mouseover(__bind(function(evt){return this.search_results_mouseover(evt);},this));this.search_results.mouseout(__bind(function(evt){return this.search_results_mouseout(evt);},this));this.form_field_jq.bind("liszt:updated",__bind(function(evt){return this.results_update_field(evt);},this));this.search_field.blur(__bind(function(evt){return this.input_blur(evt);},this));this.search_field.keyup(__bind(function(evt){return this.keyup_checker(evt);},this));this.search_field.keydown(__bind(function(evt){return this.keydown_checker(evt);},this));if(this.is_multiple){this.search_choices.click(__bind(function(evt){return this.choices_click(evt);},this));return this.search_field.focus(__bind(function(evt){return this.input_focus(evt);},this));}else{return this.selected_item.focus(__bind(function(evt){return this.activate_field(evt);},this));}};Chosen.prototype.container_click=function(evt){if(evt&&evt.type==="click"){evt.stopPropagation();}if(!this.pending_destroy_click){if(!this.active_field){if(this.is_multiple){this.search_field.val("");}$(document).click(this.click_test_action);this.results_show();}else{if(!this.is_multiple&&evt&&($(evt.target)===this.selected_item||$(evt.target).parents("a.chzn-single").length)){evt.preventDefault();this.results_toggle();}}return this.activate_field();}else{return this.pending_destroy_click=false;}};Chosen.prototype.mouse_enter=function(){return this.mouse_on_container=true;};Chosen.prototype.mouse_leave=function(){return this.mouse_on_container=false;};Chosen.prototype.input_focus=function(evt){if(!this.active_field){return setTimeout((__bind(function(){return this.container_click();},this)),50);}};Chosen.prototype.input_blur=function(evt){if(!this.mouse_on_container){this.active_field=false;return setTimeout((__bind(function(){return this.blur_test();},this)),100);}};Chosen.prototype.blur_test=function(evt){if(!this.active_field&&this.container.hasClass("chzn-container-active")){return this.close_field();}};Chosen.prototype.close_field=function(){$(document).unbind("click",this.click_test_action);if(!this.is_multiple){this.selected_item.attr("tabindex",this.search_field.attr("tabindex"));this.search_field.attr("tabindex",-1);}this.active_field=false;this.results_hide();this.container.removeClass("chzn-container-active");this.winnow_results_clear();this.clear_backstroke();this.show_search_field_default();return this.search_field_scale();};Chosen.prototype.activate_field=function(){if(!this.is_multiple&&!this.active_field){this.search_field.attr("tabindex",this.selected_item.attr("tabindex"));this.selected_item.attr("tabindex",-1);}this.container.addClass("chzn-container-active");this.active_field=true;this.search_field.val(this.search_field.val());return this.search_field.focus();};Chosen.prototype.test_active_click=function(evt){if($(evt.target).parents("#"+this.container.id).length){return this.active_field=true;}else{return this.close_field();}};Chosen.prototype.results_build=function(){var content,data,startTime,_i,_len,_ref;startTime=new Date();this.parsing=true;this.results_data=SelectParser.select_to_array(this.form_field);if(this.is_multiple&&this.choices>0){this.search_choices.find("li.search-choice").remove();this.choices=0;}else{if(!this.is_multiple){this.selected_item.find("span").text(this.default_text);}}content="";_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){data=_ref[_i];if(data.group){content+=this.result_add_group(data);}else{if(!data.empty){content+=this.result_add_option(data);if(data.selected&&this.is_multiple){this.choice_build(data);}else{if(data.selected&&!this.is_multiple){this.selected_item.find("span").text(data.text);}}}}}this.show_search_field_default();this.search_field_scale();this.search_results.html(content);return this.parsing=false;};Chosen.prototype.result_add_group=function(group){if(!group.disabled){group.dom_id=this.form_field.id+"chzn_g_"+group.array_index;return''+$("
").text(group.label).html()+" ";}else{return"";}};Chosen.prototype.result_add_option=function(option){var classes;if(!option.disabled){option.dom_id=this.form_field.id+"chzn_o_"+option.array_index;classes=option.selected&&this.is_multiple?[]:["active-result"];if(option.selected){classes.push("result-selected");}if(option.group_array_index!=null){classes.push("group-option");}return''+$("
").text(option.text).html()+" ";}else{return"";}};Chosen.prototype.results_update_field=function(){this.result_clear_highlight();this.result_single_selected=null;return this.results_build();};Chosen.prototype.result_do_highlight=function(el){var high_bottom,high_top,maxHeight,visible_bottom,visible_top;if(el.length){this.result_clear_highlight();this.result_highlight=el;this.result_highlight.addClass("highlighted");maxHeight=parseInt(this.search_results.css("maxHeight"),10);visible_top=this.search_results.scrollTop();visible_bottom=maxHeight+visible_top;high_top=this.result_highlight.position().top+this.search_results.scrollTop();high_bottom=high_top+this.result_highlight.outerHeight();if(high_bottom>=visible_bottom){return this.search_results.scrollTop((high_bottom-maxHeight)>0?high_bottom-maxHeight:0);}else{if(high_top'+item.text+' ');link=$("#"+choice_id).find("a").first();return link.click(__bind(function(evt){return this.choice_destroy_link_click(evt);},this));};Chosen.prototype.choice_destroy_link_click=function(evt){evt.preventDefault();this.pending_destroy_click=true;return this.choice_destroy($(evt.target));};Chosen.prototype.choice_destroy=function(link){this.choices-=1;this.show_search_field_default();if(this.is_multiple&&this.choices>0&&this.search_field.val().length<1){this.results_hide();}this.result_deselect(link.attr("rel"));return link.parents("li").first().remove();};Chosen.prototype.result_select=function(){var high,high_id,item,position;if(this.result_highlight){high=this.result_highlight;high_id=high.attr("id");this.result_clear_highlight();high.addClass("result-selected");if(this.is_multiple){this.result_deactivate(high);}else{this.result_single_selected=high;}position=high_id.substr(high_id.lastIndexOf("_")+1);item=this.results_data[position];item.selected=true;this.form_field.options[item.options_index].selected=true;if(this.is_multiple){this.choice_build(item);}else{this.selected_item.find("span").first().text(item.text);}this.results_hide();this.search_field.val("");this.form_field_jq.trigger("change");return this.search_field_scale();}};Chosen.prototype.result_activate=function(el){return el.addClass("active-result").show();};Chosen.prototype.result_deactivate=function(el){return el.removeClass("active-result").hide();};Chosen.prototype.result_deselect=function(pos){var result,result_data;result_data=this.results_data[pos];result_data.selected=false;this.form_field.options[result_data.options_index].selected=false;result=$("#"+this.form_field.id+"chzn_o_"+pos);result.removeClass("result-selected").addClass("active-result").show();this.result_clear_highlight();this.winnow_results();this.form_field_jq.trigger("change");return this.search_field_scale();};Chosen.prototype.results_search=function(evt){if(this.results_showing){return this.winnow_results();}else{return this.results_show();}};Chosen.prototype.winnow_results=function(){var found,option,part,parts,regex,result_id,results,searchText,startTime,startpos,text,zregex,_i,_j,_len,_len2,_ref;startTime=new Date();this.no_results_clear();results=0;searchText=this.search_field.val()===this.default_text?"":$.trim(this.search_field.val());regex=new RegExp("^"+searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i");zregex=new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i");_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];if(!option.disabled&&!option.empty){if(option.group){$("#"+option.dom_id).hide();}else{if(!(this.is_multiple&&option.selected)){found=false;result_id=option.dom_id;if(regex.test(option.text)){found=true;results+=1;}else{if(option.text.indexOf(" ")>=0||option.text.indexOf("[")===0){parts=option.text.replace(/\[|\]/g,"").split(" ");if(parts.length){for(_j=0,_len2=parts.length;_j<_len2;_j++){part=parts[_j];if(regex.test(part)){found=true;results+=1;}}}}}if(found){if(searchText.length){startpos=option.text.search(zregex);text=option.text.substr(0,startpos+searchText.length)+""+option.text.substr(startpos+searchText.length);text=text.substr(0,startpos)+""+text.substr(startpos);}else{text=option.text;}if($("#"+result_id).html!==text){$("#"+result_id).html(text);}this.result_activate($("#"+result_id));if(option.group_array_index!=null){$("#"+this.results_data[option.group_array_index].dom_id).show();}}else{if(this.result_highlight&&result_id===this.result_highlight.attr("id")){this.result_clear_highlight();}this.result_deactivate($("#"+result_id));}}}}}if(results<1&&searchText.length){return this.no_results(searchText);}else{return this.winnow_results_set_highlight();}};Chosen.prototype.winnow_results_clear=function(){var li,lis,_i,_len,_results;this.search_field.val("");lis=this.search_results.find("li");_results=[];for(_i=0,_len=lis.length;_i<_len;_i++){li=lis[_i];li=$(li);_results.push(li.hasClass("group-result")?li.show():!this.is_multiple||!li.hasClass("result-selected")?this.result_activate(li):void 0);}return _results;};Chosen.prototype.winnow_results_set_highlight=function(){var do_high;if(!this.result_highlight){do_high=this.search_results.find(".active-result").first();if(do_high){return this.result_do_highlight(do_high);}}};Chosen.prototype.no_results=function(terms){var no_results_html;no_results_html=$('No results match " " ');no_results_html.find("span").first().text(terms);return this.search_results.append(no_results_html);};Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove();};Chosen.prototype.keydown_arrow=function(){var first_active,next_sib;if(!this.result_highlight){first_active=this.search_results.find("li.active-result").first();if(first_active){this.result_do_highlight($(first_active));}}else{if(this.results_showing){next_sib=this.result_highlight.nextAll("li.active-result").first();if(next_sib){this.result_do_highlight(next_sib);}}}if(!this.results_showing){return this.results_show();}};Chosen.prototype.keyup_arrow=function(){var prev_sibs;if(!this.results_showing&&!this.is_multiple){return this.results_show();}else{if(this.result_highlight){prev_sibs=this.result_highlight.prevAll("li.active-result");if(prev_sibs.length){return this.result_do_highlight(prev_sibs.first());}else{if(this.choices>0){this.results_hide();}return this.result_clear_highlight();}}}};Chosen.prototype.keydown_backstroke=function(){if(this.pending_backstroke){this.choice_destroy(this.pending_backstroke.find("a").first());return this.clear_backstroke();}else{this.pending_backstroke=this.search_container.siblings("li.search-choice").last();return this.pending_backstroke.addClass("search-choice-focus");}};Chosen.prototype.clear_backstroke=function(){if(this.pending_backstroke){this.pending_backstroke.removeClass("search-choice-focus");}return this.pending_backstroke=null;};Chosen.prototype.keyup_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode;this.search_field_scale();switch(stroke){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0){return this.keydown_backstroke();}else{if(!this.pending_backstroke){this.result_clear_highlight();return this.results_search();}}break;case 13:evt.preventDefault();if(this.results_showing){return this.result_select();}break;case 27:if(this.results_showing){return this.results_hide();}break;case 9:case 38:case 40:case 16:break;default:return this.results_search();}};Chosen.prototype.keydown_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode;this.search_field_scale();if(stroke!==8&&this.pending_backstroke){this.clear_backstroke();}switch(stroke){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.mouse_on_container=false;break;case 13:evt.preventDefault();break;case 38:evt.preventDefault();this.keyup_arrow();break;case 40:this.keydown_arrow();break;}};Chosen.prototype.search_field_scale=function(){var dd_top,div,h,style,style_block,styles,w,_i,_len;if(this.is_multiple){h=0;w=0;style_block="position:absolute; left: -1000px; top: -1000px; display:none;";styles=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(_i=0,_len=styles.length;_i<_len;_i++){style=styles[_i];style_block+=style+":"+this.search_field.css(style)+";";}div=$("
",{style:style_block});div.text(this.search_field.val());$("body").append(div);w=div.width()+25;div.remove();if(w>this.f_width-10){w=this.f_width-10;}this.search_field.css({width:w+"px"});dd_top=this.container.height();return this.dropdown.css({top:dd_top+"px"});}};return Chosen;})();get_side_border_padding=function(elmt){var side_border_padding;return side_border_padding=elmt.outerWidth()-elmt.width();};root.get_side_border_padding=get_side_border_padding;SelectParser=(function(){function SelectParser(){this.options_index=0;this.parsed=[];}SelectParser.prototype.add_node=function(child){if(child.nodeName==="OPTGROUP"){return this.add_group(child);}else{return this.add_option(child);}};SelectParser.prototype.add_group=function(group){var group_position,option,_i,_len,_ref,_results;group_position=this.parsed.length;this.parsed.push({array_index:group_position,group:true,label:group.label,children:0,disabled:group.disabled});_ref=group.childNodes;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];_results.push(this.add_option(option,group_position,group.disabled));}return _results;};SelectParser.prototype.add_option=function(option,group_position,group_disabled){if(option.nodeName==="OPTION"){if(option.text!==""){if(group_position!=null){this.parsed[group_position].children+=1;}this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:option.value,text:option.text,selected:option.selected,disabled:group_disabled===true?group_disabled:option.disabled,group_array_index:group_position});}else{this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:true});}return this.options_index+=1;}};return SelectParser;})();SelectParser.select_to_array=function(select){var child,parser,_i,_len,_ref;parser=new SelectParser();_ref=select.childNodes;for(_i=0,_len=_ref.length;_i<_len;_i++){child=_ref[_i];parser.add_node(child);}return parser.parsed;};root.SelectParser=SelectParser;}).call(this);
\ No newline at end of file
diff --git a/war/js/highlight.js b/war/js/highlight.js
new file mode 100644
index 0000000..91b9b73
--- /dev/null
+++ b/war/js/highlight.js
@@ -0,0 +1,585 @@
+/*
+Syntax highlighting with language autodetection.
+http://softwaremaniacs.org/soft/highlight/
+*/
+
+var hljs = new function() {
+
+ /* Utility functions */
+
+ function escape(value) {
+ return value.replace(/&/gm, '&').replace(/';
+ }
+
+ while (stream1.length || stream2.length) {
+ var current = selectStream().splice(0, 1)[0];
+ result += escape(value.substr(processed, current.offset - processed));
+ processed = current.offset;
+ if ( current.event == 'start') {
+ result += open(current.node);
+ nodeStack.push(current.node);
+ } else if (current.event == 'stop') {
+ var i = nodeStack.length;
+ do {
+ i--;
+ var node = nodeStack[i];
+ result += ('' + node.nodeName.toLowerCase() + '>');
+ } while (node != current.node);
+ nodeStack.splice(i, 1);
+ while (i < nodeStack.length) {
+ result += open(nodeStack[i]);
+ i++;
+ }
+ }
+ }
+ result += value.substr(processed);
+ return result;
+ }
+
+ /* Core highlighting function */
+
+ function highlight(language_name, value) {
+
+ function subMode(lexem, mode) {
+ for (var i = 0; i < mode.contains.length; i++) {
+ if (mode.contains[i].beginRe.test(lexem)) {
+ return mode.contains[i];
+ }
+ }
+ }
+
+ function endOfMode(mode_index, lexem) {
+ if (modes[mode_index].end && modes[mode_index].endRe.test(lexem))
+ return 1;
+ if (modes[mode_index].endsWithParent) {
+ var level = endOfMode(mode_index - 1, lexem);
+ return level ? level + 1 : 0;
+ }
+ return 0;
+ }
+
+ function isIllegal(lexem, mode) {
+ return mode.illegalRe && mode.illegalRe.test(lexem);
+ }
+
+ function compileTerminators(mode, language) {
+ var terminators = [];
+
+ for (var i = 0; i < mode.contains.length; i++) {
+ terminators.push(mode.contains[i].begin);
+ }
+
+ var index = modes.length - 1;
+ do {
+ if (modes[index].end) {
+ terminators.push(modes[index].end);
+ }
+ index--;
+ } while (modes[index + 1].endsWithParent);
+
+ if (mode.illegal) {
+ terminators.push(mode.illegal);
+ }
+
+ return langRe(language, '(' + terminators.join('|') + ')', true);
+ }
+
+ function eatModeChunk(value, index) {
+ var mode = modes[modes.length - 1];
+ if (!mode.terminators) {
+ mode.terminators = compileTerminators(mode, language);
+ }
+ mode.terminators.lastIndex = index;
+ var match = mode.terminators.exec(value);
+ if (match)
+ return [value.substr(index, match.index - index), match[0], false];
+ else
+ return [value.substr(index), '', true];
+ }
+
+ function keywordMatch(mode, match) {
+ var match_str = language.case_insensitive ? match[0].toLowerCase() : match[0]
+ for (var className in mode.keywordGroups) {
+ if (!mode.keywordGroups.hasOwnProperty(className))
+ continue;
+ var value = mode.keywordGroups[className].hasOwnProperty(match_str);
+ if (value)
+ return [className, value];
+ }
+ return false;
+ }
+
+ function processKeywords(buffer, mode) {
+ if (!mode.keywords)
+ return escape(buffer);
+ var result = '';
+ var last_index = 0;
+ mode.lexemsRe.lastIndex = 0;
+ var match = mode.lexemsRe.exec(buffer);
+ while (match) {
+ result += escape(buffer.substr(last_index, match.index - last_index));
+ var keyword_match = keywordMatch(mode, match);
+ if (keyword_match) {
+ keyword_count += keyword_match[1];
+ result += '' + escape(match[0]) + ' ';
+ } else {
+ result += escape(match[0]);
+ }
+ last_index = mode.lexemsRe.lastIndex;
+ match = mode.lexemsRe.exec(buffer);
+ }
+ result += escape(buffer.substr(last_index, buffer.length - last_index));
+ return result;
+ }
+
+ function processBuffer(buffer, mode) {
+ if (mode.subLanguage && languages[mode.subLanguage]) {
+ var result = highlight(mode.subLanguage, buffer);
+ keyword_count += result.keyword_count;
+ return result.value;
+ } else {
+ return processKeywords(buffer, mode);
+ }
+ }
+
+ function startNewMode(mode, lexem) {
+ var markup = mode.className?'':'';
+ if (mode.returnBegin) {
+ result += markup;
+ mode.buffer = '';
+ } else if (mode.excludeBegin) {
+ result += escape(lexem) + markup;
+ mode.buffer = '';
+ } else {
+ result += markup;
+ mode.buffer = lexem;
+ }
+ modes.push(mode);
+ relevance += mode.relevance;
+ }
+
+ function processModeInfo(buffer, lexem, end) {
+ var current_mode = modes[modes.length - 1];
+ if (end) {
+ result += processBuffer(current_mode.buffer + buffer, current_mode);
+ return false;
+ }
+
+ var new_mode = subMode(lexem, current_mode);
+ if (new_mode) {
+ result += processBuffer(current_mode.buffer + buffer, current_mode);
+ startNewMode(new_mode, lexem);
+ return new_mode.returnBegin;
+ }
+
+ var end_level = endOfMode(modes.length - 1, lexem);
+ if (end_level) {
+ var markup = current_mode.className?' ':'';
+ if (current_mode.returnEnd) {
+ result += processBuffer(current_mode.buffer + buffer, current_mode) + markup;
+ } else if (current_mode.excludeEnd) {
+ result += processBuffer(current_mode.buffer + buffer, current_mode) + markup + escape(lexem);
+ } else {
+ result += processBuffer(current_mode.buffer + buffer + lexem, current_mode) + markup;
+ }
+ while (end_level > 1) {
+ markup = modes[modes.length - 2].className?'':'';
+ result += markup;
+ end_level--;
+ modes.length--;
+ }
+ var last_ended_mode = modes[modes.length - 1];
+ modes.length--;
+ modes[modes.length - 1].buffer = '';
+ if (last_ended_mode.starts) {
+ startNewMode(last_ended_mode.starts, '');
+ }
+ return current_mode.returnEnd;
+ }
+
+ if (isIllegal(lexem, current_mode))
+ throw 'Illegal';
+ }
+
+ var language = languages[language_name];
+ var modes = [language.defaultMode];
+ var relevance = 0;
+ var keyword_count = 0;
+ var result = '';
+ try {
+ var index = 0;
+ language.defaultMode.buffer = '';
+ do {
+ var mode_info = eatModeChunk(value, index);
+ var return_lexem = processModeInfo(mode_info[0], mode_info[1], mode_info[2]);
+ index += mode_info[0].length;
+ if (!return_lexem) {
+ index += mode_info[1].length;
+ }
+ } while (!mode_info[2]);
+ if(modes.length > 1)
+ throw 'Illegal';
+ return {
+ language: language_name,
+ relevance: relevance,
+ keyword_count: keyword_count,
+ value: result
+ }
+ } catch (e) {
+ if (e == 'Illegal') {
+ return {
+ language: null,
+ relevance: 0,
+ keyword_count: 0,
+ value: escape(value)
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ /* Initialization */
+
+ function compileModes() {
+
+ function compileMode(mode, language, is_default) {
+ if (mode.compiled)
+ return;
+
+ if (!is_default) {
+ mode.beginRe = langRe(language, mode.begin ? mode.begin : '\\B|\\b');
+ if (!mode.end && !mode.endsWithParent)
+ mode.end = '\\B|\\b'
+ if (mode.end)
+ mode.endRe = langRe(language, mode.end);
+ }
+ if (mode.illegal)
+ mode.illegalRe = langRe(language, mode.illegal);
+ if (mode.relevance == undefined)
+ mode.relevance = 1;
+ if (mode.keywords)
+ mode.lexemsRe = langRe(language, mode.lexems || hljs.IDENT_RE, true);
+ for (var key in mode.keywords) {
+ if (!mode.keywords.hasOwnProperty(key))
+ continue;
+ if (mode.keywords[key] instanceof Object)
+ mode.keywordGroups = mode.keywords;
+ else
+ mode.keywordGroups = {'keyword': mode.keywords};
+ break;
+ }
+ if (!mode.contains) {
+ mode.contains = [];
+ }
+ // compiled flag is set before compiling submodes to avoid self-recursion
+ // (see lisp where quoted_list contains quoted_list)
+ mode.compiled = true;
+ for (var i = 0; i < mode.contains.length; i++) {
+ compileMode(mode.contains[i], language, false);
+ }
+ if (mode.starts) {
+ compileMode(mode.starts, language, false);
+ }
+ }
+
+ for (var i in languages) {
+ if (!languages.hasOwnProperty(i))
+ continue;
+ compileMode(languages[i].defaultMode, languages[i], true);
+ }
+ }
+
+ function initialize() {
+ if (initialize.called)
+ return;
+ initialize.called = true;
+ compileModes();
+ }
+
+ /* Public library functions */
+
+ function highlightBlock(block, tabReplace, useBR) {
+ initialize();
+
+ var text = blockText(block, useBR);
+ var language = blockLanguage(block);
+ if (language == 'no-highlight')
+ return;
+ if (language) {
+ var result = highlight(language, text);
+ } else {
+ var result = {language: '', keyword_count: 0, relevance: 0, value: escape(text)};
+ var second_best = result;
+ for (var key in languages) {
+ if (!languages.hasOwnProperty(key))
+ continue;
+ var current = highlight(key, text);
+ if (current.keyword_count + current.relevance > second_best.keyword_count + second_best.relevance) {
+ second_best = current;
+ }
+ if (current.keyword_count + current.relevance > result.keyword_count + result.relevance) {
+ second_best = result;
+ result = current;
+ }
+ }
+ }
+
+ var class_name = block.className;
+ if (!class_name.match(result.language)) {
+ class_name = class_name ? (class_name + ' ' + result.language) : result.language;
+ }
+ var original = nodeStream(block);
+ if (original.length) {
+ var pre = document.createElement('pre');
+ pre.innerHTML = result.value;
+ result.value = mergeStreams(original, nodeStream(pre), text);
+ }
+ if (tabReplace) {
+ result.value = result.value.replace(/^((<[^>]+>|\t)+)/gm, function(match, p1, offset, s) {
+ return p1.replace(/\t/g, tabReplace);
+ })
+ }
+ if (useBR) {
+ result.value = result.value.replace(/\n/g, ' ');
+ }
+ if (/MSIE [678]/.test(navigator.userAgent) && block.tagName == 'CODE' && block.parentNode.tagName == 'PRE') {
+ // This is for backwards compatibility only. IE needs this strange
+ // hack becasue it cannot just cleanly replace block contents.
+ var pre = block.parentNode;
+ var container = document.createElement('div');
+ container.innerHTML = '' + result.value + '
';
+ block = container.firstChild.firstChild;
+ container.firstChild.className = pre.className;
+ pre.parentNode.replaceChild(container.firstChild, pre);
+ } else {
+ block.innerHTML = result.value;
+ }
+ block.className = class_name;
+ block.dataset = {};
+ block.dataset.result = {
+ language: result.language,
+ kw: result.keyword_count,
+ re: result.relevance
+ };
+ if (second_best && second_best.language) {
+ block.dataset.second_best = {
+ language: second_best.language,
+ kw: second_best.keyword_count,
+ re: second_best.relevance
+ };
+ }
+ }
+
+ function initHighlighting() {
+ if (initHighlighting.called)
+ return;
+ initHighlighting.called = true;
+ initialize();
+ var pres = document.getElementsByTagName('pre');
+ for (var i = 0; i < pres.length; i++) {
+ var code = findCode(pres[i]);
+ if (code)
+ highlightBlock(code, hljs.tabReplace);
+ }
+ }
+
+ function initHighlightingOnLoad() {
+ var original_arguments = arguments;
+ var handler = function(){initHighlighting.apply(null, original_arguments)};
+ if (window.addEventListener) {
+ window.addEventListener('DOMContentLoaded', handler, false);
+ window.addEventListener('load', handler, false);
+ } else if (window.attachEvent)
+ window.attachEvent('onload', handler);
+ else
+ window.onload = handler;
+ }
+
+ var languages = {}; // a shortcut to avoid writing "this." everywhere
+
+ /* Interface definition */
+
+ this.LANGUAGES = languages;
+ this.initHighlightingOnLoad = initHighlightingOnLoad;
+ this.highlightBlock = highlightBlock;
+ this.initHighlighting = initHighlighting;
+
+ // Common regexps
+ this.IDENT_RE = '[a-zA-Z][a-zA-Z0-9_]*';
+ this.UNDERSCORE_IDENT_RE = '[a-zA-Z_][a-zA-Z0-9_]*';
+ this.NUMBER_RE = '\\b\\d+(\\.\\d+)?';
+ this.C_NUMBER_RE = '\\b(0x[A-Za-z0-9]+|\\d+(\\.\\d+)?)';
+ this.RE_STARTERS_RE = '!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~';
+
+ // Common modes
+ this.BACKSLASH_ESCAPE = {
+ begin: '\\\\.', relevance: 0
+ };
+ this.APOS_STRING_MODE = {
+ className: 'string',
+ begin: '\'', end: '\'',
+ illegal: '\\n',
+ contains: [this.BACKSLASH_ESCAPE],
+ relevance: 0
+ };
+ this.QUOTE_STRING_MODE = {
+ className: 'string',
+ begin: '"', end: '"',
+ illegal: '\\n',
+ contains: [this.BACKSLASH_ESCAPE],
+ relevance: 0
+ };
+ this.C_LINE_COMMENT_MODE = {
+ className: 'comment',
+ begin: '//', end: '$'
+ };
+ this.C_BLOCK_COMMENT_MODE = {
+ className: 'comment',
+ begin: '/\\*', end: '\\*/'
+ };
+ this.HASH_COMMENT_MODE = {
+ className: 'comment',
+ begin: '#', end: '$'
+ };
+ this.NUMBER_MODE = {
+ className: 'number',
+ begin: this.NUMBER_RE,
+ relevance: 0
+ };
+ this.C_NUMBER_MODE = {
+ className: 'number',
+ begin: this.C_NUMBER_RE,
+ relevance: 0
+ };
+
+ // Utility functions
+ this.inherit = function(parent, obj) {
+ var result = {}
+ for (var key in parent)
+ result[key] = parent[key];
+ if (obj)
+ for (var key in obj)
+ result[key] = obj[key];
+ return result;
+ }
+}();
diff --git a/war/js/highlight.pack.js b/war/js/highlight.pack.js
new file mode 100644
index 0000000..7e510e4
--- /dev/null
+++ b/war/js/highlight.pack.js
@@ -0,0 +1 @@
+var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(/"}while(x.length||y.length){var u=t().splice(0,1)[0];v+=l(w.substr(q,u.offset-q));q=u.offset;if(u.event=="start"){v+=r(u.node);s.push(u.node)}else{if(u.event=="stop"){var p=s.length;do{p--;var o=s[p];v+=(""+o.nodeName.toLowerCase()+">")}while(o!=u.node);s.splice(p,1);while(p'+l(K[0])+""}else{M+=l(K[0])}O=N.lR.lastIndex;K=N.lR.exec(L)}M+=l(L.substr(O,L.length-O));return M}function J(r,L){if(L.sL&&d[L.sL]){var K=f(L.sL,r);s+=K.keyword_count;return K.value}else{return E(r,L)}}function H(L,r){var K=L.cN?'':"";if(L.rB){p+=K;L.buffer=""}else{if(L.eB){p+=l(r)+K;L.buffer=""}else{p+=K;L.buffer=r}}B.push(L);A+=L.r}function D(N,K,P){var Q=B[B.length-1];if(P){p+=J(Q.buffer+N,Q);return false}var L=y(K,Q);if(L){p+=J(Q.buffer+N,Q);H(L,K);return L.rB}var r=v(B.length-1,K);if(r){var M=Q.cN?" ":"";if(Q.rE){p+=J(Q.buffer+N,Q)+M}else{if(Q.eE){p+=J(Q.buffer+N,Q)+M+l(K)}else{p+=J(Q.buffer+N+K,Q)+M}}while(r>1){M=B[B.length-2].cN?"":"";p+=M;r--;B.length--}var O=B[B.length-1];B.length--;B[B.length-1].buffer="";if(O.starts){H(O.starts,"")}return Q.rE}if(w(K,Q)){throw"Illegal"}}var G=d[I];var B=[G.dM];var A=0;var s=0;var p="";try{var u=0;G.dM.buffer="";do{var x=q(C,u);var t=D(x[0],x[1],x[2]);u+=x[0].length;if(!t){u+=x[1].length}}while(!x[2]);if(B.length>1){throw"Illegal"}return{language:I,r:A,keyword_count:s,value:p}}catch(F){if(F=="Illegal"){return{language:null,r:0,keyword_count:0,value:l(C)}}else{throw F}}}function h(){function o(t,s,u){if(t.compiled){return}if(!u){t.bR=c(s,t.b?t.b:"\\B|\\b");if(!t.e&&!t.eW){t.e="\\B|\\b"}if(t.e){t.eR=c(s,t.e)}}if(t.i){t.iR=c(s,t.i)}if(t.r==undefined){t.r=1}if(t.k){t.lR=c(s,t.l||hljs.IR,true)}for(var r in t.k){if(!t.k.hasOwnProperty(r)){continue}if(t.k[r] instanceof Object){t.kG=t.k}else{t.kG={keyword:t.k}}break}if(!t.c){t.c=[]}t.compiled=true;for(var q=0;qx.keyword_count+x.r){x=u}if(u.keyword_count+u.r>w.keyword_count+w.r){x=w;w=u}}}var s=t.className;if(!s.match(w.language)){s=s?(s+" "+w.language):w.language}var o=b(t);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=k(o,b(q),A)}if(y){w.value=w.value.replace(/^((<[^>]+>|\t)+)/gm,function(B,E,D,C){return E.replace(/\t/g,y)})}if(p){w.value=w.value.replace(/\n/g," ")}if(/MSIE [678]/.test(navigator.userAgent)&&t.tagName=="CODE"&&t.parentNode.tagName=="PRE"){var q=t.parentNode;var v=document.createElement("div");v.innerHTML=""+w.value+"
";t=v.firstChild.firstChild;v.firstChild.cN=q.cN;q.parentNode.replaceChild(v.firstChild,q)}else{t.innerHTML=w.value}t.className=s;t.dataset={};t.dataset.result={language:w.language,kw:w.keyword_count,re:w.r};if(x&&x.language){t.dataset.second_best={language:x.language,kw:x.keyword_count,re:x.r}}}function j(){if(j.called){return}j.called=true;e();var q=document.getElementsByTagName("pre");for(var o=0;o|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\.",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.inherit=function(o,r){var q={};for(var p in o){q[p]=o[p]}if(r){for(var p in r){q[p]=r[p]}}return q}}();hljs.LANGUAGES.bash=function(){var d={"true":1,"false":1};var b={cN:"variable",b:"\\$([a-zA-Z0-9_]+)\\b"};var a={cN:"variable",b:"\\$\\{(([^}])|(\\\\}))+\\}",c:[hljs.CNM]};var c={cN:"string",b:'"',e:'"',i:"\\n",c:[hljs.BE,b,a],r:0};var e={cN:"test_condition",b:"",e:"",c:[c,b,a,hljs.CNM],k:{literal:d},r:0};return{dM:{k:{keyword:{"if":1,then:1,"else":1,fi:1,"for":1,"break":1,"continue":1,"while":1,"in":1,"do":1,done:1,echo:1,exit:1,"return":1,set:1,declare:1},literal:d},c:[{cN:"shebang",b:"(#!\\/bin\\/bash)|(#!\\/bin\\/sh)",r:10},hljs.HCM,{cN:"comment",b:"\\/\\/",e:"$",i:"."},hljs.CNM,c,b,a,hljs.inherit(e,{b:"\\[ ",e:" \\]",r:0}),hljs.inherit(e,{b:"\\[\\[ ",e:" \\]\\]"})]}}}();hljs.LANGUAGES.erlang=function(){var g="[a-z'][a-zA-Z0-9_']*";var l="("+g+":"+g+"|"+g+")";var d={keyword:{after:1,and:1,andalso:10,band:1,begin:1,bnot:1,bor:1,bsl:1,bzr:1,bxor:1,"case":1,"catch":1,cond:1,div:1,end:1,fun:1,let:1,not:1,of:1,orelse:10,query:1,receive:1,rem:1,"try":1,when:1,xor:1},literal:{"false":1,"true":1}};var j={cN:"comment",b:"%",e:"$",r:0};var c={b:"fun\\s+"+g+"/\\d+"};var m={b:l+"\\(",e:"\\)",rB:true,r:0,c:[{cN:"function_name",b:l,r:0},{b:"\\(",e:"\\)",eW:true,rE:true,r:0}]};var f={cN:"tuple",b:"{",e:"}",r:0};var a={cN:"variable",b:"\\b_([A-Z][A-Za-z0-9_]*)?",r:0};var k={cN:"variable",b:"[A-Z][a-zA-Z0-9_]*",r:0};var h={b:"#",e:"}",i:".",r:0,rB:true,c:[{cN:"record_name",b:"#"+hljs.UIR,r:0},{b:"{",eW:true,r:0}]};var i={k:d,b:"(fun|receive|if|try|case)",e:"end"};i.c=[j,c,hljs.inherit(hljs.ASM,{cN:""}),i,m,hljs.QSM,hljs.CNM,f,a,k,h];var b=[j,c,i,m,hljs.QSM,hljs.CNM,f,a,k,h];m.c[1].c=b;f.c=b;h.c[1].c=b;var e={cN:"params",b:"\\(",e:"\\)",eW:true,c:b};return{dM:{k:d,i:"(|\\*=|\\+=|-=|/=|/\\*|\\*/|\\(\\*|\\*\\))",c:[{cN:"function",b:"^"+g+"\\(",e:";|\\.",rB:true,c:[e,{cN:"title",b:g},{k:d,b:"->",eW:true,c:b}]},j,{cN:"pp",b:"^-",e:"\\.",r:0,eE:true,rB:true,l:"-"+hljs.IR,k:{"-module":1,"-record":1,"-undef":1,"-export":1,"-ifdef":1,"-ifndef":1,"-author":1,"-copyright":1,"-doc":1,"-vsn":1,"-import":1,"-include":1,"-include_lib":1,"-compile":1,"-define":1,"-else":1,"-endif":1,"-file":1,"-behaviour":1,"-behavior":1},c:[e]},hljs.CNM,hljs.QSM,h,a,k,f]}}}();hljs.LANGUAGES.cs={dM:{k:{"abstract":1,as:1,base:1,bool:1,"break":1,"byte":1,"case":1,"catch":1,"char":1,checked:1,"class":1,"const":1,"continue":1,decimal:1,"default":1,delegate:1,"do":1,"do":1,"double":1,"else":1,"enum":1,event:1,explicit:1,extern:1,"false":1,"finally":1,fixed:1,"float":1,"for":1,foreach:1,"goto":1,"if":1,implicit:1,"in":1,"int":1,"interface":1,internal:1,is:1,lock:1,"long":1,namespace:1,"new":1,"null":1,object:1,operator:1,out:1,override:1,params:1,"private":1,"protected":1,"public":1,readonly:1,ref:1,"return":1,sbyte:1,sealed:1,"short":1,sizeof:1,stackalloc:1,"static":1,string:1,struct:1,"switch":1,"this":1,"throw":1,"true":1,"try":1,"typeof":1,uint:1,ulong:1,unchecked:1,unsafe:1,ushort:1,using:1,virtual:1,"volatile":1,"void":1,"while":1,ascending:1,descending:1,from:1,get:1,group:1,into:1,join:1,let:1,orderby:1,partial:1,select:1,set:1,value:1,"var":1,where:1,yield:1},c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",b:"///|"},{cN:"xmlDocTag",b:"?",e:">"}]},hljs.CLCM,hljs.CBLCLM,{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},hljs.ASM,hljs.QSM,hljs.CNM]}};hljs.LANGUAGES.ruby=function(){var g="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var a="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var n={keyword:{and:1,"false":1,then:1,defined:1,module:1,"in":1,"return":1,redo:1,"if":1,BEGIN:1,retry:1,end:1,"for":1,"true":1,self:1,when:1,next:1,until:1,"do":1,begin:1,unless:1,END:1,rescue:1,nil:1,"else":1,"break":1,undef:1,not:1,"super":1,"class":1,"case":1,require:1,yield:1,alias:1,"while":1,ensure:1,elsif:1,or:1,def:1},keymethods:{__id__:1,__send__:1,abort:1,abs:1,"all?":1,allocate:1,ancestors:1,"any?":1,arity:1,assoc:1,at:1,at_exit:1,autoload:1,"autoload?":1,"between?":1,binding:1,binmode:1,"block_given?":1,call:1,callcc:1,caller:1,capitalize:1,"capitalize!":1,casecmp:1,"catch":1,ceil:1,center:1,chomp:1,"chomp!":1,chop:1,"chop!":1,chr:1,"class":1,class_eval:1,"class_variable_defined?":1,class_variables:1,clear:1,clone:1,close:1,close_read:1,close_write:1,"closed?":1,coerce:1,collect:1,"collect!":1,compact:1,"compact!":1,concat:1,"const_defined?":1,const_get:1,const_missing:1,const_set:1,constants:1,count:1,crypt:1,"default":1,default_proc:1,"delete":1,"delete!":1,delete_at:1,delete_if:1,detect:1,display:1,div:1,divmod:1,downcase:1,"downcase!":1,downto:1,dump:1,dup:1,each:1,each_byte:1,each_index:1,each_key:1,each_line:1,each_pair:1,each_value:1,each_with_index:1,"empty?":1,entries:1,eof:1,"eof?":1,"eql?":1,"equal?":1,"eval":1,exec:1,exit:1,"exit!":1,extend:1,fail:1,fcntl:1,fetch:1,fileno:1,fill:1,find:1,find_all:1,first:1,flatten:1,"flatten!":1,floor:1,flush:1,for_fd:1,foreach:1,fork:1,format:1,freeze:1,"frozen?":1,fsync:1,getc:1,gets:1,global_variables:1,grep:1,gsub:1,"gsub!":1,"has_key?":1,"has_value?":1,hash:1,hex:1,id:1,include:1,"include?":1,included_modules:1,index:1,indexes:1,indices:1,induced_from:1,inject:1,insert:1,inspect:1,instance_eval:1,instance_method:1,instance_methods:1,"instance_of?":1,"instance_variable_defined?":1,instance_variable_get:1,instance_variable_set:1,instance_variables:1,"integer?":1,intern:1,invert:1,ioctl:1,"is_a?":1,isatty:1,"iterator?":1,join:1,"key?":1,keys:1,"kind_of?":1,lambda:1,last:1,length:1,lineno:1,ljust:1,load:1,local_variables:1,loop:1,lstrip:1,"lstrip!":1,map:1,"map!":1,match:1,max:1,"member?":1,merge:1,"merge!":1,method:1,"method_defined?":1,method_missing:1,methods:1,min:1,module_eval:1,modulo:1,name:1,nesting:1,"new":1,next:1,"next!":1,"nil?":1,nitems:1,"nonzero?":1,object_id:1,oct:1,open:1,pack:1,partition:1,pid:1,pipe:1,pop:1,popen:1,pos:1,prec:1,prec_f:1,prec_i:1,print:1,printf:1,private_class_method:1,private_instance_methods:1,"private_method_defined?":1,private_methods:1,proc:1,protected_instance_methods:1,"protected_method_defined?":1,protected_methods:1,public_class_method:1,public_instance_methods:1,"public_method_defined?":1,public_methods:1,push:1,putc:1,puts:1,quo:1,raise:1,rand:1,rassoc:1,read:1,read_nonblock:1,readchar:1,readline:1,readlines:1,readpartial:1,rehash:1,reject:1,"reject!":1,remainder:1,reopen:1,replace:1,require:1,"respond_to?":1,reverse:1,"reverse!":1,reverse_each:1,rewind:1,rindex:1,rjust:1,round:1,rstrip:1,"rstrip!":1,scan:1,seek:1,select:1,send:1,set_trace_func:1,shift:1,singleton_method_added:1,singleton_methods:1,size:1,sleep:1,slice:1,"slice!":1,sort:1,"sort!":1,sort_by:1,split:1,sprintf:1,squeeze:1,"squeeze!":1,srand:1,stat:1,step:1,store:1,strip:1,"strip!":1,sub:1,"sub!":1,succ:1,"succ!":1,sum:1,superclass:1,swapcase:1,"swapcase!":1,sync:1,syscall:1,sysopen:1,sysread:1,sysseek:1,system:1,syswrite:1,taint:1,"tainted?":1,tell:1,test:1,"throw":1,times:1,to_a:1,to_ary:1,to_f:1,to_hash:1,to_i:1,to_int:1,to_io:1,to_proc:1,to_s:1,to_str:1,to_sym:1,tr:1,"tr!":1,tr_s:1,"tr_s!":1,trace_var:1,transpose:1,trap:1,truncate:1,"tty?":1,type:1,ungetc:1,uniq:1,"uniq!":1,unpack:1,unshift:1,untaint:1,untrace_var:1,upcase:1,"upcase!":1,update:1,upto:1,"value?":1,values:1,values_at:1,warn:1,write:1,write_nonblock:1,"zero?":1,zip:1}};var h={cN:"yardoctag",b:"@[A-Za-z]+"};var d={cN:"comment",b:"#",e:"$",c:[h]};var c={cN:"comment",b:"^\\=begin",e:"^\\=end",c:[h],r:10};var b={cN:"comment",b:"^__END__",e:"\\n$"};var u={cN:"subst",b:"#\\{",e:"}",l:g,k:n};var p=[hljs.BE,u];var s={cN:"string",b:"'",e:"'",c:p,r:0};var r={cN:"string",b:'"',e:'"',c:p,r:0};var q={cN:"string",b:"%[qw]?\\(",e:"\\)",c:p,r:10};var o={cN:"string",b:"%[qw]?\\[",e:"\\]",c:p,r:10};var m={cN:"string",b:"%[qw]?{",e:"}",c:p,r:10};var l={cN:"string",b:"%[qw]?<",e:">",c:p,r:10};var k={cN:"string",b:"%[qw]?/",e:"/",c:p,r:10};var j={cN:"string",b:"%[qw]?%",e:"%",c:p,r:10};var i={cN:"string",b:"%[qw]?-",e:"-",c:p,r:10};var t={cN:"string",b:"%[qw]?\\|",e:"\\|",c:p,r:10};var e={cN:"function",b:"\\bdef\\s+",e:" |$|;",l:g,k:n,c:[{cN:"title",b:a,l:g,k:n},{cN:"params",b:"\\(",e:"\\)",l:g,k:n},d,c,b]};var f={cN:"identifier",b:g,l:g,k:n,r:0};var v=[d,c,b,s,r,q,o,m,l,k,j,i,t,{cN:"class",b:"\\b(class|module)\\b",e:"$|;",k:{"class":1,module:1},c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+hljs.IR+"::)?"+hljs.IR}]},d,c,b]},e,{cN:"constant",b:"(::)?([A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:[s,r,q,o,m,l,k,j,i,t,f],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},f,{b:"("+hljs.RSR+")\\s*",c:[d,c,b,{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[hljs.BE]}],r:0}];u.c=v;e.c[1].c=v;return{dM:{l:g,k:n,c:v}}}();hljs.LANGUAGES.diff={cI:true,dM:{c:[{cN:"chunk",b:"^\\@\\@ +\\-\\d+,\\d+ +\\+\\d+,\\d+ +\\@\\@$",r:10},{cN:"chunk",b:"^\\*\\*\\* +\\d+,\\d+ +\\*\\*\\*\\*$",r:10},{cN:"chunk",b:"^\\-\\-\\- +\\d+,\\d+ +\\-\\-\\-\\-$",r:10},{cN:"header",b:"Index: ",e:"$"},{cN:"header",b:"=====",e:"=====$"},{cN:"header",b:"^\\-\\-\\-",e:"$"},{cN:"header",b:"^\\*{3} ",e:"$"},{cN:"header",b:"^\\+\\+\\+",e:"$"},{cN:"header",b:"\\*{5}",e:"\\*{5}$"},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}};hljs.LANGUAGES.javascript={dM:{k:{keyword:{"in":1,"if":1,"for":1,"while":1,"finally":1,"var":1,"new":1,"function":1,"do":1,"return":1,"void":1,"else":1,"break":1,"catch":1,"instanceof":1,"with":1,"throw":1,"case":1,"default":1,"try":1,"this":1,"switch":1,"continue":1,"typeof":1,"delete":1},literal:{"true":1,"false":1,"null":1}},c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM,hljs.CNM,{b:"("+hljs.RSR+"|case|return|throw)\\s*",k:{"return":1,"throw":1,"case":1},c:[hljs.CLCM,hljs.CBLCLM,{cN:"regexp",b:"/.*?[^\\\\/]/[gim]*"}],r:0},{cN:"function",b:"\\bfunction\\b",e:"{",k:{"function":1},c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[hljs.ASM,hljs.QSM,hljs.CLCM,hljs.CBLCLM]}]}]}};hljs.LANGUAGES.lua=function(){var b="\\[=*\\[";var e="\\]=*\\]";var a={b:b,e:e};a.c=[a];var d={cN:"comment",b:"--(?!"+b+")",e:"$"};var c={cN:"comment",b:"--"+b,e:e,c:[a],r:10};return{dM:{l:hljs.UIR,k:{keyword:{and:1,"break":1,"do":1,"else":1,elseif:1,end:1,"false":1,"for":1,"if":1,"in":1,local:1,nil:1,not:1,or:1,repeat:1,"return":1,then:1,"true":1,until:1,"while":1},built_in:{_G:1,_VERSION:1,assert:1,collectgarbage:1,dofile:1,error:1,getfenv:1,getmetatable:1,ipairs:1,load:1,loadfile:1,loadstring:1,module:1,next:1,pairs:1,pcall:1,print:1,rawequal:1,rawget:1,rawset:1,require:1,select:1,setfenv:1,setmetatable:1,tonumber:1,tostring:1,type:1,unpack:1,xpcall:1,coroutine:1,debug:1,io:1,math:1,os:1,"package":1,string:1,table:1}},c:[d,c,{cN:"function",b:"\\bfunction\\b",e:"\\)",k:{"function":1},c:[{cN:"title",b:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"},{cN:"params",b:"\\(",eW:true,c:[d,c]},d,c]},hljs.CNM,hljs.ASM,hljs.QSM,{cN:"string",b:b,e:e,c:[a],r:10}]}}}();hljs.LANGUAGES.css=function(){var a={cN:"function",b:hljs.IR+"\\(",e:"\\)",c:[{eW:true,eE:true,c:[hljs.NM,hljs.ASM,hljs.QSM]}]};return{cI:true,dM:{i:"[=/|']",c:[hljs.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@font-face",l:"[a-z-]+",k:{"font-face":1}},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:{"import":1,page:1,media:1,charset:1},c:[a,hljs.ASM,hljs.QSM,hljs.NM]},{cN:"tag",b:hljs.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[hljs.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[a,hljs.NM,hljs.QSM,hljs.ASM,hljs.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}}();hljs.LANGUAGES.xml=function(){var b="[A-Za-z0-9\\._:-]+";var a={eW:true,c:[{cN:"attribute",b:b,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,dM:{c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"', returnEnd: true,
+ subLanguage: 'javascript'
+ }
+ },
+ {
+ className: 'vbscript',
+ begin: '<%', end: '%>',
+ subLanguage: 'vbscript'
+ },
+ {
+ className: 'tag',
+ begin: '?', end: '/?>',
+ contains: [
+ {
+ className: 'title', begin: '[^ />]+'
+ },
+ TAG_INTERNALS
+ ]
+ }
+ ]
+ }
+ };
+}();
diff --git a/war/js/mediaChoserPlugin.js b/war/js/mediaChoserPlugin.js
new file mode 100644
index 0000000..f66b3bb
--- /dev/null
+++ b/war/js/mediaChoserPlugin.js
@@ -0,0 +1,56 @@
+(function($) {
+ // Define the hello button
+ $.cleditor.buttons.media = {
+ name: "media",
+ image: "../../images/images2.png",
+ title: "Choose an image from the media library",
+ command: "inserthtml",
+ popupName: "media",
+ popupClass: "cleditorPrompt",
+ popupContent: "Image URL:
",
+ buttonClick: mediaClick
+ };
+
+ // Add the button to the default controls before the bold button
+ $.cleditor.defaultOptions.controls = $.cleditor.defaultOptions.controls
+ .replace("image", "image media");
+
+ var page = 0;
+
+ function addImgClickHandler() {
+ $('#imgs img').click(function(img) {
+ console.log(img.srcElement.getAttribute('fullsrc'));
+ $('#imgToInsert').val(img.srcElement.getAttribute('fullsrc'));
+ $('#submit').click();
+ });
+ }
+
+ // Handle the hello button click event
+ function mediaClick(e, data) {
+ page = 0;
+ $('#imgs').load('/admin/mediaSelector', addImgClickHandler);
+
+ $(data.popup).children("#next").click(function(e) {
+ $('#imgs').load('/admin/mediaSelector?page=' + ++page, addImgClickHandler);
+ });
+
+ // Wire up the submit button click event
+ $(data.popup).children("#submit")
+ .unbind("click")
+ .bind("click", function(e) {
+ // Get the editor
+ var editor = data.editor;
+
+ // Get the full url of the image clicked
+ var fullUrl = $(data.popup).find("#imgToInsert").val();
+
+ // Insert the img tag into the document
+ var html = " ";
+ editor.execCommand(data.command, html, null, data.button);
+
+ // Hide the popup and set focus back to the editor
+ editor.hidePopups();
+ editor.focus();
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/war/js/postEdit.js b/war/js/postEdit.js
new file mode 100644
index 0000000..283d158
--- /dev/null
+++ b/war/js/postEdit.js
@@ -0,0 +1,88 @@
+$(document).ready(function() {
+ $(".chzn-select").chosen();
+
+ var editor = $("#content").cleditor({
+ width: 600,
+ height: 400,
+ useCSS: true,
+ docCSSFile: '/css/wysiwyg.css',
+ styles: [
+ ["Paragraph", ""],
+ ["Header 1", "
"],
+ ["Header 2", ""],
+ ["Header 3", ""],
+ ["Header 4",""],
+ ["Code sample", ""],
+ ["Quote", ""]
+ ]
+ });
+
+ $('#titleInput').blur(function(evt) {
+ if ($(this).val().length == 0) {
+ $('#titleInputError').css('display', 'inline-block');
+ $('#submitButton').attr('disabled', 'true');
+ } else {
+ $('#titleInputError').css('display', 'none');
+ if ($('#createdInput').val().match('\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}')) {
+ $('#submitButton').attr('disabled', false);
+ }
+ }
+ });
+
+ $('#createdInput').blur(function(evt) {
+ if (!$(this).val().match('\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}')) {
+ $('#dateInputError').css('display', 'inline-block');
+ $('#submitButton').attr('disabled', 'true');
+ } else {
+ $('#dateInputError').css('display', 'none');
+ if ($('#titleInput').val().length > 0) {
+ $('#submitButton').attr('disabled', false);
+ }
+ }
+ }).keydown(function(event) {
+ if (event.keyCode == 38 || event.keyCode == 40) {
+ event.preventDefault();
+ var direction = event.keyCode == 38 ? 1 : -1;
+ var date = new Date(event.srcElement.value);
+ var position = event.srcElement.selectionStart;
+ switch (position) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ date.setFullYear(date.getFullYear() + direction);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ date.setMonth(date.getMonth() + direction);
+ break;
+ case 8:
+ case 9:
+ case 10:
+ date.setDate(date.getDate() + direction);
+ break;
+ case 11:
+ case 12:
+ case 13:
+ date.setHours(date.getHours() + direction);
+ break;
+ case 14:
+ case 15:
+ case 16:
+ date.setMinutes(date.getMinutes() + direction);
+ break;
+ }
+ event.srcElement.value = '' + date.getFullYear() + '/' +
+ (date.getMonth()+1 < 10 ? '0' + (date.getMonth()+1) : (date.getMonth()+1)) + '/' +
+ (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ' +
+ (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':' +
+ (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes());
+ event.srcElement.selectionStart = position;
+ event.srcElement.selectionEnd = position;
+ }
+ });
+
+ $('#titleInput').focus();
+});
\ No newline at end of file
diff --git a/war/msg.txt b/war/msg.txt
new file mode 100644
index 0000000..5f6a0ad
--- /dev/null
+++ b/war/msg.txt
@@ -0,0 +1,183 @@
+Content-Type: multipart/mixed; boundary=20cf300fa979c69bb904a8e98271
+
+--20cf300fa979c69bb904a8e98271
+Content-Type: multipart/related; boundary=20cf300fa979c69bb804a8e98270
+
+--20cf300fa979c69bb804a8e98270
+Content-Type: multipart/alternative; boundary=20cf300fa979c69bb604a8e9826f
+
+--20cf300fa979c69bb604a8e9826f
+Content-Type: text/plain; charset=ISO-8859-1
+
+The Groovy development team is really pleased and proud to announce the
+release of the *final version of Groovy 1.8.0*!
+
+After a lot of work and efforts throughout four betas and four release
+candidates, version 1.8 of Groovy has been long in the making, but is packed
+with *tons of new features and enhancements*, for your productivity, and
+your pleasure. In particular, you'll be happy to learn about:
+
+ - the new Domain-Specific Language authoring
+capabilities
+for
+ more readability and expressivity of your business rules,
+ - the runtime performance
+improvements
+ ,
+ - the bundling of the GPars parallel and concurrency
+library
+ ,
+ - the built-in JSON
+support
+ ,
+ - the new compile-time meta-programming
+features
+(several
+ new useful AST transformations),
+ - the new functional programming aspects of
+closures
+ ,
+ - and much more.
+
+To get all the details, with code samples, we have prepared an in-depth release
+notes document. Please have a look at it to learn
+more about the features listed above, and discover other smaller
+enhancements as well.
+
+[?]
+
+You can download Groovy 1.8 in our JIRA
+tickets
+that
+have found their way into this major release.
+
+We'd like to thank all those who participated and contributed to this
+release: users, contributors, committers, framework writers, IDE developers,
+book authors. Without you all, Groovy wouldn't be the great productive
+language it is now. And again, without you all, Groovy wouldn't be
+surrounded by its *vibrant, active and rich ecosystem*, giving you advanced
+tools and frameworks for building web applications (Grails
+, Gaelyk ) or rich desktop applications
+(Griffon ),
+for building your own projects (Gradle ), for
+testing your projects (Spock ,
+Geb ),
+for tackling the concurrency and parallel problems on our multi-core /
+multi-processor architectures (GPars ), or for
+improving the quality of your Groovy code bases
+(CodeNarc for
+static code analysis,
+GContracts for
+design by contract).
+
+Enjoy this release!
+
+--20cf300fa979c69bb604a8e9826f
+Content-Type: text/html; charset=ISO-8859-1
+Content-Transfer-Encoding: quoted-printable
+
+The Groovy development team is really pleased and proud to announce t=
+he release of the=A0final version of Groovy 1.8.0 !
+
+After a lot of work and efforts throughout four betas and four release c=
+andidates, version 1.8 of Groovy has been long in the making, but is packed=
+ with=A0tons of new features and enhancements , for your productivity=
+, and your pleasure. In particular, you'll be happy to learn about:
+
+To get all the details, with =
+code samples, we have prepared an in-depth=A0release notes =A0document. Please have a look at=
+ it to learn more about the features listed above, and discover other small=
+er enhancements as well.
+
+
You can download Groovy 1.8 in our=A0JIRA tickets =A0that have found their way into this maj=
+or release.
+
+We'd like to thank all those who participated and contributed to thi=
+s release: users, contributors, committers, framework writers, IDE develope=
+rs, book authors. Without you all, Groovy wouldn't be the great product=
+ive language it is now. And again, without you all, Groovy wouldn't be =
+surrounded by its=A0vibrant, active and rich ecosystem , giving you a=
+dvanced tools and frameworks for building web applications (Grails ,=A0Gaelyk ) or rich desktop applications (Griffon ), for b=
+uilding your own projects (Gradle ), for testing your projects (Spock ,=A0Geb ), for tackling the concurrency=
+ and parallel problems on our multi-core / multi-processor architectures (<=
+a href=3D"http://gpars.codehaus.org/" target=3D"_blank">GPars), or for =
+improving the quality of your Groovy code bases (CodeNarc =A0for static code analysi=
+s,=A0GContracts =A0for design by contract).
+
+Enjoy this release!
+
+--20cf300fa979c69bb604a8e9826f--
+--20cf300fa979c69bb804a8e98270
+Content-Type: image/png; name="32C.png"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: gtalk.32C@goomoji.gmail
+Content-ID:
+
+iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAFoTx1HAAACO0lEQVQoz0VSS2hTURC9K1emz5qk
+sSLqroIUXCiIIt2JRSVgrSZFCCpEi62FSgU/tEUR1Nq+/nFlutVNRPzbmKrgqoobjTXGNoiiNfXV
+akG5zT3OzG10Me/dOzNn5syZqwAo7Zbf5c8DxKvp1uVrZq/ClzjMu4h4jicSCbAVH20VQFVxrIai
+UdhUrjIUylIgpPC9CSicA8w8FAqNNoGKKfNiF/DxIPRgcFow42crfm+oDuBVuh6dJzdRooLudsJc
+LIeZ02hpaQF+JOyf6jGa2wfNm3pgvAFpru0sAWaOQQ8EPbU4UIDsth4OvSfyrewTnlR7B/fgXtyT
+e+cuVmoJFu9vBv5MyJjM33VdwGsDVTijkI8JgVgsJoTC4TAlnYK+unJKCRkaEOe3WDJ8/tYI3ed/
+pvRAIM8X1K5FktmObIfJNkD3LFuldH8gYzL7YN7ul9mKqW3QV5zoP1VlnG6n9nFz2WyyowrpkRpM
+Pa8D85kc3Ylk5zrcjJf9ItCe/2O65X7q/Ul4zbYDc9eBn7csLzZvyN7nemEmItD9wQJhKhl4z7ze
+S6ALvFg4jiM7kQJkpbvneZTTY8F9/idKD6/IiUTTR+3uSUfMjwLJ3bImfL5jfRz7eoRmPcTS5flt
+tRYfkmz8aJhaqk4kjFEXNiwqJjHKEYG6fB1WmEtLTyzcWE/zNMkDQO4w0LbR2ssDAjDZKBaurTEk
+UKQkTlwPVmTImde9y5/qyz6XZkgRnQ9kk3Qeow7ttJ/VJUXZ/gJk3Blv6VZlpAAAAABJRU5ErkJg
+gg==
+--20cf300fa979c69bb804a8e98270--
+--20cf300fa979c69bb904a8e98271
+Content-Type: image/png; name="clipboard.png"
+Content-Disposition: attachment; filename="clipboard.png"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_gqjsik890
+
+iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
+bWFnZVJlYWR5ccllPAAAAm1JREFUeNqMU81rE1EQ/2V3s9lgSoyQSJCY5mCgFIJYD160UGgFEQsF
+i3rspZ78Ezz4D/hV8eNQeql4iAfFi+C5EUx7aJCERKiBqP2QaJNNstnd7Drz8n1zYHhv3sz85vfe
+zPOszUKIx4OB7Pmn1O8npq/TNtw7OppsfP2QaOXNfozrdldlmIaXpDO8ObWfV1JX5yOXVu5F2f68
+/uRX5WP6PoKwe7HbpKui8NPZAUB2YXllplAoIHLnBTRNRTAYEI7jYx2GYeLw9V0kk0l8Sm8wwEXB
+oOMMKZimiVqthsNHS1BVVagkSbAsS/h8Pp9YR3OUTmdodMhIJBKo1+vCLkshtCRt4Nc0DVlDhe4P
+a7/DU3CczjgDBuBqHMhiIYDVBxuw6dx0HLRtG9mdHeR2dyfKk5fP2ZZVkphBXzl5VE1SlxJtXkkd
+oq/rOh6+2TpLZ/NUIynZxGCgVKGf3Gq1KLgBifq7nc+jUC5D83pRrVaxtrmJhVTqWSGXW1bskTfg
+RMMw0Gw20W63ofs0ARCPx4Vfpge9tbgIg+IketB3mUxNse0hAFflLjhE20OJDg2LROfFYrE7TaHQ
+IO789DR3RB5jwFU5UZZlYTs0bswgFothVAKBAFRiY7bbkmKNAPB0Sr1k0RXbFYClUknYIWLAV4xG
+o4IZAchKWQfO+IcDzvccDBbdlb/I0twcuVzByOkxY+BGve5V3v4EbpwGomo3SR5h4LVauH3twhh9
+/nTf9o72S1p4ndge8Gfyvz/AxM2TEGPrpVZxNdYrkT4xd2xNb1V+fPlbeUzbugAgjZQbKD5/lcH/
+yB8X3BaGt/8JMAAlnT4RqrvSbAAAAABJRU5ErkJggg==
+--20cf300fa979c69bb904a8e98271--
\ No newline at end of file
diff --git a/war/robots.txt b/war/robots.txt
new file mode 100644
index 0000000..6b99d51
--- /dev/null
+++ b/war/robots.txt
@@ -0,0 +1,4 @@
+Sitemap: http://glaforge.appspot.com/sitemap.xml
+User-agent: *
+Disallow: /admin
+Disallow: /live
diff --git a/war/sitemap.xml b/war/sitemap.xml
new file mode 100644
index 0000000..049a9aa
--- /dev/null
+++ b/war/sitemap.xml
@@ -0,0 +1,1399 @@
+
+
+
+
+
+ http://glaforge.appspot.com/
+ weekly
+ 1.00
+
+
+ http://glaforge.appspot.com/feed/atom
+ 2011-07-30T14:03:06+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/page/contact-me
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/page/about-this-site
+ 2011-07-30T14:03:07+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/archives
+ 2011-07-30T14:03:07+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/social
+ 2011-07-30T14:03:08+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/search
+ 2011-07-30T14:03:10+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/article/blog-reboot
+ 2011-07-30T14:03:10+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/category/Gaelyk
+ 2011-07-30T14:03:10+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/category/Groovy
+ 2011-07-30T14:03:11+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/article/groovy-1-8-final-is-out-the-door
+ 2011-07-30T14:03:11+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/article/gr8conf-europe-2011-a-conference-dedicated-to-the-groovy-ecosystem
+ 2011-07-30T14:03:11+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/article/new-important-milestone-for-gaelyk-with-version-0-6-released
+ 2011-07-30T14:03:12+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/article/gaelyk-0-4-4-out-with-namespace-multitenancy-support
+ 2011-07-30T14:03:12+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/p1
+ 2011-07-30T14:03:12+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/category/Conference
+ 2011-07-30T14:03:13+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/feed/atom/Conference
+ 2011-07-30T14:03:13+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/category/DSL
+ 2011-07-30T14:03:13+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/feed/atom/DSL
+ 2011-07-30T14:03:14+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/category/French
+ 2011-07-30T14:03:14+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/feed/atom/French
+ 2011-07-30T14:03:14+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/feed/atom/Gaelyk
+ 2011-07-30T14:03:14+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/category/Geek
+ 2011-07-30T14:03:14+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/feed/atom/Geek
+ 2011-07-30T14:03:15+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/feed/atom/Groovy
+ 2011-07-30T14:03:15+00:00
+ weekly
+ 0.80
+
+
+ http://glaforge.appspot.com/article/nicer-dsls-in-groovy-1-8-thanks-to-extended-command-expressions
+ 2011-07-30T14:03:15+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/gr8conf-the-european-conference-dedicated-to-the-groovy-ecosystem-is-back
+ 2011-07-30T14:03:15+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/final-release-of-groovy-1-7
+ 2011-07-30T14:03:16+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/release-train-grails-1-1-2-groovy-1-6-6-and-1-7-rc-1-as-well-as-gaelyk-0-3-2
+ 2011-07-30T14:03:16+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/devoxx-presentation-on-google-app-engine-groovy-and-gaelyk
+ 2011-07-30T14:03:16+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/archives/p1
+ 2011-07-30T14:03:16+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/gaelyk-0-3-released-a-lightweight-groovy-toolkit-for-google-app-engine
+ 2011-07-30T14:03:17+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/gaelyk-0-2-released-a-lightweight-toolkit-for-google-app-engine
+ 2011-07-30T14:03:17+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/category/Groovy/p1
+ 2011-07-30T14:03:18+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/p2
+ 2011-07-30T14:03:18+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/announcing-the-gr8-conference-a-conference-dedicated-to-groovy-grails-and-griffon
+ 2011-07-30T14:03:18+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/javazone-writing-groovy-dsls-presentation-online
+ 2011-07-30T14:03:19+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/groovy-and-grails-at-the-paris-jug-tomorrow-night
+ 2011-07-30T14:03:19+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/meeting-neil-armstrong-and-speaking-of-groovy-and-grails
+ 2011-07-30T14:03:19+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/the-it-conference-you-can-t-miss-if-you-re-in-paris
+ 2011-07-30T14:03:19+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/groovy-grails-meetup-next-monday-evening-in-san-francisco
+ 2011-07-30T14:03:20+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/javapolis-interview-with-guillaume-laforge-groovy-project-lead
+ 2011-07-30T14:03:20+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/learn-all-about-groovy-and-grails-at-the-2gx-conference-reston-va
+ 2011-07-30T14:03:20+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/category/Conference/p1
+ 2011-07-30T14:03:20+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/a-domain-specific-language-for-unit-manipulations
+ 2011-07-30T14:03:21+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/from-named-parameters-to-domain-specific-languages
+ 2011-07-30T14:03:21+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/cnn-google-earth-et-la-g-ographie-fran-aise
+ 2011-07-30T14:03:21+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/aquarelle-de-versailles
+ 2011-07-30T14:03:22+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/noeuds-de-cravate
+ 2011-07-30T14:03:22+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/une-huile-de-v-zelay
+ 2011-07-30T14:03:22+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/une-gouache-de-macareux
+ 2011-07-30T14:03:23+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/une-esquisse-de-renaud
+ 2011-07-30T14:03:23+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/allergie-aux-piq-res-de-taon
+ 2011-07-30T14:03:23+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/la-solution-du-rubik-s-cube
+ 2011-07-30T14:03:23+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/beignets-d-acacia
+ 2011-07-30T14:03:24+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/calcul-du-jour-de-p-ques-suite
+ 2011-07-30T14:03:24+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/category/French/p1
+ 2011-07-30T14:03:24+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/tip-view-unread-mails-in-gmail
+ 2011-07-30T14:03:24+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/java-s-and-groovy-s-king-at-versailles
+ 2011-07-30T14:03:25+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/infoq-a-community-news-site-for-the-architects
+ 2011-07-30T14:03:25+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/google-summer-of-code-2005-tshirt
+ 2011-07-30T14:03:25+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/debugging-xml-parser-issues
+ 2011-07-30T14:03:25+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/four-years-to-fix-a-trivial-bug
+ 2011-07-30T14:03:26+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/google-base-ning-or-how-to-store-your-life
+ 2011-07-30T14:03:26+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/jboss-wiki-portlet-why-not-xwiki
+ 2011-07-30T14:03:26+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/talking-about-google-talk
+ 2011-07-30T14:03:26+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/spring-in-french-c-est-le-printemps
+ 2011-07-30T14:03:27+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/category/Geek/p1
+ 2011-07-30T14:03:27+00:00
+ weekly
+ 0.64
+
+
+ http://glaforge.appspot.com/article/griffon-the-holy-grail-of-swing-is-one-year-old
+ 2011-07-30T14:03:27+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/le-podcast-des-cast-codeurs-est-sorti
+ 2011-07-30T14:03:27+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/write-groovy-applications-on-google-app-engine
+ 2011-07-30T14:03:28+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/lots-of-groovy-related-news
+ 2011-07-30T14:03:28+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/the-final-version-of-groovy-1-6-is-there
+ 2011-07-30T14:03:28+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/groovy-1-6-rc-2-is-out-final-version-fast-approaching
+ 2011-07-30T14:03:28+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/the-groovy-1-6-release-candidate-is-out
+ 2011-07-30T14:03:29+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/archives/p2
+ 2011-07-30T14:03:29+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/springsource-acquires-g2one
+ 2011-07-30T14:03:29+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/groovymag-the-groovy-grails-magazine-is-out
+ 2011-07-30T14:03:29+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/category/Groovy/p2
+ 2011-07-30T14:03:30+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/p3
+ 2011-07-30T14:03:30+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/g2one-the-groovy-and-grails-meetup-at-javaone
+ 2011-07-30T14:03:30+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/groovy-awarded-jax-innovation-first-prize
+ 2011-07-30T14:03:30+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/groovy-and-grails-news-conferences-and-ide-support
+ 2011-07-30T14:03:31+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/groovy-in-action-manning
+ 2011-07-30T14:03:31+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/scripting-at-javaone-2006
+ 2011-07-30T14:03:31+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/javaone-groovy-reactions
+ 2011-07-30T14:03:31+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/calcul-du-jour-de-p-ques
+ 2011-07-30T14:03:31+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/apache-xml-rpc-c-t-client
+ 2011-07-30T14:03:32+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/les-jours-de-la-semaine
+ 2011-07-30T14:03:32+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/wifi-rabbit-for-continuous-integration
+ 2011-07-30T14:03:32+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/maven-developer-s-notebook
+ 2011-07-30T14:03:32+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/tip-o-the-day-ssh-on-windows
+ 2011-07-30T14:03:33+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/how-to-remove-accents-from-a-string
+ 2011-07-30T14:03:33+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/maven-tip-using-ant-s-optional-ftp-task
+ 2011-07-30T14:03:33+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/castor-tip-generating-java-classes-for-xsd-simple-types
+ 2011-07-30T14:03:33+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/rife-rocks-the-pants-off-of-rails
+ 2011-07-30T14:03:34+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/auto-completion-in-a-dos-console
+ 2011-07-30T14:03:34+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/intellij-as-a-team-communication-tool
+ 2011-07-30T14:03:34+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/on-board-jetbrains
+ 2011-07-30T14:03:34+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/category/Geek/p2
+ 2011-07-30T14:03:35+00:00
+ weekly
+ 0.51
+
+
+ http://glaforge.appspot.com/article/sun-abandons-swing
+ 2011-07-30T14:03:35+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/both-groovy-1-5-7-and-1-6-beta-2-are-out
+ 2011-07-30T14:03:35+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/listing-the-properties-of-a-class-in-order
+ 2011-07-30T14:03:35+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/groovy-in-action-book-now-in-japanese
+ 2011-07-30T14:03:36+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/knowing-which-variables-are-bound-or-not-in-a-groovy-script
+ 2011-07-30T14:03:36+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/archives/p3
+ 2011-07-30T14:03:36+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/groovy-and-grails-trainings-by-g2one-in-north-american
+ 2011-07-30T14:03:36+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/groovy-1-6-beta-1-release-with-great-performance-improvements
+ 2011-07-30T14:03:37+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/category/Groovy/p3
+ 2011-07-30T14:03:37+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/p4
+ 2011-07-30T14:03:37+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/becoming-an-o-reilly-book-author
+ 2011-07-30T14:03:38+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/pair-wiki-ing
+ 2011-07-30T14:03:38+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/continuous-integration-with-damagecontrol
+ 2011-07-30T14:03:38+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/jetbrains-fashion-victim
+ 2011-07-30T14:03:38+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/first-oss-get-together-in-paris
+ 2011-07-30T14:03:38+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/seen-on-maven-irc
+ 2011-07-30T14:03:39+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/intellij-ssh2-finally-finds-its-way
+ 2011-07-30T14:03:39+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/first-steps-in-tdd-land
+ 2011-07-30T14:03:39+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/happy-birthday-codehaus
+ 2011-07-30T14:03:39+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/how-smartcvs-saved-my-life
+ 2011-07-30T14:03:40+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/category/Geek/p3
+ 2011-07-30T14:03:40+00:00
+ weekly
+ 0.41
+
+
+ http://glaforge.appspot.com/article/grails-nominated-for-jax-innovation-award
+ 2011-07-30T14:03:40+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/groovy-1-5-5-released-compiler-3-5x-faster
+ 2011-07-30T14:03:40+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/groovy-grails-support-in-netbeans-and-glassfish
+ 2011-07-30T14:03:40+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/json-net-the-groovy-way
+ 2011-07-30T14:03:41+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/archives/p4
+ 2011-07-30T14:03:41+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/a-groovy-kind-of-love
+ 2011-07-30T14:03:41+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/the-groovy-zone-community-news-site-for-the-groovy-and-grails-developers
+ 2011-07-30T14:03:41+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/category/Groovy/p4
+ 2011-07-30T14:03:42+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/p5
+ 2011-07-30T14:03:42+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/cvs-and-ssh2-not-so-easy
+ 2011-07-30T14:03:42+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/javablogs-resurrection
+ 2011-07-30T14:03:42+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/intellij-prayer
+ 2011-07-30T14:03:43+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/intellij-version-of-miranda-rights
+ 2011-07-30T14:03:43+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/code-name-for-intellij-idea-5-0
+ 2011-07-30T14:03:43+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/javablogs-downtime
+ 2011-07-30T14:03:43+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/some-good-rules-for-an-efficient-ant-build-script
+ 2011-07-30T14:03:44+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/halloween-jukebox
+ 2011-07-30T14:03:44+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/wiki-like-java-html-editor
+ 2011-07-30T14:03:44+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/intellij-idea-showencoding-plugin
+ 2011-07-30T14:03:44+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/category/Geek/p4
+ 2011-07-30T14:03:45+00:00
+ weekly
+ 0.33
+
+
+ http://glaforge.appspot.com/article/markmail-archives-the-groovy-mailing-lists-and-shows-their-success
+ 2011-07-30T14:03:45+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/groovy-not-enterprise-ready-you-re-kidding
+ 2011-07-30T14:03:45+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/infoq-what-s-new-in-groovy-1-5
+ 2011-07-30T14:03:45+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/groovy-1-5-released
+ 2011-07-30T14:03:46+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/g2one-a-groovy-and-grails-company
+ 2011-07-30T14:03:46+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/groovy-1-1-beta-3-released-rc-1-and-1-1-final-around-the-corner
+ 2011-07-30T14:03:46+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/archives/p5
+ 2011-07-30T14:03:46+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/groovy-1-1-beta-2-with-contributions-from-jetbrains-and-jboss
+ 2011-07-30T14:03:47+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/qotd-eclipse-is-the-pc-of-ides-when-intellij-idea-is-the-mac
+ 2011-07-30T14:03:47+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/category/Groovy/p5
+ 2011-07-30T14:03:47+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/p6
+ 2011-07-30T14:03:47+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/unicode-charset-encodings
+ 2011-07-30T14:03:48+00:00
+ weekly
+ 0.26
+
+
+ http://glaforge.appspot.com/article/groovy-1-1-beta-1-with-annotation-support
+ 2011-07-30T14:03:48+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/groovy-net-annotations-mocks-applet-and-so-on
+ 2011-07-30T14:03:48+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/guicy-a-groovy-guice
+ 2011-07-30T14:03:49+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/new-version-of-the-groovy-eclipse-plugin
+ 2011-07-30T14:03:49+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/groovy-1-0-is-there
+ 2011-07-30T14:03:49+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/archives/p6
+ 2011-07-30T14:03:49+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/groovy-development-funding
+ 2011-07-30T14:03:50+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/groovy-and-grails-community-site-launching
+ 2011-07-30T14:03:50+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/infoq-covers-the-release-of-rc-1-and-interviews-me
+ 2011-07-30T14:03:50+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/category/Groovy/p6
+ 2011-07-30T14:03:50+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/p7
+ 2011-07-30T14:03:51+00:00
+ weekly
+ 0.21
+
+
+ http://glaforge.appspot.com/article/we-ve-just-released-groovy-rc-1
+ 2011-07-30T14:03:51+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/article/groovy-grails-jsr-223-books-conferences-and-so-on
+ 2011-07-30T14:03:51+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/article/javaday-2006-groovy-spec-lead-and-wedding
+ 2011-07-30T14:03:52+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/archives/p7
+ 2011-07-30T14:03:52+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/article/builders-in-dynamic-languages
+ 2011-07-30T14:03:52+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/article/groovy-interview-on-indicthreads
+ 2011-07-30T14:03:53+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/category/Groovy/p7
+ 2011-07-30T14:03:53+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/p8
+ 2011-07-30T14:03:53+00:00
+ weekly
+ 0.17
+
+
+ http://glaforge.appspot.com/article/antbuilder-imitation-is-the-best-form-of-flattery
+ 2011-07-30T14:03:53+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/big-thanks-for-the-second-groovy-meeting
+ 2011-07-30T14:03:54+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/archives/p8
+ 2011-07-30T14:03:54+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/re-ted-neward-on-anonymous-generic-methods
+ 2011-07-30T14:03:54+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/web-services-rpc-calls-over-google-talk
+ 2011-07-30T14:03:54+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/the-guru-of-groovy-shares-his-thoughts
+ 2011-07-30T14:03:55+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/initial-release-of-the-groovyj-intellij-plugin
+ 2011-07-30T14:03:55+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/groovy-development-aiming-for-quality
+ 2011-07-30T14:03:55+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/lucene-s-fun
+ 2011-07-30T14:03:55+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/article/maven-scripting-in-groovy
+ 2011-07-30T14:03:56+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/category/Groovy/p8
+ 2011-07-30T14:03:56+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/p9
+ 2011-07-30T14:03:56+00:00
+ weekly
+ 0.13
+
+
+ http://glaforge.appspot.com/archives/p9
+ 2011-07-30T14:03:56+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/finding-styled-paragraphs-in-a-word-document
+ 2011-07-30T14:03:57+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/re-how-groovy-can-get-her-groove-back
+ 2011-07-30T14:03:57+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/groovy-s-dead-long-live-groovy
+ 2011-07-30T14:03:57+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/groovy-code-completion-in-intellij
+ 2011-07-30T14:03:57+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/converting-a-word-document-to-html
+ 2011-07-30T14:03:57+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/a-bit-of-groovy-history
+ 2011-07-30T14:03:58+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/scripting-activex-com-components-with-groovy
+ 2011-07-30T14:03:58+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/groovy-support-in-intellij
+ 2011-07-30T14:03:58+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/article/groovy-conference-1
+ 2011-07-30T14:03:58+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/category/Groovy/p9
+ 2011-07-30T14:03:59+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/p10
+ 2011-07-30T14:03:59+00:00
+ weekly
+ 0.11
+
+
+ http://glaforge.appspot.com/archives/p10
+ 2011-07-30T14:03:59+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/article/groovy-s-birthday-and-news
+ 2011-07-30T14:04:00+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/article/new-groovy-snapshot
+ 2011-07-30T14:04:00+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/article/a-groovy-web-server
+ 2011-07-30T14:04:00+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/article/heads-up-on-file-and-stream-groovy-methods
+ 2011-07-30T14:04:01+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/article/timing-a-closure-in-groovy
+ 2011-07-30T14:04:01+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/article/groovy-jdk-doc-parsing-java-with-qdox
+ 2011-07-30T14:04:01+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/category/Groovy/p10
+ 2011-07-30T14:04:01+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/p11
+ 2011-07-30T14:04:02+00:00
+ weekly
+ 0.09
+
+
+ http://glaforge.appspot.com/archives/p11
+ 2011-07-30T14:04:02+00:00
+ weekly
+ 0.07
+
+
+ http://glaforge.appspot.com/article/groovy-logo-contest
+ 2011-07-30T14:04:02+00:00
+ weekly
+ 0.07
+
+
+ http://glaforge.appspot.com/article/groovy-a-sample-script
+ 2011-07-30T14:04:03+00:00
+ weekly
+ 0.07
+
+
+ http://glaforge.appspot.com/article/loaf-a-groovy-implementation
+ 2011-07-30T14:04:03+00:00
+ weekly
+ 0.07
+
+
+ http://glaforge.appspot.com/p12
+ 2011-07-30T14:04:03+00:00
+ weekly
+ 0.07
+
+
+ http://glaforge.appspot.com/archives/p12
+ 2011-07-30T14:04:03+00:00
+ weekly
+ 0.05
+
+
+ http://glaforge.appspot.com/p13
+ 2011-07-30T14:04:04+00:00
+ weekly
+ 0.05
+
+
+ http://glaforge.appspot.com/archives/p13
+ 2011-07-30T14:04:04+00:00
+ weekly
+ 0.04
+
+
+ http://glaforge.appspot.com/p14
+ 2011-07-30T14:04:04+00:00
+ weekly
+ 0.04
+
+
+ http://glaforge.appspot.com/archives/p14
+ 2011-07-30T14:04:05+00:00
+ weekly
+ 0.04
+
+
+ http://glaforge.appspot.com/p15
+ 2011-07-30T14:04:05+00:00
+ weekly
+ 0.04
+
+
+ http://glaforge.appspot.com/archives/p15
+ 2011-07-30T14:04:05+00:00
+ weekly
+ 0.03
+
+
+ http://glaforge.appspot.com/p16
+ 2011-07-30T14:04:06+00:00
+ weekly
+ 0.03
+
+
+ http://glaforge.appspot.com/p17
+ 2011-07-30T14:04:06+00:00
+ weekly
+ 0.02
+
+
+ http://glaforge.appspot.com/p18
+ 2011-07-30T14:04:06+00:00
+ weekly
+ 0.02
+
+
+ http://glaforge.appspot.com/p19
+ 2011-07-30T14:04:07+00:00
+ weekly
+ 0.01
+
+
+ http://glaforge.appspot.com/p20
+ 2011-07-30T14:04:07+00:00
+ weekly
+ 0.01
+
+
+ http://glaforge.appspot.com/p21
+ 2011-07-30T14:04:08+00:00
+ weekly
+ 0.01
+
+
+ http://glaforge.appspot.com/p22
+ 2011-07-30T14:04:08+00:00
+ weekly
+ 0.01
+
+
+ http://glaforge.appspot.com/p23
+ 2011-07-30T14:04:08+00:00
+ weekly
+ 0.01
+
+
+ http://glaforge.appspot.com/p24
+ 2011-07-30T14:04:09+00:00
+ weekly
+ 0.00
+
+
+ http://glaforge.appspot.com/p25
+ 2011-07-30T14:04:09+00:00
+ weekly
+ 0.00
+
+
+ http://glaforge.appspot.com/p26
+ 2011-07-30T14:04:09+00:00
+ weekly
+ 0.00
+
+
+ http://glaforge.appspot.com/p27
+ 2011-07-30T14:04:10+00:00
+ weekly
+ 0.00
+
+
+ http://glaforge.appspot.com/p28
+ 2011-07-30T14:04:10+00:00
+ weekly
+ 0.00
+
+
+ http://glaforge.appspot.com/p29
+ 2011-07-30T14:04:10+00:00
+ weekly
+ 0.00
+
+
+ http://glaforge.appspot.com/p30
+ 2011-07-30T14:04:11+00:00
+ weekly
+ 0.00
+
+
\ No newline at end of file