Showing preview only (1,534K chars total). Download the full file or copy to clipboard to get everything.
Repository: metachris/appengine-boilerplate
Branch: master
Commit: 8aefb407a38e
Files: 69
Total size: 1.5 MB
Directory structure:
gitextract_950o7lh_/
├── .gitignore
├── README.md
├── app/
│ ├── app.py
│ ├── app.yaml
│ ├── common/
│ │ ├── __init__.py
│ │ └── templateaddons.py
│ ├── cron.yaml
│ ├── handlers/
│ │ ├── __init__.py
│ │ ├── baserequesthandler.py
│ │ └── main.py
│ ├── mc/
│ │ ├── __init__.py
│ │ └── cache.py
│ ├── models.py
│ ├── queue.yaml
│ ├── services.py
│ ├── settings.py
│ ├── static_dev/
│ │ ├── .htaccess
│ │ ├── 404.html
│ │ ├── README.md
│ │ ├── build/
│ │ │ ├── build.xml
│ │ │ ├── config/
│ │ │ │ ├── default.properties
│ │ │ │ ├── manifest.appcache
│ │ │ │ └── project.properties
│ │ │ ├── createproject.sh
│ │ │ ├── runbuildscript.bat
│ │ │ └── tools/
│ │ │ ├── ant-contrib-1.0b3.jar
│ │ │ ├── csslint-rhino.js
│ │ │ ├── fulljshint.js
│ │ │ ├── fulljslint.js
│ │ │ ├── htmlcompressor-1.4.3.jar
│ │ │ ├── optipng-0.6.4-exe/
│ │ │ │ └── LICENSE.txt
│ │ │ ├── rhino.jar
│ │ │ └── yuicompressor-2.4.5.jar
│ │ ├── crossdomain.xml
│ │ ├── css/
│ │ │ ├── custom.css
│ │ │ ├── fonts.css
│ │ │ ├── libs/
│ │ │ │ └── openid.css
│ │ │ ├── normalize.css
│ │ │ └── style.css
│ │ ├── demo/
│ │ │ ├── elements.html
│ │ │ ├── hack.css
│ │ │ ├── hack2.css
│ │ │ └── tests.html
│ │ ├── humans.txt
│ │ ├── img/
│ │ │ └── .gitignore
│ │ ├── index.html
│ │ ├── js/
│ │ │ ├── libs/
│ │ │ │ ├── dd_belatedpng.js
│ │ │ │ ├── jquery-1.5.1.js
│ │ │ │ └── jquery-1.6.2.js
│ │ │ ├── mylibs/
│ │ │ │ ├── .gitignore
│ │ │ │ └── openid-en.js
│ │ │ ├── plugins.js
│ │ │ └── script.js
│ │ ├── robots.txt
│ │ └── test/
│ │ ├── index.html
│ │ ├── qunit/
│ │ │ ├── qunit.css
│ │ │ └── qunit.js
│ │ └── tests.js
│ ├── templates/
│ │ ├── account.html
│ │ ├── account_setup.html
│ │ ├── footer.html
│ │ ├── header.html
│ │ ├── index.html
│ │ └── login.html
│ └── tools/
│ ├── __init__.py
│ ├── common.py
│ ├── decorators.py
│ └── mailchimp.py
└── upload_to_appengine.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.pyc
app/static_dev/publish
app/static_dev/intermediate
================================================
FILE: README.md
================================================
App Engine Boilerplate is a versatile yet minimalistic setup for new App Engine projects.
* [html5-boilerplate 2.0](https://github.com/paulirish/html5-boilerplate)
(including it's automated build toolchain for minification and concatenation of js+css)
* Beautiful OpenID login with [openid-selector](http://code.google.com/p/openid-selector/)
* Flexible user-preferences model with auto-caching (plus Gravatar link)
* `BaseRequestHandler` for simplified rendering and access to user preferences
* `@login_required` decorator
* Memcaching setup
* Templates and template addons
* Tools such as `is_testenv()` and `slugify(url)`
* Automatically subscribe users to your MailChimp newsletter
* `app.yaml` configuration for admin areas, static files
* Released under the [BSD license](http://www.opensource.org/licenses/bsd-license.php)
This project does not contain a lot of code. To get the best understanding of it's features
we recommend to simply **browse through the files**! You can see a rather minimalistic live version [here](http://www.appengine-boilerplate.com).
OpenID Authentication
---------------------
User authentication with OpenID works out of the box, including a nice user interface via the [openid-selector] [1] jQuery plugin (also used by [stackoverflow] [2]). Be sure to enable OpenID authentication in your app settings on app engine.

More infos about appengine and openid:
* [http://code.google.com/appengine/articles/openid.html](http://code.google.com/appengine/articles/openid.html)
* [http://blog.notdot.net/2010/05/Using-OpenID-authentication-on-App-Engine](http://code.google.com/appengine/articles/openid.html)
[1]: http://code.google.com/p/openid-selector/
[2]: http://stackoverflow.com/users/login
Memcaching
----------
Returning cached data usually improves the performance of a website. App Engine provides
a custom version of memcache to store various data types, which can be used to efficiently
cache the results of datastore queries, or commonly used elements on the homepage.
[`app/mc/cache.py`](https://github.com/metachris/appengine-boilerplate/blob/master/app/mc/cache.py) contains an exemplary method of querying data from
memcache with a datastore fallback if not yet cached. Simply adapt as you need it!
HTML5-Boilerplate
-----------------
[html5 boilerplate] [1] is a great base setup for building the website frontend, and furthermore
includes a build script which minifies and compresses html, css, javascript and images.
html5-boilerplate is located in ``/static_dev``, and it's build script outputs an optimized release version to ``/static_dev/publish``.The only modification to the standard html5-boilerplate is adding a few blocks to ``/static_dev/index.html``: ``{% block header|main|scripts|footer %}``
During development the symlink ``/static`` points to ``/static_dev``. On publishing
the project ``upload_to_appengine.sh`` invokes the html5-boilerplate build script
and changes the symlink ``/static`` to ``/static_dev/publish``, in order to upload
the optimized version.
upload_to_appengine.sh
----------------------
`upload_to_appengine.sh` is a tiny shell script which simplifies invoking the html5-boilerplate build tools before testing and uploading your app to app engine. To use it you need to set ``CMD_APPCFG`` to your local `dev_appserver.py`.
Exact steps of `./upload_to_appengine.sh`:
- Asks if it should run the build process with ``ant minify``
- Changes the /static symlink to the production version
- Waits for you to test and confirm
- Uploads the app to appengine
- Reverts /static to the development environment
These would be the manual steps:
# go into html5-boilerplate's build directory
$ cd static_dev/build
# run ant, which compiles an optimized version into static_dev/publish
$ ant minify
# go back into the main directory
$ cd ../../
# change reference of /static symlink to optimized version
$ rm static
$ ln -s static_dev/publish static
# Test the optimized version
# Publish to web with appcfg.py
# After publishing you can change back to static_dev
$ rm static
$ ln -s static_dev static
[1]: https://github.com/paulirish/html5-boilerplate
Adding CSS Files
----------------
CSS files are no longer imported from `index.html` but exclusively through using
`@import` in style.css.
html5 boilerplate automatically includes, minifies and concatenates css files
which are imported via an `@import` statement from within `style.css`. Add
references to your custom stylesheets from there, never from within index.html.
Working in Windows
-------------------
There are a couple of posix symlinks within the project that will need to be updated to work with Windows.
* "/static" which points to "/static_dev" during development
* "/templates/base.html" which points to "/static/index.html"
To create the new links, delete the existing links and use the mklink command from an command prompt run with Administration privileges.
* [mklink](http://technet.microsoft.com/en-us/library/cc753194\(WS.10\).aspx) [[/D] | [/H] | [/J]] Link Target
Making these two changes will enable you to launch the project using the Google App Engine SDK Launcher. During deployment the current .sh script changes the /static link to /static_dev/publish and a forthcoming .bat script will include this functionality.
Enjoy
----------
Feedback, improvements and critique are greatly appreciated. **Fork away!**
Ideas
-----
Some ideas for future improvements:
* Check for unique username
* Email verification
* Update openid-selector
* OAuth login: Twitter, Facebook, LinkedIn, Dropbox, etc.
* About page with feedback form
* Feedback dialog
* akismet, twitter, bit.ly modules?
================================================
FILE: app/app.py
================================================
# -*- coding: utf-8 -*-
import os
from google.appengine.dist import use_library
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
use_library('django', '1.2')
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
# Load request handlers
import handlers
# Map url's to handlers
urls = [
(r'/', handlers.Main),
(r'/login', handlers.LogIn),
(r'/_ah/login_required', handlers.LogIn),
(r'/logout', handlers.LogOut),
(r'/account', handlers.Account),
(r'/account/setup', handlers.AccountSetup),
]
application = webapp.WSGIApplication(urls, debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
================================================
FILE: app/app.yaml
================================================
# Replace 'ae-boilerplate' with your application name
application: ae-boilerplate
version: 1
runtime: python
api_version: 1
default_expiration: "30d"
builtins:
- datastore_admin: on
handlers:
# Cron jobs and other secured things
- url: /services.*
script: services.py
login: admin
# If non-authenticated user, appengine will ask for login and redirect afterwards:
- url: /account
script: app.py
login: required
# Override appengine url to provide custom OpenID login page
- url: /_ah/login_required
script: app.py
# html-5 boilerplate redirects from /... to /static/...
- url: /apple-touch-icon\.png
mime_type: image/png
static_files: static/apple-touch-icon.png
upload: static/apple-touch-icon.png
- url: /favicon\.ico
mime_type: image/png
static_files: static/favicon.ico
upload: static/favicon.ico
- url: /(robots\.txt|humans\.txt|crossdomain\.xml)
static_files: static/\1
upload: static/(robots\.txt|humans\.txt|crossdomain\.xml)
- url: /img/(.*\.(gif|png|jpg))
static_files: static/img/\1
upload: static/img/(.*\.(gif|png|jpg))
- url: /swf/(.*\.swf)
static_files: static/swf/\1
upload: static/swf/(.*\.swf)
- url: /css/(.*\.css)
mime_type: text/css
static_files: static/css/\1
upload: static/css/(.*\.css)
- url: /js/(.*\.js)
mime_type: text/javascript
static_files: static/js/\1
upload: static/js/(.*\.js)
- url: /(.*\.html)
mime_type: text/html
static_files: static/\1
upload: static/(.*\.html)
# All other requests go to app.py
- url: /.*
script: app.py
================================================
FILE: app/common/__init__.py
================================================
================================================
FILE: app/common/templateaddons.py
================================================
# -*- coding: utf-8 -*-
from google.appengine.ext import webapp
from django.template import Node
"""
Custom template tags, for use from within the templates.
Before rendering a relevant template from within a handler, you need to include
the custom tags with this line of code:
webapp.template.register_template_library('common.templateaddons')
More infos about custom template tags:
- http://docs.djangoproject.com/en/dev/howto/custom-template-tags/
"""
# get registry, we need it to register our filter later.
register = webapp.template.create_template_register()
def truncate_chars(value, maxlen):
"""Truncates value and appends '...' if longer than maxlen.
Usage inside template to limit my_var to 20 characters max:
{{ my_var|truncate_chars:20 }}
"""
if len(value) < maxlen:
return value
else:
return "%s..." % value[:maxlen - 3]
register.filter(truncate_chars)
================================================
FILE: app/cron.yaml
================================================
cron:
#- description: hourly 1
# url: /services/task1
# schedule: every 1 hours synchronized
#
#- description: every minute
# url: /services/task2
# schedule: every 1 minutes synchronized
================================================
FILE: app/handlers/__init__.py
================================================
from main import *
================================================
FILE: app/handlers/baserequesthandler.py
================================================
# -*- coding: utf-8 -*-
import os
from google.appengine.api import users
from google.appengine.ext import webapp
import models
import tools.common
import settings
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__),
'../%s' % settings.TEMPLATE_DIR)
class BaseRequestHandler(webapp.RequestHandler):
"""Extension of the normal RequestHandler
- self.userprefs provides the UserPrefs object of the current user.
- self.render() provides a quick way to render templates with
common template variables already preset.
"""
def __init__(self):
super(BaseRequestHandler, self).__init__()
self.userprefs = models.UserPrefs.from_user(users.get_current_user())
def render(self, template_name, template_values={}):
# Preset values for the template
values = {
'request': self.request,
'prefs': self.userprefs,
'login_url': users.create_login_url(self.request.uri),
'logout_url': users.create_logout_url(self.request.uri),
}
# Add manually supplied template values
values.update(template_values)
# Render template
fn = os.path.join(TEMPLATE_DIR, template_name)
self.response.out.write(webapp.template.render(fn, values,
debug=tools.common.is_testenv()))
def head(self, *args):
"""Head is used by Twitter. If not there the tweet button shows 0"""
pass
================================================
FILE: app/handlers/main.py
================================================
# -*- coding: utf-8 -*-
import os
import logging
from hashlib import md5
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
# Import packages from the project
import mc
import settings
import tools.mailchimp
from models import *
from baserequesthandler import BaseRequestHandler
from tools.common import decode
from tools.decorators import login_required
# OpenID login
class LogIn(BaseRequestHandler):
"""
Redirects a user to the OpenID login site. After successful login the user
redirected to the target_url (via /login?continue=/<target_url>).
"""
def get(self):
# Wrap target url to redirect new users to the account setup step
target_url = "/account?continue=%s" % \
decode(self.request.get('continue'))
action = decode(self.request.get('action'))
if action and action == "verify":
fid = decode(self.request.get('openid_identifier'))
url = users.create_login_url(target_url, federated_identity=fid)
self.redirect(url)
else:
# BaseRequestHandler provides .render() for rendering a template
self.render("login.html", {"continue_to": target_url})
# LogOut redirects the user to the GAE logout url, and then redirects to /
class LogOut(webapp.RequestHandler):
def get(self):
url = users.create_logout_url("/")
self.redirect(url)
# Main page request handler
class Main(BaseRequestHandler):
def get(self):
# Render the template
self.render("index.html")
# Account page and after-login handler
class Account(BaseRequestHandler):
"""
The user's account and preferences. After the first login, the user is sent
to /account?continue=<target_url> in order to finish setting up the account
(email, username, newsletter).
"""
def get(self):
target_url = decode(self.request.get('continue'))
# Circumvent a bug in gae which prepends the url again
if target_url and "?continue=" in target_url:
target_url = target_url[target_url.index("?continue=") + 10:]
if not self.userprefs.is_setup:
# First log in of user. Finish setup before forwarding.
self.render("account_setup.html", {"target_url": target_url})
return
elif target_url:
# If not a new user but ?continue=<url> supplied, redirect
self.redirect(target_url)
return
# Render the account website
self.render("account.html")
class AccountSetup(BaseRequestHandler):
"""Initial setup of the account, after user logs in the first time"""
def post(self):
username = decode(self.request.get("username"))
email = decode(self.request.get("email"))
subscribe = decode(self.request.get("subscribe"))
target_url = decode(self.request.get('continue'))
target_url = target_url or "/account"
# Set a flag whether newsletter subscription setting has changed
subscription_changed = bool(self.userprefs.subscribed_to_newsletter) \
is not bool(subscribe)
# Update UserPrefs object
self.userprefs.is_setup = True
self.userprefs.nickname = username
self.userprefs.email = email
self.userprefs.email_md5 = md5(email.strip().lower()).hexdigest()
self.userprefs.subscribed_to_newsletter = bool(subscribe)
self.userprefs.put()
# Subscribe this user to the email newsletter now (if wanted). By
# default does not subscribe users to mailchimp in Test Environment!
if subscription_changed and settings.MAILCHIMP_ENABLED:
if subscribe:
tools.mailchimp.mailchimp_subscribe(email)
else:
tools.mailchimp.mailchimp_unsubscribe(email)
# After updating UserPrefs, redirect
self.redirect(target_url)
================================================
FILE: app/mc/__init__.py
================================================
import cache
================================================
FILE: app/mc/cache.py
================================================
# -*- coding: utf-8 -*-
import logging
from google.appengine.api import memcache
import models
def get_someitems(clear=False):
"""Boilerplate for your customization"""
if clear:
memcache.delete("someitems")
return
someitems = memcache.get("someitems")
if someitems:
#logging.info("return cached someitem")
return someitems
someitems = []
for someitem in Someitem.all().fetch(100):
someitems.append(someitem)
memcache.set("someitems", someitems)
logging.info("cached someitems")
return someitems
def get_userprefs(user, clear=False):
"""
Get the UserPrefs for the current user either from memcache or, if not
yet cached, from the datastore and put it into memcache. Used by
UserPrefs.from_user(user)
"""
if not user:
return user
if user.federated_identity():
key = "userprefs_fid_%s" % user.federated_identity()
else:
key = "userprefs_gid_%s" % user.user_id()
# Clearing the cache does not return anything
if clear:
memcache.delete(key)
logging.info("- cache cleared key: %s", key)
return
# Try to grab the cached UserPrefs
prefs = memcache.get(key)
if prefs:
logging.info("- returning cached userprefs for key: %s", key)
return prefs
# If not cached, query the datastore, put into cache and return object
prefs = models.UserPrefs._from_user(user)
memcache.set(key, prefs)
logging.info("cached userprefs key: %s", key)
return prefs
================================================
FILE: app/models.py
================================================
# -*- coding: utf-8 -*-
import logging
from hashlib import md5
from google.appengine.ext import db
from google.appengine.api import users
import mc
class UserPrefs(db.Model):
"""Storage for custom properties related to a user. Provides caching
for super-fast access to the UserPrefs object.
All models with user relations should reference the specific UserPrefs
model, never the GAE internal user model (due to a known GAE bug).
The UserPrefs can be retrieved/created via from_user(user):
userprefs = UserPrefs.from_user(users.get_current_user())
This retrieves the UserPrefs object is automatically from memcache or, if
not already cached, from the datastore and put into memcache. The cached
object is cleared whenever the .put() or .delete() method is called.
If users.get_current_user() is not logged in, from_user() returns None.
The BaseRequestHandler (see handlers/baserequesthandler.py and main.py)
automatically provides the current UserPref object via self.userprefs.
"""
# Base settings. Copied over from OpenID at first login (may not be valid)
nickname = db.StringProperty()
email = db.StringProperty(default="")
# The md5 has of the email is used for gravatar image urls
email_md5 = db.StringProperty(default="")
# email_verified is set after user clicked the link in verification mail
email_verified = db.BooleanProperty(default=False)
# The main reference to the Google-internal user object
federated_identity = db.StringProperty()
federated_provider = db.StringProperty()
# Google user id is only used on the dev server
google_user_id = db.StringProperty()
# Various meta information
date_joined = db.DateTimeProperty(auto_now_add=True)
date_lastlogin = db.DateTimeProperty(auto_now_add=True) # TODO
date_lastactivity = db.DateTimeProperty(auto_now_add=True) # TODO
# is_setup: set to true after setting username and email at first login
is_setup = db.BooleanProperty(default=False)
# Cursom properties
subscribed_to_newsletter = db.BooleanProperty(default=False)
@staticmethod
def from_user(user):
"""Returns the cached UserPrefs object. If not cached, get from DB and
put it into memcache."""
if not user:
return None
return mc.cache.get_userprefs(user)
@staticmethod
def _from_user(user):
"""Gets UserPrefs object from database. Used by
mc.cache.get_userprefs() if not cached."""
if user.federated_identity():
# Standard OpenID user object
q = db.GqlQuery("SELECT * FROM UserPrefs WHERE \
federated_identity = :1", user.federated_identity())
else:
# On local devserver there is only the google user object
q = db.GqlQuery("SELECT * FROM UserPrefs WHERE \
google_user_id = :1", user.user_id())
# Try to get the UserPrefs from the data store
prefs = q.get()
# If not existing, create now
if not prefs:
nick = user.nickname()
if user.email():
if not nick or "http://" in nick or "https://" in nick:
# If user has email and openid-url is nickname, replace
nick = user.email()
# Create new user preference entity
logging.info("Creating new UserPrefs for %s" % nick)
prefs = UserPrefs(nickname=nick,
email=user.email(),
email_md5=md5(user.email().strip().lower()).hexdigest(),
federated_identity=user.federated_identity(),
federated_provider=user.federated_provider(),
google_user_id=user.user_id())
# Save the newly created UserPrefs
prefs.put()
# Keep an internal reference to the Google user object (for
# clearing the cache).
prefs._user = user
# Return either found or just created user preferences
return prefs
def put(self):
"""
Overrides db.Model.put() to remove the cached object after an update.
"""
# Call the put() method of the db.Model and keep the result
key = super(UserPrefs, self).put()
# Remove previously cached object. If put() is called the first time
# (after creating the object) there would be no self._user.
if hasattr(self, "_user"):
self._clear_cache()
# Return key provided by db.Model.put()
return key
def delete(self):
"""
Overrides db.Model.delete() to remove the object from memcache.
"""
super(UserPrefs, self).delete()
self._clear_cache()
def _clear_cache(self):
"""
Removes the object from memcache. Automatically called on .put()
and .delete().
"""
mc.cache.get_userprefs(self._user, clear=True)
class YourCustomModel(db.Model):
userprefs = db.ReferenceProperty(UserPrefs)
demo_string_property = db.StringProperty()
demo_boolean_property = db.BooleanProperty(default=True)
demo_integer_property = db.IntegerProperty(default=1)
demo_datetime_property = db.DateTimeProperty(auto_now_add=True)
================================================
FILE: app/queue.yaml
================================================
# Task queue configuration
# http://code.google.com/appengine/docs/python/config/queue.html
#
queue:
#- name: fooqueue
# rate: 1/s
# bucked_size:40
# retry_parameters:
# task_retry_limit: 7
# task_age_limit: 2d
#- name: barqueue
# rate: 1/s
# retry_parameters:
# min_backoff_seconds: 10
# max_backoff_seconds: 200
# max_doublings: 0
#- name: bazqueue
# rate: 1/s
# retry_parameters:
# min_backoff_seconds: 10
# max_backoff_seconds: 200
# max_doublings: 2
================================================
FILE: app/services.py
================================================
# -*- coding: utf-8 -*-
import os
from google.appengine.dist import use_library
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
use_library('django', '1.2')
"""
Services that are accessible to admin only (eg. cron).
"""
from google.appengine.api import mail
from google.appengine.api import taskqueue
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from models import Emails
from handlers import *
class Cron1(webapp.RequestHandler):
def get(self):
"""Cron job that queries the db and forks a worker for each entry"""
emails = db.GqlQuery("SELECT * FROM emails WHERE 1")
# Start worker requests in the background
for email in emails:
taskqueue.add(url='/services/cron1-worker1/%s' % email.key())
class Cron1_Worker1(webapp.RequestHandler):
def post(self, key):
"""Worker that runs in the 'background'"""
# Get the object from the database
email = Emails.get(key)
# Construct a appengine.api.mail object
message = mail.EmailMessage()
message.sender = "Your Name <you@domain.x>"
message.to = email.to
message.subject = email.subject
# Set text and html body
message.body = email.body_text
message.html = email.body_html
# Send. Important: Sometimes emails fail to send, which will throw an
# exception and end the function there. Next round tries again.
message.send()
# Now the message was sent and we can safely delete it.
email.delete()
urls = [
(r'/services/cron1', Cron1),
(r'/services/cron1-worker1', Cron1_Worker1),
]
application = webapp.WSGIApplication(urls, debug=True)
def main():
run_wsgi_app(application)
if __name__ == "__main__":
main()
================================================
FILE: app/settings.py
================================================
#
# Update the settings as needed
#
TEMPLATE_DIR = "templates/"
# MailChimp settings to subscribe users after signup
MAILCHIMP_API_KEY = ""
# Find it on mailchimp.com via "settings" -> "list settings and unique id"
MAILCHIMP_LIST_ID = ""
# Use this switch to turn the MailChimp API calls on and off. Set to True only
# for testing and production. Set to False during development.
MAILCHIMP_ENABLED = False
================================================
FILE: app/static_dev/.htaccess
================================================
# Apache configuration file
# httpd.apache.org/docs/2.2/mod/quickreference.html
# Note .htaccess files are an overhead, this logic should be in your Apache config if possible
# httpd.apache.org/docs/2.2/howto/htaccess.html
# Techniques in here adapted from all over, including:
# Kroc Camen: camendesign.com/.htaccess
# perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/
# Sample .htaccess file of CMS MODx: modxcms.com
###
### If you run a webserver other than apache, consider:
### github.com/paulirish/html5-boilerplate-server-configs
###
# ----------------------------------------------------------------------
# Better website experience for IE users
# ----------------------------------------------------------------------
# Force the latest IE version, in various cases when it may fall back to IE7 mode
# github.com/rails/rails/commit/123eb25#commitcomment-118920
# Use ChromeFrame if it's installed for a better experience for the poor IE folk
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
BrowserMatch MSIE ie
Header set X-UA-Compatible "IE=Edge,chrome=1" env=ie
</IfModule>
</IfModule>
<IfModule mod_headers.c>
# Because X-UA-Compatible isn't sent to non-IE (to save header bytes),
# We need to inform proxies that content changes based on UA
Header append Vary User-Agent
# Cache control is set only if mod_headers is enabled, so that's unncessary to declare
</IfModule>
# ----------------------------------------------------------------------
# Cross-domain AJAX requests
# ----------------------------------------------------------------------
# Serve cross-domain ajax requests, disabled.
# enable-cors.org
# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# <IfModule mod_headers.c>
# Header set Access-Control-Allow-Origin "*"
# </IfModule>
# ----------------------------------------------------------------------
# Webfont access
# ----------------------------------------------------------------------
# allow access from all domains for webfonts
# alternatively you could only whitelist
# your subdomains like "sub.domain.com"
<FilesMatch "\.(ttf|otf|eot|woff|font.css)$">
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
</FilesMatch>
# ----------------------------------------------------------------------
# Proper MIME type for all files
# ----------------------------------------------------------------------
# audio
AddType audio/ogg oga ogg
# video
AddType video/ogg ogv
AddType video/mp4 mp4
AddType video/webm webm
# Proper svg serving. Required for svg webfonts on iPad
# twitter.com/FontSquirrel/status/14855840545
AddType image/svg+xml svg svgz
AddEncoding gzip svgz
# webfonts
AddType application/vnd.ms-fontobject eot
AddType font/truetype ttf
AddType font/opentype otf
AddType application/x-font-woff woff
# assorted types
AddType image/x-icon ico
AddType image/webp webp
AddType text/cache-manifest appcache manifest
AddType text/x-component htc
AddType application/x-chrome-extension crx
AddType application/x-xpinstall xpi
AddType application/octet-stream safariextz
# ----------------------------------------------------------------------
# Allow concatenation from within specific js and css files
# ----------------------------------------------------------------------
# e.g. Inside of script.combined.js you could have
# <!--#include file="libs/jquery-1.5.0.min.js" -->
# <!--#include file="plugins/jquery.idletimer.js" -->
# and they would be included into this single file
# this is not in use in the boilerplate as it stands. you may
# choose to name your files in this way for this advantage
# or concatenate and minify them manually.
# Disabled by default.
# <FilesMatch "\.combined\.(js|css)$">
# Options +Includes
# SetOutputFilter INCLUDES
# </FilesMatch>
# ----------------------------------------------------------------------
# gzip compression
# ----------------------------------------------------------------------
<IfModule mod_deflate.c>
# force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s,?\s(gzip|deflate)?|X{4,13}|~{4,13}|-{4,13})$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
</IfModule>
</IfModule>
# html, txt, css, js, json, xml, htc:
<IfModule filter_module>
FilterDeclare COMPRESS
FilterProvider COMPRESS DEFLATE resp=Content-Type /text/(html|css|javascript|plain|x(ml|-component))/
FilterProvider COMPRESS DEFLATE resp=Content-Type /application/(javascript|json|xml|x-javascript)/
FilterChain COMPRESS
FilterProtocol COMPRESS change=yes;byteranges=no
</IfModule>
<IfModule !mod_filter.c>
# Legacy versions of Apache
AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript
AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
</IfModule>
# webfonts and svg:
<FilesMatch "\.(ttf|otf|eot|svg)$" >
SetOutputFilter DEFLATE
</FilesMatch>
</IfModule>
# ----------------------------------------------------------------------
# Expires headers (for better cache control)
# ----------------------------------------------------------------------
# these are pretty far-future expires headers
# they assume you control versioning with cachebusting query params like
# <script src="application.js?20100608">
# additionally, consider that outdated proxies may miscache
# www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/
# if you don't use filenames to version, lower the css and js to something like
# "access plus 1 week" or so
<IfModule mod_expires.c>
ExpiresActive on
# Perhaps better to whitelist expires rules? Perhaps.
ExpiresDefault "access plus 1 month"
# cache.appcache needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5)
ExpiresByType text/cache-manifest "access plus 0 seconds"
# your document html
ExpiresByType text/html "access plus 0 seconds"
# data
ExpiresByType text/xml "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType application/json "access plus 0 seconds"
# rss feed
ExpiresByType application/rss+xml "access plus 1 hour"
# favicon (cannot be renamed)
ExpiresByType image/x-icon "access plus 1 week"
# media: images, video, audio
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpg "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# htc files (css3pie)
ExpiresByType text/x-component "access plus 1 month"
# webfonts
ExpiresByType font/truetype "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType application/x-font-woff "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
# css and javascript
ExpiresByType text/css "access plus 2 months"
ExpiresByType application/javascript "access plus 2 months"
ExpiresByType text/javascript "access plus 2 months"
<IfModule mod_headers.c>
Header append Cache-Control "public"
</IfModule>
</IfModule>
# ----------------------------------------------------------------------
# ETag removal
# ----------------------------------------------------------------------
# Since we're sending far-future expires, we don't need ETags for
# static content.
# developer.yahoo.com/performance/rules.html#etags
FileETag None
# ----------------------------------------------------------------------
# Stop screen flicker in IE on CSS rollovers
# ----------------------------------------------------------------------
# The following directives stop screen flicker in IE on CSS rollovers - in
# combination with the "ExpiresByType" rules for images (see above). If
# needed, un-comment the following rules.
# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary
# ----------------------------------------------------------------------
# Cookie setting from iframes
# ----------------------------------------------------------------------
# Allow cookies to be set from iframes (for IE only)
# If needed, uncomment and specify a path or regex in the Location directive
# <IfModule mod_headers.c>
# <Location />
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
# </Location>
# </IfModule>
# ----------------------------------------------------------------------
# Start rewrite engine
# ----------------------------------------------------------------------
# Turning on the rewrite engine is necessary for the following rules and features.
<IfModule mod_rewrite.c>
RewriteEngine On
</IfModule>
# ----------------------------------------------------------------------
# Suppress or force the "www." at the beginning of URLs
# ----------------------------------------------------------------------
# The same content should never be available under two different URLs - especially not with and
# without "www." at the beginning, since this can cause SEO problems (duplicate content).
# That's why you should choose one of the alternatives and redirect the other one.
# By default option 1 (no "www.") is activated. Remember: Shorter URLs are sexier.
# no-www.org/faq.php?q=class_b
# If you rather want to use option 2, just comment out all option 1 lines
# and uncomment option 2.
# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME!
# ----------------------------------------------------------------------
# Option 1:
# Rewrite "www.domain.com -> domain.com"
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,L]
</IfModule>
# ----------------------------------------------------------------------
# Option 2:
# To rewrite "domain.com -> www.domain.com" uncomment the following lines.
# Be aware that the following rule might not be a good idea if you
# use "real" subdomains for certain parts of your website.
# <IfModule mod_rewrite.c>
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC]
# RewriteCond %{HTTP_HOST} (.+)$ [NC]
# RewriteRule ^(.*)$ http://www.%1/$1 [R=301,L]
# </IfModule>
# ----------------------------------------------------------------------
# Add/remove trailing slash to (non-file) URLs
# ----------------------------------------------------------------------
# Google treats URLs with and without trailing slashes separately.
# Forcing a trailing slash is usually preferred, but all that's really
# important is that one correctly redirects to the other.
# By default option 1 (force trailing slash) is activated.
# http://googlewebmastercentral.blogspot.com/2010/04/to-slash-or-not-to-slash.html
# http://www.alistapart.com/articles/slashforward/
# http://httpd.apache.org/docs/2.0/misc/rewriteguide.html#url Trailing Slash Problem
# ----------------------------------------------------------------------
# Option 1:
# Rewrite "domain.com/foo -> domain.com/foo/"
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/|#(.*))$
RewriteRule ^(.*)$ /$1/ [R=301,L]
</IfModule>
# ----------------------------------------------------------------------
# Option 2:
# Rewrite "domain.com/foo/ -> domain.com/foo"
#<IfModule mod_rewrite.c>
# RewriteRule ^(.*)/$ /$1 [R=301,L]
#</IfModule>
# ----------------------------------------------------------------------
# Built-in filename-based cache busting
# ----------------------------------------------------------------------
# If you're not using the build script to manage your filename version revving,
# you might want to consider enabling this, which will route requests for
# /css/style.20110203.css to /css/style.css
# To understand why this is important and a better idea than all.css?v1231,
# read: github.com/paulirish/html5-boilerplate/wiki/Version-Control-with-Cachebusting
# Uncomment to enable.
# <IfModule mod_rewrite.c>
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteCond %{REQUEST_FILENAME} !-d
# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L]
# </IfModule>
# ----------------------------------------------------------------------
# Prevent SSL cert warnings
# ----------------------------------------------------------------------
# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent
# https://www.domain.com when your cert only allows https://secure.domain.com
# Uncomment the following lines to use this feature.
# <IfModule mod_rewrite.c>
# RewriteCond %{SERVER_PORT} !^443
# RewriteRule (.*) https://example-domain-please-change-me.com/$1 [R=301,L]
# </IfModule>
# ----------------------------------------------------------------------
# Prevent 404 errors for non-existing redirected folders
# ----------------------------------------------------------------------
# without -MultiViews, Apache will give a 404 for a rewrite if a folder of the same name does not exist
# e.g. /blog/hello : webmasterworld.com/apache/3808792.htm
Options -MultiViews
# ----------------------------------------------------------------------
# custom 404 page
# ----------------------------------------------------------------------
# You can add custom pages to handle 500 or 403 pretty easily, if you like.
ErrorDocument 404 /404.html
# ----------------------------------------------------------------------
# UTF-8 encoding
# ----------------------------------------------------------------------
# use utf-8 encoding for anything served text/plain or text/html
AddDefaultCharset utf-8
# force utf-8 for a number of file formats
AddCharset utf-8 .html .css .js .xml .json .rss
# ----------------------------------------------------------------------
# A little more security
# ----------------------------------------------------------------------
# Do we want to advertise the exact version number of Apache we're running?
# Probably not.
## This can only be enabled if used in httpd.conf - It will not work in .htaccess
# ServerTokens Prod
# "-Indexes" will have Apache block users from browsing folders without a default document
# Usually you should leave this activated, because you shouldn't allow everybody to surf through
# every folder on your server (which includes rather private places like CMS system folders).
# Options -Indexes
# Block access to "hidden" directories whose names begin with a period. This
# includes directories used by version control systems such as Subversion or Git.
<IfModule mod_rewrite.c>
RewriteRule "(^|/)\." - [F]
</IfModule>
# If your server is not already configured as such, the following directive
# should be uncommented in order to set PHP's register_globals option to OFF.
# This closes a major security hole that is abused by most XSS (cross-site
# scripting) attacks. For more information: http://php.net/register_globals
#
# IF REGISTER_GLOBALS DIRECTIVE CAUSES 500 INTERNAL SERVER ERRORS :
#
# Your server does not allow PHP directives to be set via .htaccess. In that
# case you must make this change in your php.ini file instead. If you are
# using a commercial web host, contact the administrators for assistance in
# doing this. Not all servers allow local php.ini files, and they should
# include all PHP configurations (not just this one), or you will effectively
# reset everything to PHP defaults. Consult www.php.net for more detailed
# information about setting PHP directives.
# php_flag register_globals Off
================================================
FILE: app/static_dev/404.html
================================================
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Page Not Found :(</title>
<style>
body { text-align: center;}
h1 { font-size: 50px; text-align: center }
span[frown] { transform: rotate(90deg); display:inline-block; color: #bbb; }
body { font: 20px Constantia, 'Hoefler Text', "Adobe Caslon Pro", Baskerville, Georgia, Times, serif; color: #999; text-shadow: 2px 2px 2px rgba(200, 200, 200, 0.5); }
::-moz-selection{ background:#FF5E99; color:#fff; }
::selection { background:#FF5E99; color:#fff; }
article {display:block; text-align: left; width: 500px; margin: 0 auto; }
a { color: rgb(36, 109, 56); text-decoration:none; }
a:hover { color: rgb(96, 73, 141) ; text-shadow: 2px 2px 2px rgba(36, 109, 56, 0.5); }
</style>
</head>
<body>
<article>
<h1>Not found <span frown>:(</span></h1>
<div>
<p>Sorry, but the page you were trying to view does not exist.</p>
<p>It looks like this was the result of either:</p>
<ul>
<li>a mistyped address</li>
<li>an out-of-date link</li>
</ul>
</div>
<script>
var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),
GOOG_FIXURL_SITE = location.host;
</script>
<script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
</article>
</body>
</html>
================================================
FILE: app/static_dev/README.md
================================================
# HTML5 Boilerplate [http://html5boilerplate.com](http://html5boilerplate.com)
## Changelog:
### v.2.0 : August 10th, 2011
### v2.0 HIGHLIGHTS
#### NORMALIZE.CSS
We are now using [normalize.css](http://github.com/necolas/normalize.css/) developed by Nicolas Gallagher along with Jonathan Neal instead of the traditional CSS Reset stylesheet.
normalize.css retains useful browser defaults and includes several common fixes to improve cross-browser (desktop and mobile) styling consistency.
Lots of research has gone into normalize, verifying what are the default user agent styles provided by each browser. We can very specifically change only the ones we need to instead of the bulldozer approach.
##### Why this is great news:
* Who likes being so damn redundant and declaring: em, i { font-style: italic; }
* By using normalization instead of a reset + building up default styles, we use less styles and save bytes
* Less noise in your dev tools: when debugging, you don't have to trawl through every reset selector to reach the actual style that is causing the issue.
* More details here: http://necolas.github.com/normalize.css/
#### PROMPT CHROME FRAME FOR IE6
* With the latest release of Chrome frame which does not require admin access to be installed, we felt it was a good time to prompt IE 6 users to install Chrome Frame. (Using protocol-relative url and exact version for higher expires headers)
####BUILD SCRIPT++: Faster, @import inlining, appcache generation
* If 15 seconds was too long to wait before, you'll be happy with the changes. Via a new "intermediate" folder, we cut down build time by 80% or more.
* If you use <code>@import</code>s in your CSS to author in multiple files, the build script will inline all these together. This way, you have a maintainable authoring experience, and still a highly performant production version.
* Making an app that works offline is a badge of honor. Now with a flick of a config switch, the H5BP build script can autogenerate your cache manifest file with all the right info and wire it up. It'll also keep the manifest revved as you deploy new changes.
##### ADDING RESPOND.JS
* Add respond.js as a shift to a responsive approach. Updated it to improved, comment-free version which would enable IEs to also apply styles using media queries.
#### PNGFIX & HANDHELD REMOVED
* Remove handheld.css as we do not think it was useful among the diverse feature phones
* We feel tools like imagealpha and pngquant are more useful than using stopgap fixes like belatedpng.
### detailed 2.0 changelog
#### .HTACCESS
* Disable directory browsing by default
* removed trailing slash redirects in htaccess. More: https://github.com/paulirish/html5-boilerplate/wiki/Proper-usage-of-trailing-slash-redirects #493 #515
* Updating TTF mimetype to fix Google Chrome warning
* Improved support for all versions of Apache, incl workaround for bug in mod_filter: Fixes #441. Fixes #499. Fixes #535. Closes #549. (the grouping ticket) Ref #576
* Use substring matching in gzip filter_module and re-enable gzip for some common MIME-types
* mod_deflate trigger rules modifications
* Add gzip support for XHTML, RSS, Atom
* Move font & SVG compression from FilesMatch to FilterProvider / AddOutputFilterByType
* Added m4a (Need it for IE9) and m4v (HandBrake default) MIME types.
* moved ETag removal configs closer
* added Header unset ETag In some servers setting "FileETag None" alone, is not enough. Removing header and setting it to None fixes the issue.
* Add `Options +FollowSymlinks` when `RewriteEngine` is used. Fixes #489.
* Some more security for PHP: turn off error display and turn on error logging
* Allow Blackberry to read vCards
#### BUILD SCRIPT
* CSSLint, JSLint, JSHint tools are now optionally available in the build script
* New features in build script:
* Added a files.bypass property which when set, will not compress the listed JavaScript files, but just silently passes it on to the publish folder without any change.
* Added a images.bypass with a list of image files or folders within the img directory that you do not want to be optimized. Fixes #564
* Build script is compatible with php files now. it appears. fixes #392.
* Build script now generates appcache manifest. see #652
* Test for ant version to head off problems with ant < 1.8.2
* removes concatenated css files from index.html when they are linked to with link tag. Fixes #452
* Added DOCTYPE so Eclipse and other IDE's do not complain about the lack of schema. http://stackoverflow.com/questions/363768/disable-dtd-warning-for-ant-scripts-in-eclipse
* Updated Windows optipng and jpegtran paths to include ${basedir}
* Minification affects all .css and .js files in /css and /js dirs, not just the ones explicitly included in concatenation.
* Build script: compress all images in subfolders, too.
* Added gae.js_dir and gae.css_dir so that App Engine projects can have the correct directory names swapped in their templates.
* added a second replace token statement so that "/css/style.css" gets swapped too.
* change *.png and *.jpg to **/*.png and **/*.jpg so that optimize commands reach subdirectories.
* Improved build script compatibility with Netbeans IDE. default.properties: added IDE generated files/folders to exclude from build script .gitignore: Filename case correction for Windows generated Thumb.db Fix #374
* Adding properties to project.properties so that Google App Engine builds don't have "static" prepended when swapping for minified versions.
* console.log messages are no longer commented out. use log() instead
* Much faster build process
Intermediate stages are stored in a new intermediate folder, and only
files that should be published are copied into the publish folder.
Files are not deleted at the beginning of every build, and files that
have already been processed will not be reprocessed unless the source
has changed.
* Files are revved by SHA, not incrementally at each build
Versioned files are referenced by a SHA-1 hash of the content rather
than a build number. This means that changing your HTML and rebuilding
will not cause your users to redownload the same CSS and JavaScript, and
a reverted change may cause users to use a copy that was previously
downloaded. It may be better to use only part of the hash so the HTTP
request is shorter.
* copy files last This slightly simplifies copying because we don't have to exclude PNG, JPEG, or HTML files from the copy stage. it comes preminified, and we don't need to minify it again This also updates the HTML so that the script is not missing if the unminified scripts are unavailable on the server. This commit requires a change to existing HTML files :/
* change the source htaccess rather than updating it
* update yuicompressor to 2.4.5. fixes media query minification issue.
* update htmlcompressor to 1.1 which uses the new yuicompressor for CSS.
* try not to re-optimize the same images every time
* Lots of bug fixes for edge cases and improved techniques..
#### INDEX.HTML
* Use minified jQuery by default. / jQuery updated to 1.6.2
* Add respond.js as part of shift to 'mobile first' approach.
* Updated to Modernizr 2.0 Complete, Production minified.
* Prompt IE 6 users to install Chrome Frame, update chromeframe install to 1.0.3. Move chromeframe to bottom of page after the other scripts. also reference exact version for higher expires headers. Use protocol-relative url for chrome frame URL Fixes #495
* Removing touch icon link tags and retaining only the comment.
* Encourage people to send the X-UA-Compatible HTTP header instead of leaving it in the HTML, to avoid edge case issues. Fixes #378.
* Remove the cache-busting query parameters from the HTML.
* Simplify the conditional comment containing code for IE 9+ and modern browsers
* Simpler escape for `</script>`. See http://mathiasbynens.be/notes/etago for more information.
* Encourage people to use a custom Modernizr build containing only the features they need for that particular project.
* Added maximum touch-icon support as per http://mathiasbynens.be/notes/touch-icons#sizes
* Add a link to optional <meta> tags that could be added to the <head> element: https://github.com/paulirish/html5-boilerplate/issues/482
* Standardize the use of single and double quotes as per http://h5bp.com/d/The-markup★quotes
* Added Site Speed tracking for Google Analytics
* Using Modernizr.load/yepnope for loading Google Analytics. Fixes #542
* Google Analytics now retrieved with <code>Modernizr.load()</code> for byte brevity and optimal speed
#### STYLE.CSS
* Major: Now using css normalization instead of css reset + building up default styles. Fixes #412, #500, #534. Closes #456. Links #566
* Add `'oldie'` class to conditional `<html>` classnames. Fix #522
* Add `img { max-width: 100%; }` to print styles to prevent images from getting cut off.
* Update clearfix to use 'micro' clearfix http://nicolasgallagher.com/micro-clearfix-hack/
* Add placeholder CSS MQs for mobile-first approach
* Tweaking our hot pink ::selection. It is now #fe57a1, which is Festal (adj): pertaining to or befitting a feast, festival, holiday, or gala occasion.
* Use black for links when printing, refs #147
* added vertical-align: middle to fix borders on image containers. Fixes #440
* Add `<svg>` overflow fix for IE9. Group `<img>` and `<svg>` rules in an 'embedded content' section of CSS file. Add {cursor:pointer} to <label> element.
* Switch to outline:0 for accesible focus treatment. Avoids Opera bug when combined with transitions. Also saves bytes.
* Set `{overflow:auto}` for `<button>` and `<input>` in `<table>` in IE6/7. Avoids numerous layout and whitespace issues that result from setting {overflow:visible} to fix the odd inner spacing of those form elements.
* Add `{resize: vertical}` to `<textarea>`. Only allow vertical resizing
#### MISC
* gitignore additions: textmate project folder, older CVS folders, sass_cache.
* Update HTML elements demo: reduce repetition, remove deprecated elements, add certain HTML5 elements, add more comprehensive collection of HTML5 input types, include different form markup styles, add form elements box-sizing test
* Add .gitattributes to help with consistent line endings
* Changed curly quotes to straight quotes in crossdomain.xml
#### Significant commits:
* 26a391c60d0356e2e0dcf1929381583622e1be9c Revert "Added native iOS inertia scrolling"
* ddaf66a515c09f835603f95fe723d7da691324e6 Major: Now using css normalization instead of css reset + building up default styles
* e5e057e53815ed55f4ecfaef3057bf2940c7c0b2 Change our conditional comments around the HTML tag to use a single .oldie class.
* 7f53f98ec734e6b655d7a50fd245277d388fac1e Revert "Change our conditional comments around the HTML tag to use a single .oldie class."
* 648026d780dc6b9ecad8d37d61a92b69be5fd654 Tweaking our hot pink ::selection based on a suggestion from David Murdoch and research from Adam Diehm.
* 0e1c7ba929caddec63971cccfb7de7c0d343e060 Use minified jQuery by default.
* a0ac99a4d96453e68ff4e650fca3055767ec26aa optimize build process
* bb22ca66a8619808a87c1b5438845ed44baa4d3e Remove the cache-busting query parameters from the HTML.
#### CONTRIBUTORS
[alrra](https://github.com/alrra) [Adeel Ejaz](http://adeelejaz.com/) [David Murdoch](http://www.vervestudios.co/) [Jonathan Fielding](https://github.com/jonathan-fielding) [Robert Ros](https://github.com/rros) [Rob Larsen](http://htmlcssjavascript.com/) [William Meleyal](http://meleyal.com/) [Bruno De Barros](http://terraduo.com/) [Mike Almond](http://mikealmond.com/) [Frank](https://github.com/thatcoolguy) [Joey Baker](http://byjoeybaker.com/) [Ben Word](http://benword.com/) [Mike Botsko](http://www.botsko.net/) [Carlos Rosquillas](https://github.com/disusered) [Todd H. Gardner](https://github.com/toddhgardner) [rdeknijf](https://github.com/rdeknijf) [John Attebury](https://github.com/johnattebury) [Calvin Rien](https://github.com/darktable) [Ryan Seddon](https://github.com/ryanseddon) [Dayle Rees](http://www.daylerees.com/) [Ryan Smith-Roberts](https://lab.net/) [Brian Blakely](https://github.com/brianblakely) [Steve Heffernan](http://www.steveheffernan.com) [Barney Carroll](http://barneycarroll.com/) [Osman Gormus](https://github.com/gormus) [Jason Tokoph](http://www.mozes.com/) [See Guo Lin](http://see.guol.in/) [Jeremey Hustman](http://www.ukontrol.com/) [James Williams](http://jameswilliams.be/blog) [John-Scott Atlakson](https://github.com/jsma) [stereobooster](https://github.com/stereobooster) [walker](http://walkerhamilton.com/) [François Robichet](http://www.francois.robichet.com/) [leobetosouza](http://leobetosouza.com/) [Matthew Donoughe](http://static.dyndns.org/~mdonoughe/) [Patrick Hall](http://lotsofwords.org/) [Andy Dawson](http://www.ad7six.com/) [Daniel Filho](http://danielfilho.info/blog/) [Clément](https://github.com/clemos) [Joe Morgan](https://github.com/JoeMorgan) [Han Lin Yap](http://www.zencodez.net/) [Gregg Gajic](https://github.com/gg) [Michael Cetrulo](http://www.linkedin.com/in/web2samus) [Robert Doucette](https://github.com/robbyrice) [lexadecimal.com](http://lexadecimal.com/) [Adam Diehm](http://twitter.com/atdiehm)
### v.1.0 : March 21st, 2011
#### Build Script
<ul>
<li>Files linked via <code>@import</code> will be inlined into the files they are imported to using Corey Hart's CSS Compressor.</li>
<li>Environments are definable.</li>
<li>htaccess Expires headers are upgraded to 1year, as the filenames are revved</li>
<li>Massive rewrite so you can define which HTML, CSS, and JS files to operate on in your configurable project.properties files. This allows you to let the build script operate on unique folder architectures (including non-H5BP projects).</li>
<li>Added a source directory option in the build config, so your source files can be in a different directory from the final generated files. (Useful for other CMSes/frameworks like Django.) </li>
</ul>
#### index.html
<ul>
<li>We use a <a href="http://paulirish.com/2010/the-protocol-relative-url/">protocol-relative URL</a> for the jQuery include, to prevent the mixed content warning.</li>
<li>The order of <code><meta></code> tags, <code><title></code>, and <code>charset</code> has been <a href="https://github.com/paulirish/html5-boilerplate/wiki/The-markup">documented more extensively now</a>. TL;DR: You are <a href="https://github.com/paulirish/html5-boilerplate/commit/4b67ea5cabb8c2b75faf2e255344cdffdf190464">safe to use the boilerplate's order of tags</a>.</li>
<li>We've shortened up the Google Analytics snippet.</li>
<li>Added an ARIA <code>role</code> attribute to <code>div#main</code>. This assumes your main content goes within that container.</li>
<li>IE9 doesn't get its own conditional class! Yay!</li>
</ul>
#### style.css
<ul>
<li>Added <code>.focusable</code> helper class, which extends <code>.visuallyhidden</code> to allow the element to be focusable when navigated to via the keyboard.</li>
<li>Anchor links are no longer reset. Basically our reset is effectively merged with Eric Meyer's recent CSS reset update, and the HTML5 Doctor reset.</li>
<li>An unordered list within a <code><nav></code> element will no longer have a margin.</li>
<li>All helper classes are now after primary styles to ensure correct overrides and not be burdened with resets. </li>
<li><code>.visuallyhidden</code> is no longer camelCase for consistency with other classname formats.</li>
<li>Updated the specificity of <code>.visuallyhidden</code> to make sure it overrides all other declarations. </li>
<li>Removed reset on <code><img></code> elements within table cells as they look ugly alongside multiline texts. Browsers default to baseline alignment for images, which works better than top alignment.</li>
<li>Increased margin-left on <code><ol></code>, to allow for 2-digit list numbers.</li>
<li>Added a print reset on IE's proprietary filters.</li>
<li>Print styles no longer prints hash links or JavaScript links.</li>
<li>Updated <code><sub></code>/<code><sup></code> CSS so that they're not impacted by <code>line-height</code>, so now you can do sub/superscripts without worrying.</li>
</ul>
#### Project
<ul>
<li>Added a <a href="http://humanstxt.org">humans.txt</a> so you can clarify authorship and tools used.</li>
<li>Removed YUI profiling. You probably weren't using it anyway.</li>
<li>Removed QUnit's unit tests. There is no need to ship with them, really.</li>
</ul>
#### Webserver Configs
#### .htaccess
<ul>
<li>.htaccess is far more documented now. Take a read through it!</li>
<li><a href="https://github.com/paulirish/html5-boilerplate/commit/37b5fec090d00f38de64b591bcddcb205aadf8ee">Changed mimetype of <code>.ico</code> files to <code>image/x-icon</code></a>.</li>
<li>HTML Manifest files now use <code>.appcache</code> extension instead of <code>.manifest</code>, as per <a href="http://html5.org/r/5812">http://html5.org/r/5812</a>.</li>
<li>Force deflate for accept-encoding headers mangled by turtle tappers, courtesy of <a href="http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/">Yahoo!'s research</a></li>
<li>We nerfed some of the directives in case you're on a server without <code>mod_headers</code>. (Which is totally crazy, man!)</li>
<li>Block access to <code>.git</code> and <code>.svn</code> folders.</li>
<li>Eradicating Chrome's console warning on WOFF font downloads.</li>
<li>More optimizations available if you set the <code>.htaccess</code> details up in your <code>httpd.conf</code></li>
<li><code>.htaccess</code> now caches <code>.htc</code> files</li>
<li>Moved all server configurations (except Apache's <code>.htaccess</code>) over to <a href ="https://github.com/paulirish/html5-boilerplate-server-configs">the new html5-boilerplate-server-configs repo</a>. Head over there if you're not using Apache. </li>
<li>Updated <code>.htaccess</code> and <code>mime.types</code> for <code>ogg</code> formats.</li>
<li>Fixed regression where EOT fonts had been excluded from DEFLATE compression</li>
<li>Apache version independence: Use <code>mod_filter</code> for compression, with fallback to AddOutputFilterByType directive for legacy versions</li>
<li>Added plugin/extension mime types for Safari, Chrome, Firefox</li>
</ul>
#### nginx
<ul>
<li>Cleaned up cache expires directives.</li>
<li>Now includes SVG and font formats for gzipping.</li>
<li>expires header bug fixed.</li>
</ul>
#### IIS
<ul>
<li>Added Flash video mime types to IIS server</li>
<li>Fixed some mimetype weirdness that was preventing proper caching</li>
</ul>
<ul>
<li>Also Google App Engine, Lighttpd, and NodeJS <a href="https://github.com/paulirish/html5-boilerplate-server-configs">configurations were added</a></li>
</ul>
<p>Basically a lot of great updates were made for 1.0. <a href="https://github.com/paulirish/html5-boilerplate/compare/v0.9.5...v1.0">Here are all 220 commits since last release.</a>. You may ask though, <a href="http://html5boilerplate.com/docs/#FAQs★do-i-need-to-upgrade-my-sites-to-a-new-version">do I need to upgrade existing sites</a>? Short answer: nah, you're good.</p>
#### Contributors
[Mickael Daniel](http://blog.mklog.fr/), Dave Kirk, [Jonathan Verrecchia](http://www.html5-css3.fr/), [nlogax](https://github.com/nlogax), [Rob Larsen](http://htmlcssjavascript.com/),
[David Murdoch](http://www.vervestudios.co/), [AD7six](http://www.ad7six.com/),
[Mathias Bynens](http://mathiasbynens.be/), [Michael van Laar](http://www.michael-van-laar.de/), [Mike West](http://mikewest.org/), [Mikko Tikkanen](http://www.mintusability.com/), [Velir](http://velir.com/), [Stephen Gariepy](http://garowetz.ca/)
##### Boilerplate
[Adam J. McIntyre](http://www.amodernfable.com/), [Adeel Ejaz](http://adeelejaz.com/), akolesnikov, [Alex Dunae](http://dialect.ca/), [Andrew Le](http://andrewdle.com/), [ashnur](https://github.com/ashnur), [Ben Truyman](http://bentruyman.com/), [Bruno Aguirre](http://brunoaguirre.com/), [Chris Hager](http://metachris.org/), [Corey Ward](http://blog.coreyward.net/), [Craig Barnes](https://github.com/craigbarnes), crappish, [Daniel Schildt](http://autiomaa.org/), [Dave DeSandro](https://github.com/daveatnclud), [Dustin Whittle](http://dustinwhittle.com/), grigio, [Irakli Nadareishvili](http://freshblurbs.com/), [Jaime Bueza](http://jaime.bueza.com/), [Jake Ingman](https://github.com/jingman), [James A. Rosen](http://jamesarosen.com/), [Jeremy Balch](https://github.com/balchjd), [joe bartlett](http://twitter.com/jdbartlett), [Joe Sak](http://www.joesak.com/), [John Bacon](https://github.com/johnbacon)
[Jonathan Fielding](https://github.com/jonathan-fielding), [Jonathan Neal](http://iecss.com/), [kblomqvist](https://github.com/kblomqvist), [Kenneth Nordahl](http://nordahl.me/), [Maarten Verbaarschot](https://github.com/mverbaar), [Manuel Strehl](http://www.manuel-strehl.de/), [Marcel Turi](http://marcel.turi.co/), [Martin Hintzmann](https://github.com/Hintzmann), [mikealmond](https://github.com/mikealmond)
[mikkotikkanen](http://www.mintusability.com/), [Nic Pottier](https://github.com/nicpottier), [Paul Neave](http://www.neave.com/), [Peter Beverloo](http://peter.sh/), [Rick Waldron](http://weblog.bocoup.com/), [Rob Flaherty](http://www.ravelrumba.com/), [S Anand](http://www.s-anand.net/), [Sam Sherlock](http://samsherlock.com/), [Michael Cetrulo](http://www.linkedin.com/in/web2samus), [simshaun](https://github.com/simshaun), [Sirupsen](http://sirupsen.com/), [Stephen Gariepy](http://garowetz.ca/), [timemachine3030 ](https://github.com/timemachine3030), [Vinay](http://www.artminister.com/), [Weston Ruter](http://weston.ruter.net/), [WraithKenny](http://unfocus.com/), [Yann Mainier](http://yann.mainier.com/), [Michael van Laar](http://www.michael-van-laar.de/), [Massimo Lombardo](http://unwiredbrain.com/), [Ivan Nikolić ](http://twitter.com/niksy), [Kaelig](http://kaelig.fr/), [Richard Bradshaw](http://bradshawenterprises.com/), [SammyK](http://sammyk.me/), [alrra](https://github.com/alrra), [Rizky Syazuli](http://id.linkedin.com/in/rizky), [iszak](https://github.com/Iszak), [aaron peters](https://github.com/aaronpeters), [Swaroop C H](http://www.swaroopch.com/), [Mike Połtyn](http://mike.poltyn.com/), Marco d'Itri, Mike Lamb , [BIG Folio](http://bigfolio.com/), Philip von Bargen, Meander, Daniel Harttman, rse, timwillison, ken nordahl, [Erik Dahlström](http://my.opera.com/macdev_ed), christopherjacob, [Chew Choon Keat](http://blog.choonkeat.com/), benalman, stoyan, Markus, [Vladimir Carrer](http://www.vcarrer.com/), [aristidesfl](https://github.com/aristidesfl), [Trevor Norris](http://blog.trevorjnorris.com/) [Miloš Gavrilović](http://www.arvag.net/)
#####Configs
[Dusan Hlavaty](http://sk.linkedin.com/in/dusanhlavaty), [Sean Caetano Martin](http://www.xonecas.com/), [yaph](http://www.ramiro.org/), [michaud](https://github.com/michaud), Paul Sarena, [Graham Weldon](http://grahamweldon.com/), [Ron. Adams](http://visual-assault.org/)
#####Translators
[alrra](http://twitter.com/alrra), [Anton Kovalyov](http://self.kovalyov.net/), [Milos Gavrilovic](http://www.arvag.net/), [jorge-vitrubio](https://github.com/jorge-vitrubio), Julian Wachholz, [laviperchik](https://github.com/laviperchik), [lenzcom](https://github.com/lenzcom), [Mathias Bynens](http://mathiasbynens.be/), [Mickael Daniel](http://blog.mklog.fr/), [Mike West](http://mikewest.org/), [Niels Bom](http://www.nielsbom.com/), Ricardo Tomasi, [skill83 ](https://github.com/skill83), [Sean Caetano Martin](http://www.xonecas.com/), [Yuya Saito](http://css.studiomohawk.com/), [Zee-Julien](https://github.com/Zee-Julien)
### v.0.9.5 : October 25th, 2010
Major changes:
<ul>
<li>Removed <code>-webkit-font-smoothing: antialiased;</code> it makes monospace too thin.</li>
<li>IE conditional classes have moved from the <code><body></code> tag to the <code><html></code> tag ( #44 ).</li>
<li>Dropped <code>text-rendering: <a href="http://www.aestheticallyloyal.com/public/optimize-legibility/">optimizeLegibility</a></code> as it breaks small-caps, looks odd on Linux machines, and goes invisible on WebOS.</li>
<li>Added a IE6 call for the minified <code>dd_belatedpng</code>.</li>
<li>Revised viewport declaration to allow user scaling and clear Webkit console errors ( #37 ).</li>
<li>Updated Modernizr to 1.6 </li>
<li>Added <code>web.config</code> file for Microsoft IIS</li>
<li>Beta release of the <a href="http://github.com/paulirish/html5-boilerplate/wiki/Build-script">Build Script</a> (this is HUGE)</li>
<li>New project scaffolding <a href="http://github.com/paulirish/html5-boilerplate/wiki/makep.sh">bash script</a>.</li>
</ul>
#### General
* Updated Modernizr to 1.6 (smaller and faster)
* Added web.config file for Microsoft IIS. Now forcing latest IE version and ChromeFrame, if installed.
* Added <code>favicon</code> and <code>default icon</code> for iOS.
* Updated <code>crossdomain.xml</code> wording for better security guidelines ( #124 ).
* Expires value for <code>nginx.conf</code> corrected.
* License clarified.
#### style.css
* Removed <code>-webkit-font-smoothing: antialiased</code> as it made monospace too thin.
* Updated fonts normalization to YUI 3.2.0 PR1.
* Table Header set explicitly for IE6, and table row now has <code>page-break: avoid</code> in print CSS.
* <code>text-shadow:none !important</code> set for all text in print CSS.
* Removed scrollbar from <code><textarea></code>s in IE.
* Fixed <code><textarea></code> stylings and form field treatment for validity. Added default <code>background-color</code>.
* New robust clearfix solution without IE 5.5 hack ( #45 #126 ).
* Margins for form-elements explicitly set to <code>0</code> as webkit adds 2px space around form elements' chrome.
* Dropped <code>text-rendering: optimizeLegibility</code> as it breaks <code>small-caps</code> and looks odd on Linux machines.
* Lists now have a left margin of <code>1.8em</code>. Default <code>list-style-type</code> for ordered list is <code>decimal</code>.
* Image Replacement now works with right-to-left text ( #68 ).
* Removed "Star Hack" for checkboxes in favor of <code>.ie7</code> selector.
#### index.html
* IE conditional classes have moved from the <code><body></code> tag to the <code><html></code> tag ( #44 ).
* Added a IE6 call for the minified <code>dd_belatedpng</code>.
* Google Analytics script will now work with SSL in IE6.
* Added protocol independent absolute path for cdn jquery, with improved fallback-to-local code to protect against edge case IE bug.
* Commented out handheld CSS ( #73 ).
* Mobile viewport and textsize styles adjusted per group feedback ( #37 ).
#### .htaccess
* More files are served via gzip like <code>.htc</code> ( #55 ).
* Added Expires header for content types image/gif and video/webm.
* Fixed favicon display in IE6 ( #113 ).
* Corrected mimetypes for fonts.
* Removed caching for files of type json/xml.
* Better use of <code>ifmodule</code> for more stability in different Apache environments.
[View full diff and commit history](http://github.com/paulirish/html5-boilerplate/compare/v0.9.1...v0.9.5)
#### Contributors
Shi Chuan, Rob Larsen, Ivan Nikolić, Mikko Tikkanen, Velir, Paul Neave, Weston Ruter, Jeffrey Barke, Robert Meissner, SirFunk, Philip von Bargen, Kroc Camen, Rick Waldron, Andreas Madsen, Marco d'Itri, Adeelejaz, James Rosen, Dave DeSandro, Ken Newman, Daniel Lenz, Swaroop C H, Yann Mainier, Joe Sak, Irakli, Rob Flaherty, Jeff Starr, Mike Lamb, Holek, Aaron Peters, Kaelig, Meander, Charlie Ussery, Ciney, Région Wallonne, Sirupsen, and Paul Hayes.
### v.0.9.1 : August 13th, 2010
* HTML5 Boilerplate is now in the Public Domain
* Nginx configuration added
* Font stacks (sans-serif and monospace) simplified
* Very accessible <code>a:focus</code> styles.
* Corrected IE=edge,chromeframe enabling (As a result, the base HTML [does not validate](http://bit.ly/cGSSgr))
* ServerSideIncludes disabled by default.
* Apache config bugfixes
* Conditional body tag class combined
* dd_belatedPNG updated to 0.0.8. Redundant BackgroundImageCache fix removed.
[View full diff and commit history](http://github.com/paulirish/html5-boilerplate/compare/v0.9...v0.9.1)
##### Thanks:
voodootikigod, garowetz, fearphage, christopherjacob, mathias bynens, daniel harttman, rse, chris dary, erik dahlstrom, timwillison, kenneth nordahl, riddle, elcuervo, andreas kuckartz, 3rdEden, riley willis, majic3
### v0.9 : August 10th, 2010 - Initial release
## License:
Major components:
* Modernizr: MIT/BSD license
* jQuery: MIT/GPL license
* DD_belatedPNG: MIT license
* YUI Profiling: BSD license
* HTML5Doctor CSS reset: Public Domain
* CSS Reset Reloaded: Public Domain
Everything else:
* [The Unlicense](http://unlicense.org) (aka: public domain)
## Summary:
This is a set of files that a front-end developer can use to get started on a website, with following included:
1. Cross-browser compatible (IE6? Yeah, we got that.)
2. HTML5 ready. Use the new tags with certainty.
3. Optimal caching and compression rules for Grade-A performance
4. Best practice site configuration defaults
5. Think there's too much? The HTML5 Boilerplate is delete-key friendly. :)
6. Mobile browser optimizations
7. Progressive enhancement graceful degredation ........ yeah yeah we got that
8. IE-specific classes for maximum cross-browser control
9. Want to write unit tests but lazy? A full, hooked up test suite is waiting for you.
10. Javascript profiling…in IE6 and IE7? Sure, no problem.
11. Console.log nerfing so you won't break anyone by mistake.
12. Never go wrong with your doctype or markup!
13. An optimal print stylesheet, performance optimized
14. iOS, Android, Opera Mobile-adaptable markup and CSS skeleton.
15. IE6 pngfix baked in.
16. jQuery, waiting for you
## Releases
There are two releases: a documented release (which is exactly what you see here), and a "stripped" release (with most of the descriptive comments stripped out).
Watch the [current tickets](http://github.com/paulirish/html5-boilerplate/issues) to view the areas of active development.
================================================
FILE: app/static_dev/build/build.xml
================================================
<?xml version="1.0"?>
<!DOCTYPE project>
<project name="Boilerplate Build" default="build" basedir="../"> <!-- one back since we're in build/ -->
<!-- Load in Ant-Contrib to give us access to some very useful tasks! -->
<!-- the .jar file is located in the tools directory -->
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="${basedir}/build/tools/ant-contrib-1.0b3.jar"/>
</classpath>
</taskdef>
<!-- load shell environment -->
<property environment="ENV" />
<!-- load property files -->
<property file="build/config/project.properties"/>
<property file="build/config/default.properties"/>
<!-- merge the stylesheet properties -->
<var name="stylesheet-files" value="${file.default.stylesheets}, ${file.stylesheets}"/>
<!-- merge the pages properties -->
<var name="page-files" value="${file.pages}, ${file.pages.default.include}"/>
<!-- Test for Ant Version Delete this task and all instances of overwrite='no' if you can't upgrade to 1.8.2-->
<fail message="All features of the build script require Ant version 1.8.2. Please upgrade to 1.8.2 or remove all instances of 'overwrite=no' (and this fail task) from the build script to continue">
<condition>
<not>
<contains string="${ant.version}" substring="1.8.2"/>
</not>
</condition>
</fail>
<!--
*************************************************
* BASE TARGETS *
*************************************************
-->
<target name="basics">
<if>
<equals arg1="${env}" arg2="dev"/>
<then>
<!-- Build a dev environment -->
<echo message="Building a Development Environment..."/>
<antcall target="-basics.dev"/>
</then>
<elseif>
<equals arg1="${env}" arg2="test"/>
<then>
<!-- Build a test environment -->
<echo message="Building a Test Environment..."/>
<antcall target="-basics.test"/>
</then>
</elseif>
<else>
<!-- Build a production environment -->
<echo message="Building a Production Environment..."/>
<antcall target="-basics.production"/>
</else>
</if>
</target>
<target name="text">
<if>
<equals arg1="${env}" arg2="dev"/>
<then>
<!-- Build a dev environment -->
<echo message="Building a Development Environment..."/>
<antcall target="-text.dev"/>
</then>
<elseif>
<equals arg1="${env}" arg2="test"/>
<then>
<!-- Build a test environment -->
<echo message="Building a Test Environment..."/>
<antcall target="-text.test"/>
</then>
</elseif>
<else>
<!-- Build a production environment -->
<echo message="Building a Production Environment..."/>
<antcall target="-text.production"/>
</else>
</if>
<antcall target="-imgcopy"/>
</target>
<target name="buildkit">
<if>
<equals arg1="${env}" arg2="dev"/>
<then>
<!-- Build a dev environment -->
<echo message="Building a Development Environment..."/>
<antcall target="-buildkit.dev"/>
</then>
<elseif>
<equals arg1="${env}" arg2="test"/>
<then>
<!-- Build a test environment -->
<echo message="Building a Test Environment..."/>
<antcall target="-buildkit.test"/>
</then>
</elseif>
<else>
<!-- Build a production environment -->
<echo message="Building a Production Environment..."/>
<antcall target="-buildkit.production"/>
</else>
</if>
</target>
<target name="build">
<if>
<equals arg1="${env}" arg2="dev"/>
<then>
<!-- Build a dev environment -->
<echo message="Building a Development Environment..."/>
<antcall target="-build.dev" />
</then>
<elseif>
<equals arg1="${env}" arg2="test"/>
<then>
<!-- Build a test environment -->
<echo message="Building a Test Environment..."/>
<antcall target="-build.test" />
</then>
</elseif>
<else>
<!-- Build a production environment -->
<echo message="Building a Production Environment..."/>
<antcall target="-build.production" />
</else>
</if>
</target>
<target name="minify">
<if>
<equals arg1="${env}" arg2="dev"/>
<then>
<!-- Build a dev environment -->
<echo message="Building a Development Environment..."/>
<antcall target="-minify.dev"/>
</then>
<elseif>
<equals arg1="${env}" arg2="test"/>
<then>
<!-- Build a test environment -->
<echo message="Building a Test Environment..."/>
<antcall target="-minify.test"/>
</then>
</elseif>
<else>
<!-- Build a production environment -->
<echo message="Building a Production Environment..."/>
<antcall target="-minify.production"/>
</else>
</if>
</target>
<target name="clean" depends="-clean"/>
<!-- JSLint target, run separately -->
<target name="jslint">
<apply dir="${dir.source}/${dir.js}" executable="java" parallel="false" failonerror="true">
<fileset dir="./${dir.source}/">
<include name="**/${dir.js}/*.js"/>
<exclude name="**/*.min.js"/>
<exclude name="**/${dir.js.libs}/"/>
<exclude name="**/${dir.publish}/"/>
</fileset>
<arg value="-jar" />
<arg path="./${dir.build.tools}/${tool.rhino}" />
<arg path="./${dir.build.tools}/${tool.jslint}" />
<srcfile/>
<arg value="${tool.jslint.opts}" />
</apply>
<echo>JSLint Successful</echo>
</target>
<!-- JSHint target, run separately -->
<target name="jshint">
<apply dir="${dir.source}/${dir.js}" executable="java" parallel="false" failonerror="true">
<fileset dir="./${dir.source}/">
<include name="**/${dir.js}/*.js"/>
<exclude name="**/*.min.js"/>
<exclude name="**/${dir.js.libs}/"/>
<exclude name="**/${dir.publish}/"/>
</fileset>
<arg value="-jar" />
<arg path="./${dir.build.tools}/${tool.rhino}" />
<arg path="./${dir.build.tools}/${tool.jshint}" />
<srcfile/>
<arg value="${tool.jshint.opts}" />
</apply>
<echo>JSHint Successful</echo>
</target>
<!-- CSSLint target, run separately -->
<target name="csslint">
<apply dir="${dir.source}/${dir.css}" executable="java" parallel="false" failonerror="true">
<fileset dir="./${dir.source}/">
<include name="**/${dir.css}/*.css"/>
<exclude name="**/*.min.css"/>
<exclude name="**/${dir.publish}/"/>
</fileset>
<arg value="-jar" />
<arg path="./${dir.build.tools}/${tool.rhino}" />
<arg path="./${dir.build.tools}/${tool.csslint}" />
<srcfile/>
<arg value="${tool.csslint.opts}" />
</apply>
<echo>CSSLint Successful</echo>
</target>
<!--
*************************************************
* BUILD TARGETS *
*************************************************
-->
<!-- Target: basics -->
<target name="-basics.dev"
depends="-rev,
-copy"/>
<target name="-basics.test"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-copy"/>
<target name="-basics.production"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-copy"/>
<!-- Target: text -->
<target name="-text.dev"
depends="-rev,
-copy"/>
<target name="-text.test"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlclean,
-copy"/>
<target name="-text.production"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlclean,
-copy"/>
<!-- Target: buildkit -->
<target name="-buildkit.dev"
depends="-rev,
-imagespng,
-imagesjpg,
-copy"/>
<target name="-buildkit.test"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlbuildkit,
-imagespng,
-imagesjpg,
-copy"/>
<target name="-buildkit.production"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlbuildkit,
-imagespng,
-imagesjpg,
-copy"/>
<!-- Target: build -->
<target name="-build.dev"
depends="-rev,
-imagespng,
-imagesjpg,
-copy"/>
<target name="-build.test"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlclean,
-imagespng,
-imagesjpg,
-copy"/>
<target name="-build.production"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlclean,
-imagespng,
-imagesjpg,
-copy"/>
<!-- Target: minify -->
<target name="-minify.dev"
depends="-rev,
-imagespng,
-imagesjpg,
-copy"/>
<target name="-minify.test"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlcompress,
-imagespng,
-imagesjpg,
-copy"/>
<target name="-minify.production"
depends="-rev,
-usemin,
-js.all.minify,
-js.main.concat,
-js.mylibs.concat,
-js.scripts.concat,
-css,
-manifest,
-htmlcompress,
-imagespng,
-imagesjpg,
-copy"/>
<!--
*************************************************
* FUNCTION TARGETS *
*************************************************
-->
<target name="-clean" description="(PRIVATE) Wipe the previous build (Deletes the dir.publish directory">
<!-- This is a private target -->
<echo message="Cleaning up previous build directory..."/>
<delete dir="./${dir.intermediate}/"/>
<delete dir="./${dir.publish}/"/>
</target>
<target name="-rev" description="(PRIVATE) Increase the current build number by one and set build date">
<!-- This is a private target -->
<echo message="====================================================================="/>
<echo message="Welcome to the HTML5 Boilerplate Build Script!"/>
<echo message=" "/>
<echo message="We're going to get your site all ship-shape and ready for prime time."/>
<echo message=" "/>
<echo message="This should take somewhere between 15 seconds and a few minutes,"/>
<echo message="mostly depending on how many images we're going to compress."/>
<echo message=" "/>
<echo message="Feel free to come back or stay here and follow along."/>
<echo message="====================================================================="/>
<echo message=" "/>
<echo message=" "/>
</target>
<target name="-mkdirs">
<if>
<or>
<equals arg1="${dir.publish}" arg2="."/>
<equals arg1="${dir.publish}" arg2=".."/>
<equals arg1="${dir.publish}" arg2="/"/>
<equals arg1="${dir.publish}" arg2="./"/>
<equals arg1="${dir.publish}" arg2="../"/>
</or>
<then>
<fail message="Your dir.publish folder is set to ${dir.publish} which could delete your entire site or worse. Change it in project.properties"/>
</then>
<else>
<echo message="Creating directory structure... ${dir.publish}"/>
<mkdir dir="${dir.intermediate}"/>
<copy todir="${dir.intermediate}" includeEmptyDirs="true">
<fileset dir="${dir.source}/" excludes="${file.default.exclude}, ${file.exclude}">
<type type="dir"/>
</fileset>
</copy>
<mkdir dir="${dir.publish}"/>
<copy todir="${dir.publish}" includeEmptyDirs="true">
<fileset dir="${dir.source}/" excludes="${file.default.exclude}, ${file.exclude}">
<type type="dir"/>
</fileset>
</copy>
</else>
</if>
</target>
<target name="-copy" depends="-mkdirs">
<!-- This is a private target -->
<echo message="Copying over new files..."/>
<copy todir="./${dir.publish}">
<fileset dir="${dir.source}/" excludes="${file.default.exclude}, ${file.exclude}">
<!-- exclude files that are superseded by optimized versions with different names -->
<!-- this is not strictly necessary, but it avoids putting unreferenced files into your server -->
<exclude name="${dir.js}/**/*.js"/>
<exclude name="${dir.css}/**/*.css"/>
<exclude name="${file.manifest}"/>
</fileset>
</copy>
<echo message="A copy of all non-dev files are now in: ./${dir.publish}."/>
</target>
<!-- JAVASCRIPT -->
<target name="-js.main.concat" depends="-js.all.minify" description="(PRIVATE) Concatenates the JS files in dir.js">
<echo message="Concatenating Main JS scripts..."/>
<!-- overwrite=no here means not to overwrite if the target is newer than the sources -->
<concat destfile="./${dir.intermediate}/${dir.js}/scripts-concat.js" overwrite="no">
<fileset dir="./${dir.intermediate}/">
<include name="${dir.js.main}/plugins.js"/>
<include name="${dir.js.main}/script.js"/>
</fileset>
</concat>
</target>
<target name="-js.mylibs.concat" depends="-js.all.minify" description="(PRIVATE) Concatenates the JS files in dir.js.mylibs">
<mkdir dir="./${dir.intermediate}/${dir.js.mylibs}"/>
<echo message="Concatenating JS libraries"/>
<!-- overwrite=no here means not to overwrite if the target is newer than the sources -->
<concat destfile="./${dir.intermediate}/${dir.js}/mylibs-concat.js" overwrite="no">
<fileset dir="./${dir.intermediate}/${dir.js.mylibs}/"
includes="**/*.js"
excludes="${file.js.bypass}"/>
</concat>
</target>
<target name="-js.scripts.concat" depends="-js.main.concat,-js.mylibs.concat" if="build.concat.scripts">
<echo message="Concatenating library file with main script file"/>
<!-- overwrite=no here means not to overwrite if the target is newer than the sources -->
<concat destfile="./${dir.intermediate}/${dir.js}/scripts-concat.min.js" overwrite="no">
<fileset dir="./${dir.intermediate}/${dir.js}/">
<include name="mylibs-concat.js"/>
<include name="scripts-concat.js"/>
</fileset>
</concat>
<checksum file="${dir.intermediate}/${dir.js}/scripts-concat.min.js" algorithm="sha" property="scripts.sha" />
<if>
<isset property="gae.js_dir" />
<then>
<property name="scripts.js" value="${gae.js_dir}/${scripts.sha}.js" />
</then>
<else>
<property name="scripts.js" value="${dir.js}/${scripts.sha}.js" />
</else>
</if>
<copy file="${dir.intermediate}/${dir.js}/scripts-concat.min.js" tofile="${dir.publish}/${dir.js}/${scripts.sha}.js" />
</target>
<target name="-js.all.minify" depends="-mkdirs" description="(PRIVATE) Minifies the scripts.js files created by js.scripts.concat">
<echo message="Minifying scripts"/>
<copy todir="${dir.intermediate}/">
<fileset dir="${dir.source}/" includes="${dir.js}/**/*.min.js" />
</copy>
<apply executable="java" parallel="false">
<fileset dir="${dir.source}/" >
<include name="${dir.js}/**/*.js"/>
<exclude name="${dir.js}/**/*.min.js"/>
</fileset>
<arg line="-jar"/>
<arg path="./${dir.build.tools}/${tool.yuicompressor}"/>
<srcfile/>
<arg line="--line-break"/>
<arg line="4000"/>
<arg line="-o"/>
<mapper type="glob" from="*.js" to="${basedir}/${dir.intermediate}/*.js"/>
<targetfile/>
</apply>
<!-- at this point all js files are minified with their original names -->
<copy todir="${dir.publish}/">
<fileset dir="${dir.intermediate}/">
<include name="${dir.js}/**/*.js"/>
<exclude name="${dir.js.mylibs}/**/*.js"/>
<exclude name="${dir.js}/scripts-concat.js"/>
<exclude name="${dir.js}/mylibs-concat.js"/>
<exclude name="${dir.js}/scripts-concat.min.js"/>
<exclude name="${dir.js}/plugins.js"/>
<exclude name="${dir.js}/script.js"/>
</fileset>
</copy>
<copy todir="${dir.publish}/${dir.js.mylibs}/">
<fileset dir="${dir.source}/${dir.js.mylibs}/"
includes="${file.js.bypass}"/>
</copy>
</target>
<!-- HTML -->
<target name="-usemin" depends="-js.scripts.concat,-css" description="(PRIVATE) Replaces references to non-minified scripts">
<echo message="Switching to minified js files..."/>
<!-- Changes to style.css or scripts.js mean that the html must be updated, and it will be.
Unfortunately, the html we want to update may not have the tags we want to replace(because it was updated before).
This outofdate check ensures that the html files have the markers for us to replace. -->
<outofdate property="needhtmlrefresh">
<sourcefiles>
<fileset dir="${dir.publish}" includes="${style.css}, ${scripts.js}"/>
</sourcefiles>
<targetfiles>
<fileset dir="${dir.intermediate}" includes="${page-files}"/>
</targetfiles>
</outofdate>
<!-- force the files to be overwritten with older copies from source if needhtmlrefresh is set -->
<copy todir="${dir.intermediate}" overwrite="${needhtmlrefresh}">
<fileset dir="${dir.source}" includes="${page-files}"/>
</copy>
<!-- switch from a regular jquery to minified -->
<replaceregexp match="jquery-(\d|\d(\.\d)+)\.js" replace="jquery-\1.min.js" flags="g">
<fileset dir="./${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
<!-- switch any google CDN reference to minified -->
<replaceregexp match="(\d|\d(\.\d)+)\/jquery\.js" replace="\1/jquery.min.js" flags="g">
<fileset dir="./${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
<echo>Kill off those versioning flags: ?v=2</echo>
<replaceregexp match='\?v=\d+">' replace='">' flags="g">
<fileset dir="./${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
<echo>Remove favicon.ico reference if it is pointing to the root</echo>
<replaceregexp match="<link rel=["']shortcut icon["'] href=["']/favicon\.ico["']>" replace="">
<fileset dir="${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
<!-- we maintain the apple-touch-icon reference for Android 2.2 www.ravelrumba.com/blog/android-apple-touch-icon
<replace token="<link rel="apple-touch-icon" href="/apple-touch-icon.png">" value="">
<fileset dir="${dir.intermediate}" includes="${page-files}"/>
</replace>
-->
<echo message="Update the HTML to reference our concatenated script file: ${scripts.js}"/>
<!-- style.css replacement handled as a replacetoken above -->
<replaceregexp match="<!-- scripts concatenated [\d\w\s\W]*?!-- end ((scripts)|(concatenated and minified scripts))-->" replace="<script defer src='/${scripts.js}\'></script>" flags="m">
<fileset dir="${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
<!--[! use comments like this one to avoid having them get minified -->
<echo message="Updating the HTML with the new css filename: ${style.css}"/>
<replaceregexp match="<!-- CSS concatenated [\d\w\s\W]*?!-- end CSS-->" replace="<link rel='stylesheet' href='/${style.css}'>" flags="m">
<fileset dir="${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
</target>
<target name="-manifest" depends="-usemin">
<if>
<isset property="file.manifest" />
<then>
<echo message="copying a fresh ${dir.build}/config/${file.manifest} to /${dir.intermediate}"/>
<delete file="${dir.intermediate}/${file.manifest}"/>
<copy file="${dir.build}/config/${file.manifest}" tofile="${dir.intermediate}/${file.manifest}" />
<echo message="manifest creation" />
<!-- update version -->
<echo message="Updating the site.manifest version date to today, current time"/>
<tstamp>
<format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss"/>
</tstamp>
<replaceregexp match="# version .+" replace="# version ${TODAY}" file="${dir.intermediate}/${file.manifest}"/>
<!-- add html files -->
<echo message="Updating the site.manifest with html files: ${page-files}"/>
<for list="${page-files}" param="file" delimiter="," trim="true">
<sequential>
<replaceregexp match="# html files" replace="# html files${line.separator}@{file}" file="${dir.intermediate}/${file.manifest}" />
</sequential>
</for>
<!-- add stylesheet files -->
<echo message="Updating the site.manifest with the new css filename: ${style.css}"/>
<replace token="# css files" value="# css files${line.separator}${style.css}" file="${dir.intermediate}/${file.manifest}" />
<!-- add javascript files -->
<echo message="Updating the site.manifest with the new js filename: ${scripts.js}"/>
<for param="file">
<path>
<fileset dir="./${dir.intermediate}/${dir.js.mylibs}/"
includes="${file.js.bypass}" />
</path>
<sequential>
<basename property="filename.@{file}" file="@{file}" />
<replaceregexp match="# js files" replace="# js files${line.separator}${dir.js.mylibs}/${filename.@{file}}" file="${dir.intermediate}/${file.manifest}" />
</sequential>
</for>
<for param="file">
<path>
<fileset dir="./${dir.intermediate}/${dir.js.libs}/"
includes="*.min.js"/>
</path>
<sequential>
<basename property="filename.@{file}" file="@{file}" />
<replaceregexp match="# js files" replace="# js files${line.separator}${dir.js.libs}/${filename.@{file}}" file="${dir.intermediate}/${file.manifest}" />
</sequential>
</for>
<replace token="# js files" value="# js files${line.separator}${scripts.js}" file="${dir.intermediate}/${file.manifest}" />
<echo message="copying ${file.manifest} to /${dir.publish}"/>
<copy file="${dir.intermediate}/${file.manifest}" tofile="${dir.publish}/${file.manifest}" />
<echo>Add manifest attribute to HTML: </echo>
<replaceregexp match="<html (.*?)>\s*?<!--<!\[endif" replace='<html \1 manifest="${file.manifest}"> <!--<![endif' flags="g">
<fileset dir="./${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
</then>
<else>
<echo message="no manifest.appcache generated!" />
</else>
</if>
</target>
<target name="-htmlclean" depends="-usemin">
<echo message="Run htmlcompressor on the HTML"/>
<echo message=" - maintaining whitespace"/>
<echo message=" - removing html comments"/>
<echo message=" - compressing inline style/script tag contents"/>
<apply executable="java" parallel="false" dest="./${dir.publish}/" >
<fileset dir="./${dir.intermediate}/" includes="${page-files}"/>
<arg value="-jar"/>
<arg path="./${dir.build.tools}/${tool.htmlcompressor}"/>
<arg line="--preserve-multi-spaces"/>
<arg line="--remove-quotes"/>
<arg line="--compress-js"/>
<arg line="--compress-css"/>
<arg line="--preserve-php"/>
<arg line="--preserve-ssi"/>
<srcfile/>
<arg value="-o"/>
<mapper type="glob" from="*" to="../${dir.publish}/*"/>
<targetfile/>
</apply>
</target>
<target name="-htmlbuildkit" depends="-usemin">
<echo message="Run htmlcompressor on the HTML"/>
<echo message=" - maintaining whitespace"/>
<echo message=" - retain html comments"/>
<echo message=" - compressing inline style/script tag contents"/>
<apply executable="java" parallel="false" dest="./${dir.publish}/" >
<fileset dir="./${dir.intermediate}/" includes="${page-files}"/>
<arg value="-jar"/>
<arg path="./${dir.build.tools}/${tool.htmlcompressor}"/>
<arg value="--preserve-comments"/>
<arg line="--preserve-multi-spaces"/>
<arg line="--compress-js"/>
<arg line="--compress-css"/>
<arg line="--preserve-php"/>
<arg line="--preserve-ssi"/>
<srcfile/>
<arg value="-o"/>
<mapper type="glob" from="*" to="../${dir.publish}/*"/>
<targetfile/>
</apply>
</target>
<target name="-htmlcompress" depends="-usemin">
<echo message="Run htmlcompressor on the HTML"/>
<echo message=" - removing unnecessary whitespace"/>
<echo message=" - removing html comments"/>
<echo message=" - compressing inline style/script tag contents"/>
<apply executable="java" parallel="false" dest="./${dir.publish}/" >
<fileset dir="./${dir.intermediate}/" includes="${page-files}"/>
<arg value="-jar"/>
<arg path="./${dir.build.tools}/${tool.htmlcompressor}"/>
<arg line="--remove-quotes"/>
<arg line="--compress-js"/>
<arg line="--compress-css"/>
<arg line="--preserve-php"/>
<arg line="--preserve-ssi"/>
<srcfile/>
<arg value="-o"/>
<mapper type="glob" from="*" to="../${dir.publish}/*"/>
<targetfile/>
</apply>
</target>
<!-- CSS -->
<target name="-css-remove-concatenated-stylesheets">
<echo>Removing ${css_file} from html</echo>
<replaceregexp match="<link.+href=".*${css_file}".*>" replace=" ">
<fileset dir="${dir.intermediate}" includes="${page-files}"/>
</replaceregexp>
</target>
<target name="css-split" description="turns style.css into multiple files @imported together">
<copy file="${dir.source}/${dir.css}/${file.root.stylesheet}" tofile="${dir.source}/${dir.css}/${file.root.stylesheet}.temp"/>
<replaceregexp file="${dir.source}/${dir.css}/${file.root.stylesheet}.temp"
match=".*"
replace="/* remove me */" flags="s" />
<loadfile property="root" srcfile="${dir.source}/${dir.css}/${file.root.stylesheet}"/>
<var name="curr.buffer" value=""/>
<for delimiter="${line.separator}" param="currline" list="${root}">
<sequential>
<!-- does this line contain an h5bp-import? -->
<propertyregex property="export.name" input="@{currline}" regexp="^.*==\|== +(.*) +==+$" select="\1" casesensitive="true" override="true" />
<if>
<isset property="export.name"/>
<then>
<propertyregex property="export.name" input="${export.name}" regexp=" " replace="." global="true" override="true" />
<var name="export.name" value="${export.name}.css"/>
<if>
<isset property="curr.file"/>
<then>
<!-- create curr.file -->
<copy file="${dir.source}/${dir.css}/${file.root.stylesheet}" tofile="${dir.source}/${dir.css}/${curr.file}" overwrite="true"/>
<!-- write the curr.buffer into the curr.file -->
<replaceregexp file="${dir.source}/${dir.css}/${curr.file}"
match=".*"
replace="${curr.buffer}" flags="s" />
<var name="curr.buffer" value=""/>
<var name="curr.file" unset="true"/>
</then>
</if>
<var name="curr.file" value="${export.name}"/>
<var name="export.name" unset="true"/>
<!-- insert import line into new root -->
<replace file="${dir.source}/${dir.css}/${file.root.stylesheet}.temp" token="/* remove me */" value="@import url(${curr.file});${line.separator}/* remove me */"/>
</then>
</if>
<var name="curr.buffer" value="${curr.buffer}@{currline}${line.separator}" />
</sequential>
</for>
<!-- one more time to write out the last file -->
<if>
<isset property="curr.file"/>
<then>
<!-- create curr.file -->
<copy file="${dir.source}/${dir.css}/${file.root.stylesheet}" tofile="${dir.source}/${dir.css}/${curr.file}" overwrite="true"/>
<!-- write the curr.buffer into the curr.file -->
<replaceregexp file="${dir.source}/${dir.css}/${curr.file}"
match=".*"
replace="${curr.buffer}" flags="s" />
<var name="curr.buffer" value=""/>
<var name="curr.file" unset="true"/>
</then>
</if>
<replace file="${dir.source}/${dir.css}/${file.root.stylesheet}.temp" token="/* remove me */" value="${curr.buffer}"/>
<copy file="${dir.source}/${dir.css}/${file.root.stylesheet}" tofile="${dir.source}/${dir.css}/${file.root.stylesheet}.orig" overwrite="false"/>
<move file="${dir.source}/${dir.css}/${file.root.stylesheet}.temp" tofile="${dir.source}/${dir.css}/${file.root.stylesheet}" overwrite="false"/>
</target>
<target name="-css" depends="-mkdirs" description="Concatenates and Minifies any stylesheets listed in the file.stylesheets property">
<echo message="Concatenating any @imports..."/>
<!-- copy source file to intermediate directory -->
<copy file="${dir.source}/${dir.css}/${file.root.stylesheet}" tofile="${dir.intermediate}/${dir.css}/${file.root.stylesheet}"/>
<!-- replace imports with h5bp-import tags (part 1) this one wraps @media types -->
<replaceregexp file="${dir.intermediate}/${dir.css}/${file.root.stylesheet}"
match="^@import\s+(?:url\s*\(\s*['"]?|['"])((?!http:|https:|ftp:)[^"^'^\s]+)(?:['"]?\s*\)|['"])\s*([\w\s,\-]*);.*$"
replace="@media \2{ /* h5bp-import: \1 */ }" byline="true" />
<!-- replace imports with h5bp-import tags (part 2) -->
<replaceregexp file="${dir.intermediate}/${dir.css}/${file.root.stylesheet}"
match="^@media \{ (/\* .* \*/) \}" replace="\1" byline="true" />
<!-- copy skeleton to concat file -->
<copy file="${dir.intermediate}/${dir.css}/${file.root.stylesheet}"
tofile="${dir.intermediate}/${dir.css}/style-concat.css" overwrite="true"/>
<!-- load the file into a property -->
<loadfile property="imports" srcfile="${dir.intermediate}/${dir.css}/${file.root.stylesheet}"/>
<var name="concat-files" value="${file.root.stylesheet}"/>
<!-- go over the file line by line -->
<for delimiter="${line.separator}" param="import" list="${imports}">
<sequential>
<!-- does this line contain an h5bp-import? -->
<propertyregex property="file.name" input="@{import}" regexp="/\* h5bp-import: (.*) \*/" select="\1" casesensitive="true" override="true" />
<if>
<isset property="file.name"/>
<then>
<var name="concat-files" value="${file.name},${concat-files}"/>
<!-- load the file into a variable -->
<loadfile property="file.contents" srcFile="${dir.source}/${dir.css}/${file.name}"/>
<!-- pop that file into the concatenated output file -->
<replace file="${dir.intermediate}/${dir.css}/style-concat.css" token="/* h5bp-import: ${file.name} */" value="${file.contents}"/>
<var name="file.contents" unset="true"/>
</then>
</if>
</sequential>
</for>
<echo message="Minifying css..."/>
<apply executable="java" parallel="false">
<fileset dir="${dir.intermediate}/${dir.css}/" includes="style-concat.css"/>
<arg line="-jar"/>
<arg path="${dir.build.tools}/${tool.yuicompressor}"/>
<srcfile/>
<arg line="-o"/>
<mapper type="merge" to="${basedir}/${dir.intermediate}/${dir.css}/style-concat.min.css"/>
<targetfile/>
</apply>
<checksum file="${dir.intermediate}/${dir.css}/style-concat.min.css" algorithm="sha" property="css.sha" />
<if>
<isset property="gae.css_dir" />
<then>
<property name="style.css" value="${gae.css_dir}/${css.sha}.css" />
</then>
<else>
<property name="style.css" value="${dir.css}/${css.sha}.css" />
</else>
</if>
<copy file="${dir.intermediate}/${dir.css}/style-concat.min.css" tofile="${dir.publish}/${dir.css}/${css.sha}.css" />
<echo message="Minifying any unconcatenated css files..."/>
<apply executable="java" parallel="false">
<fileset dir="${dir.source}/${dir.css}/" excludes="${concat-files}" includes="**/*.css"/>
<arg line="-jar"/>
<arg path="${dir.build.tools}/${tool.yuicompressor}"/>
<srcfile/>
<arg line="-o"/>
<mapper type="glob" from="*.css" to="${basedir}/${dir.publish}/${dir.css}/*.css"/>
<targetfile/>
</apply>
<foreach list="${file.stylesheets}" param="css_file" target="-css-remove-concatenated-stylesheets" />
</target>
<!-- IMAGES -->
<target name="-imagespng" depends="-mkdirs" description="(PRIVATE) Optimizes .png images using optipng">
<echo message="Optimizing images..."/>
<echo message="This part might take a while. But everything else is already done."/>
<echo message=" "/>
<echo message="First, we run optipng on the .png files..."/>
<!-- osfamily=unix is actually true on OS X as well -->
<!-- On *nix's and OS X, check for optipng and give a helpful message if it's not installed -->
<if>
<and>
<os family="unix" />
<available file="optipng" filepath="${ENV.PATH}" />
</and>
<then>
<!-- work around https://sourceforge.net/tracker/?func=detail&aid=2671422&group_id=151404&atid=780916 -->
<delete>
<fileset dir="./${dir.publish}/${dir.images}/">
<include name="**/*.png"/>
</fileset>
</delete>
<apply executable="optipng" dest="./${dir.publish}/${dir.images}/" osfamily="unix">
<fileset dir="./${dir.source}/${dir.images}/" includes="**/*.png" excludes="${images.bypass}, ${images.default.bypass}"/>
<arg value="-quiet"/>
<arg value="-o7"/>
<arg value="-out"/>
<targetfile/>
<srcfile/>
<mapper type="identity"/>
</apply>
</then>
<elseif>
<os family="unix" />
<then>
<echo message="*** optipng NOT INSTALLED. SKIPPING OPTIMIZATION OF PNGs." />
<echo message="*** Install optipng to enable png optimization." />
<echo message="*** For instructions see 'Dependencies' at: http://html5boilerplate.com/docs/#Build-script#dependencies" />
</then>
</elseif>
</if>
<!-- work around https://sourceforge.net/tracker/?func=detail&aid=2671422&group_id=151404&atid=780916 -->
<delete>
<fileset dir="./${dir.publish}/${dir.images}/">
<include name="**/*.png"/>
</fileset>
</delete>
<apply executable="${basedir}/${dir.build.tools}/optipng-0.6.4-exe/optipng.exe" dest="./${dir.publish}/${dir.images}/" osfamily="windows">
<fileset dir="./${dir.source}/${dir.images}/" includes="**/*.png" excludes="${images.bypass}, ${images.default.bypass}"/>
<arg value="-quiet"/>
<arg value="-o7"/>
<arg value="-out"/>
<targetfile/>
<srcfile/>
<mapper type="identity"/>
</apply>
</target>
<target name="-imagesjpg" depends="-mkdirs" description="(PRIVATE) Optimizes .jpg images using jpegtan">
<echo message="Now, we clean up those jpgs..."/>
<if>
<equals arg1="${images.strip.metadata}" arg2="true"/>
<then>
<var name="strip-meta-tags" value="none"/>
</then>
<else>
<var name="strip-meta-tags" value="all"/>
</else>
</if>
<!-- On *nix's and OS X, check for jpegtran and give a helpful message if it's not installed -->
<if>
<and>
<os family="unix" />
<available file="jpegtran" filepath="${ENV.PATH}" />
</and>
<then>
<apply executable="jpegtran" dest="./${dir.publish}/${dir.images}" osfamily="unix">
<fileset dir="${dir.source}/${dir.images}" includes="**/*.jpg" excludes="${images.bypass}, ${images.default.bypass}"/>
<arg value="-copy"/>
<arg value="${strip-meta-tags}"/>
<arg value="-optimize"/>
<arg value="-outfile"/>
<targetfile/>
<srcfile/>
<mapper type="identity"/>
<!-- you may want to flag optimized images. If so, do it here. Otherwise change this to type="identity" -->
<!--<mapper type="glob" from="*.jpg" to="*.jpg"/>-->
</apply>
</then>
<elseif>
<os family="unix" />
<then>
<echo message="*** jpegtran NOT INSTALLED. SKIPPING OPTIMIZATION OF JPEGs." />
<echo message="*** Install jpegtran to enable jpeg optimization." />
<echo message="*** For instructions see 'Dependencies' at: http://html5boilerplate.com/docs/#Build-script#dependencies" />
</then>
</elseif>
</if>
<apply executable="${basedir}/${dir.build.tools}/jpegtran.exe" dest="./${dir.publish}/${dir.images}" osfamily="windows">
<fileset dir="${dir.source}/${dir.images}" includes="**/*.jpg" excludes="${images.bypass}, ${images.default.bypass}"/>
<arg value="-copy"/>
<arg value="${strip-meta-tags}"/>
<arg value="-optimize"/>
<arg value="-outfile"/>
<targetfile/>
<srcfile/>
<mapper type="identity"/>
<!-- you may want to flag optimized images. If so, do it here. Otherwise change this to type="identity" -->
<!--<mapper type="glob" from="*.jpg" to="*.jpg"/>-->
</apply>
</target>
<target name="-imgcopy" depends="-mkdirs">
<echo message="Copying over the unmodified images."/>
<copy todir="./${dir.publish}/${dir.images}">
<fileset dir="${dir.source}/${dir.images}" includes="**/*.jpg, **/*.png"/>
</copy>
</target>
</project>
================================================
FILE: app/static_dev/build/config/default.properties
================================================
#
# Default Build Settings
# you can override these settings on a project basis in a project.properties file
# so probably best not to touch these as they could be overwritten in later versions!
#
#
# Directory Paths
#
dir.source = .
dir.intermediate = intermediate
dir.publish = publish
dir.build = build
dir.build.tools = ${dir.build}/tools
dir.test = test
dir.demo = demo
dir.js = js
dir.js.main = ${dir.js}
# scripts in the lib directory will only be minified, not concatenated together
dir.js.libs = ${dir.js}/libs
dir.js.mylibs = ${dir.js}/mylibs
dir.css = css
dir.images = img
#
# HTML, PHP, etc files to clean and update script/css references
#
file.pages.default.include = index.html, 404.html
# You will need to include the property file.pages.include in your project.properties file
# and add any extra pages you want to be updated by the scripts in a comma separated list
# the server configuration you're going with. If you don't use apache,
# get a different one here: github.com/paulirish/html5-boilerplate-server-configs
file.serverconfig = .htaccess
#
# Files not to be copied over by the script to the publish directory
#
file.default.exclude = .gitignore, .project, .settings, README.markdown, README.md, **/.git/**, **/.svn/**, ${dir.test}/**, ${dir.demo}/**, ${dir.intermediate}/**, ${dir.publish}/**, ${dir.build}/**, **/nbproject/**, *.komodoproject, **/.komodotools/**, **/dwsync.xml, **_notes, **/.hg/**, **/.idea/**
# Declare the file.exclude property in your project.properties file if you want to exclude files / folders you have added
# Note: you cannot declare an empty file.exclude property
#
# Bypass Optimization for these files
#
# file.default.js.bypass
# If set, these files will not be optimized (minifications, concatinations, image optimizations will not be applied)
# Note: you cannot declare an empty file.default.bypass property
#
# Root Stylesheet
# this is the file that contains the @import directives
#
file.root.stylesheet = style.css
#
# Default Stylesheet
#
file.default.stylesheets =
#
# Script Optimisation
#
# If set, concat libraries with main scripts file, producing single script file
build.concat.scripts = true
#
# Image Optimisation
#
images.strip.metadata = true
# Seting this to true will strip the metadata from all jpeg files.
# YOU SHOULD ONLY DO THIS IF YOU OWN THE COPYRIGHT TO ALL THE IMAGES IN THE BUILD
#
# Bypass Optimization for these image files or folders
#
# images.default.bypass
# If set, these images will not be optimized
# Note: you cannot declare an empty images.default.bypass property
# Build Info
build.version.info = buildinfo.properties
build.scripts.dir = ${dir.build}/build-scripts
# Tools
tool.yuicompressor = yuicompressor-2.4.5.jar
tool.htmlcompressor = htmlcompressor-1.4.3.jar
tool.csscompressor = css-compressor/cli.php
tool.rhino = rhino.jar
tool.jslint = fulljslint.js
tool.jshint = fulljshint.js
tool.csslint = csslint-rhino.js
# Default Lint Utils Options
tool.jshint.opts = maxerr=25,eqeqeq=true
tool.jslint.opts = maxerr=25,evil=true,browser=true,eqeqeq=true,immed=true,newcap=true,nomen=true,es5=true,rhino=true,undef=true,white=false,devel=true
tool.csslint.opts =
================================================
FILE: app/static_dev/build/config/manifest.appcache
================================================
CACHE MANIFEST
# version xxxxxxxxx
CACHE:
# html files
# css files
# js files
FALLBACK:
NETWORK:
*
================================================
FILE: app/static_dev/build/config/project.properties
================================================
# project.properties file defines overrides for default.properties
# Explanation: This file should be created by each user as and when he or she needs to override particular values.
# Consequently, it should not be placed under version control.
# Stylesheets
#
# Note: Stylesheets will be concatenated in the order they are listed in the file.stylesheets property (i.e. the last
# file listed will be at the end of the concatenated file), so it probably makes sense to have the main style.css file
# as the first entry
# Example:
# file.stylesheets = style.css, lightbox.css, plugin.css
#
file.stylesheets =
# Web Pages
#
# These are the pages (files) that will be served to users (.html, .php, .asp, etc). Files in this property will
# be minified / optimised and have any stylesheet or javascript references updated to the minified examples
#
# The paths need to be relative
#
# Files can be added in a comma separated form
file.pages =
# site manifest for offline
# this is the name of the manifest file you declared in the <html> tag
# Uncomment this line to enable appcache generation:
# file.manifest = manifest.appcache
# Excluded files and dirs
#
# Add any files or directories you add to the project and do not want to be copied to the publish directory as a
# comma separated list
# These files are ignored in addition to the default ones specified in default.properties.
file.exclude =
# Bypassed JavaScript files and dirs
#
# Add any files or folders within the mylibs directory that you want to be copied to the publish directory as a
# comma separated list
# These files will not be concatenated or minimized and will simply be copied over as is.
# Note: you cannot declare an empty file.bypass property, it would exclude the entire mylibs folder
# Example:
# file.js.bypass = widgets.js, gadgets.js, gidgets.js
# file.js.bypass =
# Specify an environment to build
#
# By Default, it builds a production environment
# Set to dev if building a development environment
# Set to test if building a test environment
env =
#
# Bypass Optimization for these image files or folders
#
# images.bypass
# If set, these images will not be optimized
# Note: you cannot declare an empty images.bypass property, it would exclude the entire img folder from being optimized
# Directory Structure
#
# Override any directory paths specific to this project
#
# dir.publish
# dir.js
# dir.js.libs
# dir.js.mylibs
# dir.css
# dir.images
# Google App Engine Directory Structure
#
# Prevent "static/" being included in concated file paths.
#
# gae.css_dir = /css
# gae.js_dir = /js
# Override default JSHint Options (see http://jshint.com/ for description of options)
#tool.jshint.opts =
# Override default JSLint Options (see http://www.jslint.com/lint.html for description of options)
#tool.jslint.opts =
# Override default CSSLint Options (see http://csslint.net/about.html#settings for description of options)
#tool.csslint.opts =
================================================
FILE: app/static_dev/build/createproject.sh
================================================
#!/usr/bin/env bash
#Generate a new project from your HTML5 Boilerplate repo clone
#by: Rick Waldron & Michael Cetrulo
##first run
# $ cd html5-boilerplate/build
# $ chmod +x createproject.sh && ./createproject.sh
##usage
# $ cd html5-boilerplate/build
# $ ./createproject.sh
# find project root (also ensure script is ran from within repo)
src=$(git rev-parse --show-toplevel) || {
echo "try running the script from within html5-boilerplate directories." >&2
exit 1
}
[[ -d $src ]] || {
echo "fatal: could not determine html5-boilerplate's root directory." >&2
echo "try updating git." >&2
exit 1
}
# get a name for new project
while [[ -z $name ]]
do
echo "To create a new html5-boilerplate project, enter a new directory name:"
read name || exit
done
dst=$src/../$name
if [[ -d $dst ]]
then
echo "$dst exists"
else
#create new project
mkdir -- "$dst" || exit 1
#sucess message
echo "Created Directory: $dst"
cd -- "$src"
cp -vr -- css js img build test *.html *.xml *.txt *.png *.ico .htaccess "$dst"
#sucess message
echo "Created Project: $dst"
fi
================================================
FILE: app/static_dev/build/runbuildscript.bat
================================================
# This is for windows users only.
# If you're on a mac or linux, just run `ant build` from this folder in Terminal
set MYDIR=%~dp0
ant build
================================================
FILE: app/static_dev/build/tools/csslint-rhino.js
================================================
/*!
CSSLint
Copyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* Build time: 5-July-2011 03:16:53 */
var CSSLint = (function(){
/*!
Parser-Lib
Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* Build time: 5-July-2011 03:12:40 */
var parserlib = {};
(function(){
/**
* A generic base to inherit from for any object
* that needs event handling.
* @class EventTarget
* @constructor
*/
function EventTarget(){
/**
* The array of listeners for various events.
* @type Object
* @property _listeners
* @private
*/
this._listeners = {};
}
EventTarget.prototype = {
//restore constructor
constructor: EventTarget,
/**
* Adds a listener for a given event type.
* @param {String} type The type of event to add a listener for.
* @param {Function} listener The function to call when the event occurs.
* @return {void}
* @method addListener
*/
addListener: function(type, listener){
if (!this._listeners[type]){
this._listeners[type] = [];
}
this._listeners[type].push(listener);
},
/**
* Fires an event based on the passed-in object.
* @param {Object|String} event An object with at least a 'type' attribute
* or a string indicating the event name.
* @return {void}
* @method fire
*/
fire: function(event){
if (typeof event == "string"){
event = { type: event };
}
if (!event.target){
event.target = this;
}
if (!event.type){
throw new Error("Event object missing 'type' property.");
}
if (this._listeners[event.type]){
//create a copy of the array and use that so listeners can't chane
var listeners = this._listeners[event.type].concat();
for (var i=0, len=listeners.length; i < len; i++){
listeners[i].call(this, event);
}
}
},
/**
* Removes a listener for a given event type.
* @param {String} type The type of event to remove a listener from.
* @param {Function} listener The function to remove from the event.
* @return {void}
* @method removeListener
*/
removeListener: function(type, listener){
if (this._listeners[type]){
var listeners = this._listeners[type];
for (var i=0, len=listeners.length; i < len; i++){
if (listeners[i] === listener){
listeners.splice(i, 1);
break;
}
}
}
}
};
/**
* Convenient way to read through strings.
* @namespace parserlib.util
* @class StringReader
* @constructor
* @param {String} text The text to read.
*/
function StringReader(text){
/**
* The input text with line endings normalized.
* @property _input
* @type String
* @private
*/
this._input = text.replace(/\n\r?/g, "\n");
/**
* The row for the character to be read next.
* @property _line
* @type int
* @private
*/
this._line = 1;
/**
* The column for the character to be read next.
* @property _col
* @type int
* @private
*/
this._col = 1;
/**
* The index of the character in the input to be read next.
* @property _cursor
* @type int
* @private
*/
this._cursor = 0;
}
StringReader.prototype = {
//restore constructor
constructor: StringReader,
//-------------------------------------------------------------------------
// Position info
//-------------------------------------------------------------------------
/**
* Returns the column of the character to be read next.
* @return {int} The column of the character to be read next.
* @method getCol
*/
getCol: function(){
return this._col;
},
/**
* Returns the row of the character to be read next.
* @return {int} The row of the character to be read next.
* @method getLine
*/
getLine: function(){
return this._line ;
},
/**
* Determines if you're at the end of the input.
* @return {Boolean} True if there's no more input, false otherwise.
* @method eof
*/
eof: function(){
return (this._cursor == this._input.length)
},
//-------------------------------------------------------------------------
// Basic reading
//-------------------------------------------------------------------------
/**
* Reads the next character without advancing the cursor.
* @param {int} count How many characters to look ahead (default is 1).
* @return {String} The next character or null if there is no next character.
* @method peek
*/
peek: function(count){
var c = null;
count = (typeof count == "undefined" ? 1 : count);
//if we're not at the end of the input...
if (this._cursor < this._input.length){
//get character and increment cursor and column
c = this._input.charAt(this._cursor + count - 1);
}
return c;
},
/**
* Reads the next character from the input and adjusts the row and column
* accordingly.
* @return {String} The next character or null if there is no next character.
* @method read
*/
read: function(){
var c = null;
//if we're not at the end of the input...
if (this._cursor < this._input.length){
//if the last character was a newline, increment row count
//and reset column count
if (this._input.charAt(this._cursor) == "\n"){
this._line++;
this._col=1;
} else {
this._col++;
}
//get character and increment cursor and column
c = this._input.charAt(this._cursor++);
}
return c;
},
//-------------------------------------------------------------------------
// Misc
//-------------------------------------------------------------------------
/**
* Saves the current location so it can be returned to later.
* @method mark
* @return {void}
*/
mark: function(){
this._bookmark = {
cursor: this._cursor,
line: this._line,
col: this._col
};
},
reset: function(){
if (this._bookmark){
this._cursor = this._bookmark.cursor;
this._line = this._bookmark.line;
this._col = this._bookmark.col;
delete this._bookmark;
}
},
//-------------------------------------------------------------------------
// Advanced reading
//-------------------------------------------------------------------------
/**
* Reads up to and including the given string. Throws an error if that
* string is not found.
* @param {String} pattern The string to read.
* @return {String} The string when it is found.
* @throws Error when the string pattern is not found.
* @method readTo
*/
readTo: function(pattern){
var buffer = "",
c;
/*
* First, buffer must be the same length as the pattern.
* Then, buffer must end with the pattern or else reach the
* end of the input.
*/
while (buffer.length < pattern.length || buffer.lastIndexOf(pattern) != buffer.length - pattern.length){
c = this.read();
if (c){
buffer += c;
} else {
throw new Error("Expected \"" + pattern + "\" at line " + this._line + ", col " + this._col + ".");
}
}
return buffer;
},
/**
* Reads characters while each character causes the given
* filter function to return true. The function is passed
* in each character and either returns true to continue
* reading or false to stop.
* @param {Function} filter The function to read on each character.
* @return {String} The string made up of all characters that passed the
* filter check.
* @method readWhile
*/
readWhile: function(filter){
var buffer = "",
c = this.read();
while(c !== null && filter(c)){
buffer += c;
c = this.read();
}
return buffer;
},
/**
* Reads characters that match either text or a regular expression and
* returns those characters. If a match is found, the row and column
* are adjusted; if no match is found, the reader's state is unchanged.
* reading or false to stop.
* @param {String|RegExp} matchter If a string, then the literal string
* value is searched for. If a regular expression, then any string
* matching the pattern is search for.
* @return {String} The string made up of all characters that matched or
* null if there was no match.
* @method readMatch
*/
readMatch: function(matcher){
var source = this._input.substring(this._cursor),
value = null;
//if it's a string, just do a straight match
if (typeof matcher == "string"){
if (source.indexOf(matcher) === 0){
value = this.readCount(matcher.length);
}
} else if (matcher instanceof RegExp){
if (matcher.test(source)){
value = this.readCount(RegExp.lastMatch.length);
}
}
return value;
},
/**
* Reads a given number of characters. If the end of the input is reached,
* it reads only the remaining characters and does not throw an error.
* @param {int} count The number of characters to read.
* @return {String} The string made up the read characters.
* @method readCount
*/
readCount: function(count){
var buffer = "";
while(count--){
buffer += this.read();
}
return buffer;
}
};
/**
* Type to use when a syntax error occurs.
* @class SyntaxError
* @namespace parserlib.util
* @constructor
* @param {String} message The error message.
* @param {int} line The line at which the error occurred.
* @param {int} col The column at which the error occurred.
*/
function SyntaxError(message, line, col){
/**
* The column at which the error occurred.
* @type int
* @property col
*/
this.col = col;
/**
* The line at which the error occurred.
* @type int
* @property line
*/
this.line = line;
/**
* The text representation of the unit.
* @type String
* @property text
*/
this.message = message;
}
//inherit from Error
SyntaxError.prototype = new Error();
/**
* Base type to represent a single syntactic unit.
* @class SyntaxUnit
* @namespace parserlib.util
* @constructor
* @param {String} text The text of the unit.
* @param {int} line The line of text on which the unit resides.
* @param {int} col The column of text on which the unit resides.
*/
function SyntaxUnit(text, line, col){
/**
* The column of text on which the unit resides.
* @type int
* @property col
*/
this.col = col;
/**
* The line of text on which the unit resides.
* @type int
* @property line
*/
this.line = line;
/**
* The text representation of the unit.
* @type String
* @property text
*/
this.text = text;
}
/**
* Create a new syntax unit based solely on the given token.
* Convenience method for creating a new syntax unit when
* it represents a single token instead of multiple.
* @param {Object} token The token object to represent.
* @return {parserlib.util.SyntaxUnit} The object representing the token.
* @static
* @method fromToken
*/
SyntaxUnit.fromToken = function(token){
return new SyntaxUnit(token.value, token.startLine, token.startCol);
};
SyntaxUnit.prototype = {
//restore constructor
constructor: SyntaxUnit,
/**
* Returns the text representation of the unit.
* @return {String} The text representation of the unit.
* @method valueOf
*/
valueOf: function(){
return this.toString();
},
/**
* Returns the text representation of the unit.
* @return {String} The text representation of the unit.
* @method toString
*/
toString: function(){
return this.text;
}
};
/**
* Generic TokenStream providing base functionality.
* @class TokenStreamBase
* @namespace parserlib.util
* @constructor
* @param {String|StringReader} input The text to tokenize or a reader from
* which to read the input.
*/
function TokenStreamBase(input, tokenData){
/**
* The string reader for easy access to the text.
* @type StringReader
* @property _reader
* @private
*/
//this._reader = (typeof input == "string") ? new StringReader(input) : input;
this._reader = input ? new StringReader(input.toString()) : null;
/**
* Token object for the last consumed token.
* @type Token
* @property _token
* @private
*/
this._token = null;
/**
* The array of token information.
* @type Array
* @property _tokenData
* @private
*/
this._tokenData = tokenData;
/**
* Lookahead token buffer.
* @type Array
* @property _lt
* @private
*/
this._lt = [];
/**
* Lookahead token buffer index.
* @type int
* @property _ltIndex
* @private
*/
this._ltIndex = 0;
this._ltIndexCache = [];
}
/**
* Accepts an array of token information and outputs
* an array of token data containing key-value mappings
* and matching functions that the TokenStream needs.
* @param {Array} tokens An array of token descriptors.
* @return {Array} An array of processed token data.
* @method createTokenData
* @static
*/
TokenStreamBase.createTokenData = function(tokens){
var nameMap = [],
typeMap = {},
tokenData = tokens.concat([]),
i = 0,
len = tokenData.length+1;
tokenData.UNKNOWN = -1;
tokenData.unshift({name:"EOF"});
for (; i < len; i++){
nameMap.push(tokenData[i].name);
tokenData[tokenData[i].name] = i;
if (tokenData[i].text){
typeMap[tokenData[i].text] = i;
}
}
tokenData.name = function(tt){
return nameMap[tt];
};
tokenData.type = function(c){
return typeMap[c];
};
return tokenData;
};
TokenStreamBase.prototype = {
//restore constructor
constructor: TokenStreamBase,
//-------------------------------------------------------------------------
// Matching methods
//-------------------------------------------------------------------------
/**
* Determines if the next token matches the given token type.
* If so, that token is consumed; if not, the token is placed
* back onto the token stream. You can pass in any number of
* token types and this will return true if any of the token
* types is found.
* @param {int|int[]} tokenTypes Either a single token type or an array of
* token types that the next token might be. If an array is passed,
* it's assumed that the token can be any of these.
* @param {variant} channel (Optional) The channel to read from. If not
* provided, reads from the default (unnamed) channel.
* @return {Boolean} True if the token type matches, false if not.
* @method match
*/
match: function(tokenTypes, channel){
//always convert to an array, makes things easier
if (!(tokenTypes instanceof Array)){
tokenTypes = [tokenTypes];
}
var tt = this.get(channel),
i = 0,
len = tokenTypes.length;
while(i < len){
if (tt == tokenTypes[i++]){
return true;
}
}
//no match found, put the token back
this.unget();
return false;
},
/**
* Determines if the next token matches the given token type.
* If so, that token is consumed; if not, an error is thrown.
* @param {int|int[]} tokenTypes Either a single token type or an array of
* token types that the next token should be. If an array is passed,
* it's assumed that the token must be one of these.
* @param {variant} channel (Optional) The channel to read from. If not
* provided, reads from the default (unnamed) channel.
* @return {void}
* @method mustMatch
*/
mustMatch: function(tokenTypes, channel){
//always convert to an array, makes things easier
if (!(tokenTypes instanceof Array)){
tokenTypes = [tokenTypes];
}
if (!this.match.apply(this, arguments)){
token = this.LT(1);
throw new SyntaxError("Expected " + this._tokenData[tokenTypes[0]].name +
" at line " + token.startLine + ", character " + token.startCol + ".", token.startLine, token.startCol);
}
},
//-------------------------------------------------------------------------
// Consuming methods
//-------------------------------------------------------------------------
/**
* Keeps reading from the token stream until either one of the specified
* token types is found or until the end of the input is reached.
* @param {int|int[]} tokenTypes Either a single token type or an array of
* token types that the next token should be. If an array is passed,
* it's assumed that the token must be one of these.
* @param {variant} channel (Optional) The channel to read from. If not
* provided, reads from the default (unnamed) channel.
* @return {void}
* @method advance
*/
advance: function(tokenTypes, channel){
while(this.LA(0) != 0 && !this.match(tokenTypes, channel)){
this.get();
}
return this.LA(0);
},
/**
* Consumes the next token from the token stream.
* @return {int} The token type of the token that was just consumed.
* @method get
*/
get: function(channel){
var tokenInfo = this._tokenData,
reader = this._reader,
value,
i =0,
len = tokenInfo.length,
found = false,
token,
info;
//check the lookahead buffer first
if (this._lt.length && this._ltIndex >= 0 && this._ltIndex < this._lt.length){
i++;
this._token = this._lt[this._ltIndex++];
info = tokenInfo[this._token.type];
//obey channels logic
while((info.channel !== undefined && channel !== info.channel) &&
this._ltIndex < this._lt.length){
this._token = this._lt[this._ltIndex++];
info = tokenInfo[this._token.type];
i++;
}
//here be dragons
if ((info.channel === undefined || channel === info.channel) &&
this._ltIndex <= this._lt.length){
this._ltIndexCache.push(i);
return this._token.type;
}
}
//call token retriever method
token = this._getToken();
//if it should be hidden, don't save a token
if (token.type > -1 && !tokenInfo[token.type].hide){
//apply token channel
token.channel = tokenInfo[token.type].channel;
//save for later
this._token = token;
this._lt.push(token);
//save space that will be moved (must be done before array is truncated)
this._ltIndexCache.push(this._lt.length - this._ltIndex + i);
//keep the buffer under 5 items
if (this._lt.length > 5){
this._lt.shift();
}
//also keep the shift buffer under 5 items
if (this._ltIndexCache.length > 5){
this._ltIndexCache.shift();
}
//update lookahead index
this._ltIndex = this._lt.length;
}
/*
* Skip to the next token if:
* 1. The token type is marked as hidden.
* 2. The token type has a channel specified and it isn't the current channel.
*/
info = tokenInfo[token.type];
if (info &&
(info.hide ||
(info.channel !== undefined && channel !== info.channel))){
return this.get(channel);
} else {
//return just the type
return token.type;
}
},
/**
* Looks ahead a certain number of tokens and returns the token type at
* that position. This will throw an error if you lookahead past the
* end of input, past the size of the lookahead buffer, or back past
* the first token in the lookahead buffer.
* @param {int} The index of the token type to retrieve. 0 for the
* current token, 1 for the next, -1 for the previous, etc.
* @return {int} The token type of the token in the given position.
* @method LA
*/
LA: function(index){
var total = index,
tt;
if (index > 0){
//TODO: Store 5 somewhere
if (index > 5){
throw new Error("Too much lookahead.");
}
//get all those tokens
while(total){
tt = this.get();
total--;
}
//unget all those tokens
while(total < index){
this.unget();
total++;
}
} else if (index < 0){
if(this._lt[this._ltIndex+index]){
tt = this._lt[this._ltIndex+index].type;
} else {
throw new Error("Too much lookbehind.");
}
} else {
tt = this._token.type;
}
return tt;
},
/**
* Looks ahead a certain number of tokens and returns the token at
* that position. This will throw an error if you lookahead past the
* end of input, past the size of the lookahead buffer, or back past
* the first token in the lookahead buffer.
* @param {int} The index of the token type to retrieve. 0 for the
* current token, 1 for the next, -1 for the previous, etc.
* @return {Object} The token of the token in the given position.
* @method LA
*/
LT: function(index){
//lookahead first to prime the token buffer
this.LA(index);
//now find the token, subtract one because _ltIndex is already at the next index
return this._lt[this._ltIndex+index-1];
},
/**
* Returns the token type for the next token in the stream without
* consuming it.
* @return {int} The token type of the next token in the stream.
* @method peek
*/
peek: function(){
return this.LA(1);
},
/**
* Returns the actual token object for the last consumed token.
* @return {Token} The token object for the last consumed token.
* @method token
*/
token: function(){
return this._token;
},
/**
* Returns the name of the token for the given token type.
* @param {int} tokenType The type of token to get the name of.
* @return {String} The name of the token or "UNKNOWN_TOKEN" for any
* invalid token type.
* @method tokenName
*/
tokenName: function(tokenType){
if (tokenType < 0 || tokenType > this._tokenData.length){
return "UNKNOWN_TOKEN";
} else {
return this._tokenData[tokenType].name;
}
},
/**
* Returns the token type value for the given token name.
* @param {String} tokenName The name of the token whose value should be returned.
* @return {int} The token type value for the given token name or -1
* for an unknown token.
* @method tokenName
*/
tokenType: function(tokenName){
return this._tokenData[tokenName] || -1;
},
/**
* Returns the last consumed token to the token stream.
* @method unget
*/
unget: function(){
//if (this._ltIndex > -1){
if (this._ltIndexCache.length){
this._ltIndex -= this._ltIndexCache.pop();//--;
this._token = this._lt[this._ltIndex - 1];
} else {
throw new Error("Too much lookahead.");
}
}
};
parserlib.util = {
StringReader: StringReader,
SyntaxError : SyntaxError,
SyntaxUnit : SyntaxUnit,
EventTarget : EventTarget,
TokenStreamBase : TokenStreamBase
};
})();
/*
Parser-Lib
Copyright (c) 2009-2011 Nicholas C. Zakas. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* Build time: 5-July-2011 03:12:40 */
(function(){
var EventTarget = parserlib.util.EventTarget,
TokenStreamBase = parserlib.util.TokenStreamBase,
StringReader = parserlib.util.StringReader,
SyntaxError = parserlib.util.SyntaxError,
SyntaxUnit = parserlib.util.SyntaxUnit;
var Colors = {
aliceblue :"#f0f8ff",
antiquewhite :"#faebd7",
aqua :"#00ffff",
aquamarine :"#7fffd4",
azure :"#f0ffff",
beige :"#f5f5dc",
bisque :"#ffe4c4",
black :"#000000",
blanchedalmond :"#ffebcd",
blue :"#0000ff",
blueviolet :"#8a2be2",
brown :"#a52a2a",
burlywood :"#deb887",
cadetblue :"#5f9ea0",
chartreuse :"#7fff00",
chocolate :"#d2691e",
coral :"#ff7f50",
cornflowerblue :"#6495ed",
cornsilk :"#fff8dc",
crimson :"#dc143c",
cyan :"#00ffff",
darkblue :"#00008b",
darkcyan :"#008b8b",
darkgoldenrod :"#b8860b",
darkgray :"#a9a9a9",
darkgreen :"#006400",
darkkhaki :"#bdb76b",
darkmagenta :"#8b008b",
darkolivegreen :"#556b2f",
darkorange :"#ff8c00",
darkorchid :"#9932cc",
darkred :"#8b0000",
darksalmon :"#e9967a",
darkseagreen :"#8fbc8f",
darkslateblue :"#483d8b",
darkslategray :"#2f4f4f",
darkturquoise :"#00ced1",
darkviolet :"#9400d3",
deeppink :"#ff1493",
deepskyblue :"#00bfff",
dimgray :"#696969",
dodgerblue :"#1e90ff",
firebrick :"#b22222",
floralwhite :"#fffaf0",
forestgreen :"#228b22",
fuchsia :"#ff00ff",
gainsboro :"#dcdcdc",
ghostwhite :"#f8f8ff",
gold :"#ffd700",
goldenrod :"#daa520",
gray :"#808080",
green :"#008000",
greenyellow :"#adff2f",
honeydew :"#f0fff0",
hotpink :"#ff69b4",
indianred :"#cd5c5c",
indigo :"#4b0082",
ivory :"#fffff0",
khaki :"#f0e68c",
lavender :"#e6e6fa",
lavenderblush :"#fff0f5",
lawngreen :"#7cfc00",
lemonchiffon :"#fffacd",
lightblue :"#add8e6",
lightcoral :"#f08080",
lightcyan :"#e0ffff",
lightgoldenrodyellow :"#fafad2",
lightgrey :"#d3d3d3",
lightgreen :"#90ee90",
lightpink :"#ffb6c1",
lightsalmon :"#ffa07a",
lightseagreen :"#20b2aa",
lightskyblue :"#87cefa",
lightslategray :"#778899",
lightsteelblue :"#b0c4de",
lightyellow :"#ffffe0",
lime :"#00ff00",
limegreen :"#32cd32",
linen :"#faf0e6",
magenta :"#ff00ff",
maroon :"#800000",
mediumaquamarine:"#66cdaa",
mediumblue :"#0000cd",
mediumorchid :"#ba55d3",
mediumpurple :"#9370d8",
mediumseagreen :"#3cb371",
mediumslateblue :"#7b68ee",
mediumspringgreen :"#00fa9a",
mediumturquoise :"#48d1cc",
mediumvioletred :"#c71585",
midnightblue :"#191970",
mintcream :"#f5fffa",
mistyrose :"#ffe4e1",
moccasin :"#ffe4b5",
navajowhite :"#ffdead",
navy :"#000080",
oldlace :"#fdf5e6",
olive :"#808000",
olivedrab :"#6b8e23",
orange :"#ffa500",
orangered :"#ff4500",
orchid :"#da70d6",
palegoldenrod :"#eee8aa",
palegreen :"#98fb98",
paleturquoise :"#afeeee",
palevioletred :"#d87093",
papayawhip :"#ffefd5",
peachpuff :"#ffdab9",
peru :"#cd853f",
pink :"#ffc0cb",
plum :"#dda0dd",
powderblue :"#b0e0e6",
purple :"#800080",
red :"#ff0000",
rosybrown :"#bc8f8f",
royalblue :"#4169e1",
saddlebrown :"#8b4513",
salmon :"#fa8072",
sandybrown :"#f4a460",
seagreen :"#2e8b57",
seashell :"#fff5ee",
sienna :"#a0522d",
silver :"#c0c0c0",
skyblue :"#87ceeb",
slateblue :"#6a5acd",
slategray :"#708090",
snow :"#fffafa",
springgreen :"#00ff7f",
steelblue :"#4682b4",
tan :"#d2b48c",
teal :"#008080",
thistle :"#d8bfd8",
tomato :"#ff6347",
turquoise :"#40e0d0",
violet :"#ee82ee",
wheat :"#f5deb3",
white :"#ffffff",
whitesmoke :"#f5f5f5",
yellow :"#ffff00",
yellowgreen :"#9acd32"
};
/**
* Represents a selector combinator (whitespace, +, >).
* @namespace parserlib.css
* @class Combinator
* @extends parserlib.util.SyntaxUnit
* @constructor
* @param {String} text The text representation of the unit.
* @param {int} line The line of text on which the unit resides.
* @param {int} col The column of text on which the unit resides.
*/
function Combinator(text, line, col){
SyntaxUnit.call(this, text, line, col);
/**
* The type of modifier.
* @type String
* @property type
*/
this.type = "unknown";
//pretty simple
if (/^\s+$/.test(text)){
this.type = "descendant";
} else if (text == ">"){
this.type = "child";
} else if (text == "+"){
this.type = "adjacent-sibling";
} else if (text == "~"){
this.type = "sibling";
}
}
Combinator.prototype = new SyntaxUnit();
Combinator.prototype.constructor = Combinator;
var Level1Properties = {
"background": 1,
"background-attachment": 1,
"background-color": 1,
"background-image": 1,
"background-position": 1,
"background-repeat": 1,
"border": 1,
"border-bottom": 1,
"border-bottom-width": 1,
"border-color": 1,
"border-left": 1,
"border-left-width": 1,
"border-right": 1,
"border-right-width": 1,
"border-style": 1,
"border-top": 1,
"border-top-width": 1,
"border-width": 1,
"clear": 1,
"color": 1,
"display": 1,
"float": 1,
"font": 1,
"font-family": 1,
"font-size": 1,
"font-style": 1,
"font-variant": 1,
"font-weight": 1,
"height": 1,
"letter-spacing": 1,
"line-height": 1,
"list-style": 1,
"list-style-image": 1,
"list-style-position": 1,
"list-style-type": 1,
"margin": 1,
"margin-bottom": 1,
"margin-left": 1,
"margin-right": 1,
"margin-top": 1,
"padding": 1,
"padding-bottom": 1,
"padding-left": 1,
"padding-right": 1,
"padding-top": 1,
"text-align": 1,
"text-decoration": 1,
"text-indent": 1,
"text-transform": 1,
"vertical-align": 1,
"white-space": 1,
"width": 1,
"word-spacing": 1
};
var Level2Properties = {
//Aural
"azimuth": 1,
"cue-after": 1,
"cue-before": 1,
"cue": 1,
"elevation": 1,
"pause-after": 1,
"pause-before": 1,
"pause": 1,
"pitch-range": 1,
"pitch": 1,
"play-during": 1,
"richness": 1,
"speak-header": 1,
"speak-numeral": 1,
"speak-punctuation": 1,
"speak": 1,
"speech-rate": 1,
"stress": 1,
"voice-family": 1,
"volume": 1,
//Paged
"orphans": 1,
"page-break-after": 1,
"page-break-before": 1,
"page-break-inside": 1,
"widows": 1,
//Interactive
"cursor": 1,
"outline-color": 1,
"outline-style": 1,
"outline-width": 1,
"outline": 1,
//Visual
"background-attachment": 1,
"background-color": 1,
"background-image": 1,
"background-position": 1,
"background-repeat": 1,
"background": 1,
"border-collapse": 1,
"border-color": 1,
"border-spacing": 1,
"border-style": 1,
"border-top": 1,
"border-top-color": 1,
"border-top-style": 1,
"border-top-width": 1,
"border-width": 1,
"border": 1,
"bottom": 1,
"caption-side": 1,
"clear": 1,
"clip": 1,
"color": 1,
"content": 1,
"counter-increment": 1,
"counter-reset": 1,
"direction": 1,
"display": 1,
"empty-cells": 1,
"float": 1,
"font-family": 1,
"font-size": 1,
"font-style": 1,
"font-variant": 1,
"font-weight": 1,
"font": 1,
"height": 1,
"left": 1,
"letter-spacing": 1,
"line-height": 1,
"list-style-image": 1,
"list-style-position": 1,
"list-style-type": 1,
"list-style": 1,
"margin-right": 1,
"margin-top": 1,
"margin": 1,
"max-height": 1,
"max-width": 1,
"min-height": 1,
"min-width": 1,
"overflow": 1,
"padding-top": 1,
"padding": 1,
"position": 1,
"quotes": 1,
"right": 1,
"table-layout": 1,
"text-align": 1,
"text-decoration": 1,
"text-indent": 1,
"text-transform": 1,
"top": 1,
"unicode-bidi": 1,
"vertical-align": 1,
"visibility": 1,
"white-space": 1,
"width": 1,
"word-spacing": 1,
"z-index": 1
};
/**
* Represents a media feature, such as max-width:500.
* @namespace parserlib.css
* @class MediaFeature
* @extends parserlib.util.SyntaxUnit
* @constructor
* @param {SyntaxUnit} name The name of the feature.
* @param {SyntaxUnit} value The value of the feature or null if none.
*/
function MediaFeature(name, value){
SyntaxUnit.call(this, "(" + name + (value !== null ? ":" + value : "") + ")", name.startLine, name.startCol);
/**
* The name of the media feature
* @type String
* @property name
*/
this.name = name;
/**
* The value for the feature or null if there is none.
* @type SyntaxUnit
* @property value
*/
this.value = value;
}
MediaFeature.prototype = new SyntaxUnit();
MediaFeature.prototype.constructor = MediaFeature;
/**
* Represents an individual media query.
* @namespace parserlib.css
* @class MediaQuery
* @extends parserlib.util.SyntaxUnit
* @constructor
* @param {String} modifier The modifier "not" or "only" (or null).
* @param {String} mediaType The type of media (i.e., "print").
* @param {Array} parts Array of selectors parts making up this selector.
* @param {int} line The line of text on which the unit resides.
* @param {int} col The column of text on which the unit resides.
*/
function MediaQuery(modifier, mediaType, features, line, col){
SyntaxUnit.call(this, (modifier ? modifier + " ": "") + (mediaType ? mediaType + " " : "") + features.join(" and "), line, col);
/**
* The media modifier ("not" or "only")
* @type String
* @property modifier
*/
this.modifier = modifier;
/**
* The mediaType (i.e., "print")
* @type String
* @property mediaType
*/
this.mediaType = mediaType;
/**
* The parts that make up the selector.
* @type Array
* @property features
*/
this.features = features;
}
MediaQuery.prototype = new SyntaxUnit();
MediaQuery.prototype.constructor = MediaQuery;
/**
* A CSS3 parser.
* @namespace parserlib.css
* @class Parser
* @constructor
* @param {Object} options (Optional) Various options for the parser:
* starHack (true|false) to allow IE6 star hack as valid,
* underscoreHack (true|false) to interpret leading underscores
* as IE6-7 targeting for known properties, ieFilters (true|false)
* to indicate that IE < 8 filters should be accepted and not throw
* syntax errors.
*/
function Parser(options){
//inherit event functionality
EventTarget.call(this);
this.options = options || {};
this._tokenStream = null;
}
Parser.prototype = function(){
var proto = new EventTarget(), //new prototype
prop,
additions = {
//restore constructor
constructor: Parser,
//-----------------------------------------------------------------
// Grammar
//-----------------------------------------------------------------
_stylesheet: function(){
/*
* stylesheet
* : [ CHARSET_SYM S* STRING S* ';' ]?
* [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
* [ namespace [S|CDO|CDC]* ]*
* [ [ ruleset | media | page | font_face ] [S|CDO|CDC]* ]*
* ;
*/
var tokenStream = this._tokenStream,
charset = null,
token,
tt;
this.fire("startstylesheet");
//try to read character set
this._charset();
this._skipCruft();
//try to read imports - may be more than one
while (tokenStream.peek() == Tokens.IMPORT_SYM){
this._import();
this._skipCruft();
}
//try to read namespaces - may be more than one
while (tokenStream.peek() == Tokens.NAMESPACE_SYM){
this._namespace();
this._skipCruft();
}
//get the next token
tt = tokenStream.peek();
//try to read the rest
while(tt > Tokens.EOF){
try {
switch(tt){
case Tokens.MEDIA_SYM:
this._media();
this._skipCruft();
break;
case Tokens.PAGE_SYM:
this._page();
this._skipCruft();
break;
case Tokens.FONT_FACE_SYM:
this._font_face();
this._skipCruft();
break;
case Tokens.S:
this._readWhitespace();
break;
default:
if(!this._ruleset()){
//error handling for known issues
switch(tt){
case Tokens.CHARSET_SYM:
token = tokenStream.LT(1);
this._charset(false);
throw new SyntaxError("@charset not allowed here.", token.startLine, token.startCol);
case Tokens.IMPORT_SYM:
token = tokenStream.LT(1);
this._import(false);
throw new SyntaxError("@import not allowed here.", token.startLine, token.startCol);
case Tokens.NAMESPACE_SYM:
token = tokenStream.LT(1);
this._namespace(false);
throw new SyntaxError("@namespace not allowed here.", token.startLine, token.startCol);
default:
tokenStream.get(); //get the last token
this._unexpectedToken(tokenStream.token());
}
}
}
} catch(ex) {
if (ex instanceof SyntaxError && !this.options.strict){
this.fire({
type: "error",
error: ex,
message: ex.message,
line: ex.line,
col: ex.col
});
} else {
throw ex;
}
}
tt = tokenStream.peek();
}
if (tt != Tokens.EOF){
this._unexpectedToken(tokenStream.token());
}
this.fire("endstylesheet");
},
_charset: function(emit){
var tokenStream = this._tokenStream,
charset,
token,
line,
col;
if (tokenStream.match(Tokens.CHARSET_SYM)){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._readWhitespace();
tokenStream.mustMatch(Tokens.STRING);
token = tokenStream.token();
charset = token.value;
this._readWhitespace();
tokenStream.mustMatch(Tokens.SEMICOLON);
if (emit !== false){
this.fire({
type: "charset",
charset:charset,
line: line,
col: col
});
}
}
},
_import: function(emit){
/*
* import
* : IMPORT_SYM S*
* [STRING|URI] S* media_query_list? ';' S*
*/
var tokenStream = this._tokenStream,
tt,
uri,
importToken,
mediaList = [];
//read import symbol
tokenStream.mustMatch(Tokens.IMPORT_SYM);
importToken = tokenStream.token();
this._readWhitespace();
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
//grab the URI value
uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
this._readWhitespace();
mediaList = this._media_query_list();
//must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
this._readWhitespace();
if (emit !== false){
this.fire({
type: "import",
uri: uri,
media: mediaList,
line: importToken.startLine,
col: importToken.startCol
});
}
},
_namespace: function(emit){
/*
* namespace
* : NAMESPACE_SYM S* [namespace_prefix S*]? [STRING|URI] S* ';' S*
*/
var tokenStream = this._tokenStream,
line,
col,
prefix,
uri;
//read import symbol
tokenStream.mustMatch(Tokens.NAMESPACE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._readWhitespace();
//it's a namespace prefix - no _namespace_prefix() method because it's just an IDENT
if (tokenStream.match(Tokens.IDENT)){
prefix = tokenStream.token().value;
this._readWhitespace();
}
tokenStream.mustMatch([Tokens.STRING, Tokens.URI]);
/*if (!tokenStream.match(Tokens.STRING)){
tokenStream.mustMatch(Tokens.URI);
}*/
//grab the URI value
uri = tokenStream.token().value.replace(/(?:url\()?["']([^"']+)["']\)?/, "$1");
this._readWhitespace();
//must end with a semicolon
tokenStream.mustMatch(Tokens.SEMICOLON);
this._readWhitespace();
if (emit !== false){
this.fire({
type: "namespace",
prefix: prefix,
uri: uri,
line: line,
col: col
});
}
},
_media: function(){
/*
* media
* : MEDIA_SYM S* media_query_list S* '{' S* ruleset* '}' S*
* ;
*/
var tokenStream = this._tokenStream,
line,
col,
mediaList;// = [];
//look for @media
tokenStream.mustMatch(Tokens.MEDIA_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._readWhitespace();
mediaList = this._media_query_list();
tokenStream.mustMatch(Tokens.LBRACE);
this._readWhitespace();
this.fire({
type: "startmedia",
media: mediaList,
line: line,
col: col
});
while(true) {
if (tokenStream.peek() == Tokens.PAGE_SYM){
this._page();
} else if (!this._ruleset()){
break;
}
}
tokenStream.mustMatch(Tokens.RBRACE);
this._readWhitespace();
this.fire({
type: "endmedia",
media: mediaList,
line: line,
col: col
});
},
//CSS3 Media Queries
_media_query_list: function(){
/*
* media_query_list
* : S* [media_query [ ',' S* media_query ]* ]?
* ;
*/
var tokenStream = this._tokenStream,
mediaList = [];
this._readWhitespace();
if (tokenStream.peek() == Tokens.IDENT || tokenStream.peek() == Tokens.LPAREN){
mediaList.push(this._media_query());
}
while(tokenStream.match(Tokens.COMMA)){
this._readWhitespace();
mediaList.push(this._media_query());
}
return mediaList;
},
/*
* Note: "expression" in the grammar maps to the _media_expression
* method.
*/
_media_query: function(){
/*
* media_query
* : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
* | expression [ AND S* expression ]*
* ;
*/
var tokenStream = this._tokenStream,
type = null,
ident = null,
token = null,
expressions = [];
if (tokenStream.match(Tokens.IDENT)){
ident = tokenStream.token().value.toLowerCase();
//since there's no custom tokens for these, need to manually check
if (ident != "only" && ident != "not"){
tokenStream.unget();
ident = null;
} else {
token = tokenStream.token();
}
}
this._readWhitespace();
if (tokenStream.peek() == Tokens.IDENT){
type = this._media_type();
if (token === null){
token = tokenStream.token();
}
} else if (tokenStream.peek() == Tokens.LPAREN){
if (token === null){
token = tokenStream.LT(1);
}
expressions.push(this._media_expression());
}
if (type === null && expressions.length === 0){
return null;
} else {
this._readWhitespace();
while (tokenStream.match(Tokens.IDENT)){
if (tokenStream.token().value.toLowerCase() != "and"){
this._unexpectedToken(tokenStream.token());
}
this._readWhitespace();
expressions.push(this._media_expression());
}
}
return new MediaQuery(ident, type, expressions, token.startLine, token.startCol);
},
//CSS3 Media Queries
_media_type: function(){
/*
* media_type
* : IDENT
* ;
*/
return this._media_feature();
},
/**
* Note: in CSS3 Media Queries, this is called "expression".
* Renamed here to avoid conflict with CSS3 Selectors
* definition of "expression". Also note that "expr" in the
* grammar now maps to "expression" from CSS3 selectors.
* @method _media_expression
* @private
*/
_media_expression: function(){
/*
* expression
* : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
* ;
*/
var tokenStream = this._tokenStream,
feature = null,
token,
expression = null;
tokenStream.mustMatch(Tokens.LPAREN);
feature = this._media_feature();
this._readWhitespace();
if (tokenStream.match(Tokens.COLON)){
this._readWhitespace();
token = tokenStream.LT(1);
expression = this._expression();
}
tokenStream.mustMatch(Tokens.RPAREN);
this._readWhitespace();
return new MediaFeature(feature, (expression ? new SyntaxUnit(expression, token.startLine, token.startCol) : null));
},
//CSS3 Media Queries
_media_feature: function(){
/*
* media_feature
* : IDENT
* ;
*/
var tokenStream = this._tokenStream;
tokenStream.mustMatch(Tokens.IDENT);
return SyntaxUnit.fromToken(tokenStream.token());
},
//CSS3 Paged Media
_page: function(){
/*
* page:
* PAGE_SYM S* IDENT? pseudo_page? S*
* '{' S* [ declaration | margin ]? [ ';' S* [ declaration | margin ]? ]* '}' S*
* ;
*/
var tokenStream = this._tokenStream,
line,
col,
identifier = null,
pseudoPage = null;
//look for @page
tokenStream.mustMatch(Tokens.PAGE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._readWhitespace();
if (tokenStream.match(Tokens.IDENT)){
identifier = tokenStream.token().value;
//The value 'auto' may not be used as a page name and MUST be treated as a syntax error.
if (identifier.toLowerCase() === "auto"){
this._unexpectedToken(tokenStream.token());
}
}
//see if there's a colon upcoming
if (tokenStream.peek() == Tokens.COLON){
pseudoPage = this._pseudo_page();
}
this._readWhitespace();
this.fire({
type: "startpage",
id: identifier,
pseudo: pseudoPage,
line: line,
col: col
});
this._readDeclarations(true, true);
this.fire({
type: "endpage",
id: identifier,
pseudo: pseudoPage,
line: line,
col: col
});
},
//CSS3 Paged Media
_margin: function(){
/*
* margin :
* margin_sym S* '{' declaration [ ';' S* declaration? ]* '}' S*
* ;
*/
var tokenStream = this._tokenStream,
line,
col,
marginSym = this._margin_sym();
if (marginSym){
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this.fire({
type: "startpagemargin",
margin: marginSym,
line: line,
col: col
});
this._readDeclarations(true);
this.fire({
type: "endpagemargin",
margin: marginSym,
line: line,
col: col
});
return true;
} else {
return false;
}
},
//CSS3 Paged Media
_margin_sym: function(){
/*
* margin_sym :
* TOPLEFTCORNER_SYM |
* TOPLEFT_SYM |
* TOPCENTER_SYM |
* TOPRIGHT_SYM |
* TOPRIGHTCORNER_SYM |
* BOTTOMLEFTCORNER_SYM |
* BOTTOMLEFT_SYM |
* BOTTOMCENTER_SYM |
* BOTTOMRIGHT_SYM |
* BOTTOMRIGHTCORNER_SYM |
* LEFTTOP_SYM |
* LEFTMIDDLE_SYM |
* LEFTBOTTOM_SYM |
* RIGHTTOP_SYM |
* RIGHTMIDDLE_SYM |
* RIGHTBOTTOM_SYM
* ;
*/
var tokenStream = this._tokenStream;
if(tokenStream.match([Tokens.TOPLEFTCORNER_SYM, Tokens.TOPLEFT_SYM,
Tokens.TOPCENTER_SYM, Tokens.TOPRIGHT_SYM, Tokens.TOPRIGHTCORNER_SYM,
Tokens.BOTTOMLEFTCORNER_SYM, Tokens.BOTTOMLEFT_SYM,
Tokens.BOTTOMCENTER_SYM, Tokens.BOTTOMRIGHT_SYM,
Tokens.BOTTOMRIGHTCORNER_SYM, Tokens.LEFTTOP_SYM,
Tokens.LEFTMIDDLE_SYM, Tokens.LEFTBOTTOM_SYM, Tokens.RIGHTTOP_SYM,
Tokens.RIGHTMIDDLE_SYM, Tokens.RIGHTBOTTOM_SYM]))
{
return SyntaxUnit.fromToken(tokenStream.token());
} else {
return null;
}
},
_pseudo_page: function(){
/*
* pseudo_page
* : ':' IDENT
* ;
*/
var tokenStream = this._tokenStream;
tokenStream.mustMatch(Tokens.COLON);
tokenStream.mustMatch(Tokens.IDENT);
//TODO: CSS3 Paged Media says only "left", "center", and "right" are allowed
return tokenStream.token().value;
},
_font_face: function(){
/*
* font_face
* : FONT_FACE_SYM S*
* '{' S* declaration [ ';' S* declaration ]* '}' S*
* ;
*/
var tokenStream = this._tokenStream,
line,
col;
//look for @page
tokenStream.mustMatch(Tokens.FONT_FACE_SYM);
line = tokenStream.token().startLine;
col = tokenStream.token().startCol;
this._readWhitespace();
this.fire({
type: "startfontface",
line: line,
col: col
});
this._readDeclarations(true);
this.fire({
type: "endfontface",
line: line,
col: col
});
},
_operator: function(){
/*
* operator
* : '/' S* | ',' S* | /( empty )/
* ;
*/
var tokenStream = this._tokenStream,
token = null;
if (tokenStream.match([Tokens.SLASH, Tokens.COMMA])){
token = tokenStream.token();
this._readWhitespace();
}
return token ? PropertyValuePart.fromToken(token) : null;
},
_combinator: function(){
/*
* combinator
* : PLUS S* | GREATER S* | TILDE S* | S+
* ;
*/
var tokenStream = this._tokenStream,
value = null,
token;
if(tokenStream.match([Tokens.PLUS, Tokens.GREATER, Tokens.TILDE])){
token = tokenStream.token();
value = new Combinator(token.value, token.startLine, token.startCol);
this._readWhitespace();
}
return value;
},
_unary_operator: function(){
/*
* unary_operator
* : '-' | '+'
* ;
*/
var tokenStream = this._tokenStream;
if (tokenStream.match([Tokens.MINUS, Tokens.PLUS])){
return tokenStream.token().value;
} else {
return null;
}
},
_property: function(){
/*
* property
* : IDENT S*
* ;
*/
var tokenStream = this._tokenStream,
value = null,
hack = null,
tokenValue,
token,
line,
col;
//check for star hack - throws error if not allowed
if (tokenStream.peek() == Tokens.STAR && this.options.starHack){
tokenStream.get();
token = tokenStream.token();
hack = token.value;
line = token.startLine;
col = token.startCol;
}
if(tokenStream.match(Tokens.IDENT)){
token = tokenStream.token();
tokenValue = token.value;
//check for underscore hack - no error if not allowed because it's valid CSS syntax
if (tokenValue.charAt(0) == "_" && this.options.underscoreHack){
hack = "_";
tokenValue = tokenValue.substring(1);
}
value = new PropertyName(tokenValue, hack, (line||token.startLine), (col||token.startCol));
this._readWhitespace();
}
return value;
},
//Augmented with CSS3 Selectors
_ruleset: function(){
/*
* ruleset
* : selectors_group
* '{' S* declaration? [ ';' S* declaration? ]* '}' S*
* ;
*/
var tokenStream = this._tokenStream,
tt,
selectors;
/*
* Error Recovery: If even a single selector fails to parse,
* then the entire ruleset should be thrown away.
*/
try {
selectors = this._selectors_group();
} catch (ex){
if (ex instanceof SyntaxError && !this.options.strict){
//fire error event
this.fire({
type: "error",
error: ex,
message: ex.message,
line: ex.line,
col: ex.col
});
//skip over everything until closing brace
tt = tokenStream.advance([Tokens.RBRACE]);
if (tt == Tokens.RBRACE){
//if there's a right brace, the rule is finished so don't do anything
} else {
//otherwise, rethrow the error because it wasn't handled properly
throw ex;
}
} else {
//not a syntax error, rethrow it
throw ex;
}
//trigger parser to continue
return true;
}
//if it got here, all selectors parsed
if (selectors){
this.fire({
type: "startrule",
selectors: selectors,
line: selectors[0].line,
col: selectors[0].col
});
this._readDeclarations(true);
this.fire({
type: "endrule",
selectors: selectors,
line: selectors[0].line,
col: selectors[0].col
});
}
return selectors;
},
//CSS3 Selectors
_selectors_group: function(){
/*
gitextract_950o7lh_/ ├── .gitignore ├── README.md ├── app/ │ ├── app.py │ ├── app.yaml │ ├── common/ │ │ ├── __init__.py │ │ └── templateaddons.py │ ├── cron.yaml │ ├── handlers/ │ │ ├── __init__.py │ │ ├── baserequesthandler.py │ │ └── main.py │ ├── mc/ │ │ ├── __init__.py │ │ └── cache.py │ ├── models.py │ ├── queue.yaml │ ├── services.py │ ├── settings.py │ ├── static_dev/ │ │ ├── .htaccess │ │ ├── 404.html │ │ ├── README.md │ │ ├── build/ │ │ │ ├── build.xml │ │ │ ├── config/ │ │ │ │ ├── default.properties │ │ │ │ ├── manifest.appcache │ │ │ │ └── project.properties │ │ │ ├── createproject.sh │ │ │ ├── runbuildscript.bat │ │ │ └── tools/ │ │ │ ├── ant-contrib-1.0b3.jar │ │ │ ├── csslint-rhino.js │ │ │ ├── fulljshint.js │ │ │ ├── fulljslint.js │ │ │ ├── htmlcompressor-1.4.3.jar │ │ │ ├── optipng-0.6.4-exe/ │ │ │ │ └── LICENSE.txt │ │ │ ├── rhino.jar │ │ │ └── yuicompressor-2.4.5.jar │ │ ├── crossdomain.xml │ │ ├── css/ │ │ │ ├── custom.css │ │ │ ├── fonts.css │ │ │ ├── libs/ │ │ │ │ └── openid.css │ │ │ ├── normalize.css │ │ │ └── style.css │ │ ├── demo/ │ │ │ ├── elements.html │ │ │ ├── hack.css │ │ │ ├── hack2.css │ │ │ └── tests.html │ │ ├── humans.txt │ │ ├── img/ │ │ │ └── .gitignore │ │ ├── index.html │ │ ├── js/ │ │ │ ├── libs/ │ │ │ │ ├── dd_belatedpng.js │ │ │ │ ├── jquery-1.5.1.js │ │ │ │ └── jquery-1.6.2.js │ │ │ ├── mylibs/ │ │ │ │ ├── .gitignore │ │ │ │ └── openid-en.js │ │ │ ├── plugins.js │ │ │ └── script.js │ │ ├── robots.txt │ │ └── test/ │ │ ├── index.html │ │ ├── qunit/ │ │ │ ├── qunit.css │ │ │ └── qunit.js │ │ └── tests.js │ ├── templates/ │ │ ├── account.html │ │ ├── account_setup.html │ │ ├── footer.html │ │ ├── header.html │ │ ├── index.html │ │ └── login.html │ └── tools/ │ ├── __init__.py │ ├── common.py │ ├── decorators.py │ └── mailchimp.py └── upload_to_appengine.sh
SYMBOL INDEX (355 symbols across 17 files)
FILE: app/app.py
function main (line 26) | def main():
FILE: app/common/templateaddons.py
function truncate_chars (line 22) | def truncate_chars(value, maxlen):
FILE: app/handlers/baserequesthandler.py
class BaseRequestHandler (line 14) | class BaseRequestHandler(webapp.RequestHandler):
method __init__ (line 21) | def __init__(self):
method render (line 25) | def render(self, template_name, template_values={}):
method head (line 42) | def head(self, *args):
FILE: app/handlers/main.py
class LogIn (line 22) | class LogIn(BaseRequestHandler):
method get (line 27) | def get(self):
class LogOut (line 43) | class LogOut(webapp.RequestHandler):
method get (line 44) | def get(self):
class Main (line 50) | class Main(BaseRequestHandler):
method get (line 51) | def get(self):
class Account (line 57) | class Account(BaseRequestHandler):
method get (line 63) | def get(self):
class AccountSetup (line 83) | class AccountSetup(BaseRequestHandler):
method post (line 85) | def post(self):
FILE: app/mc/cache.py
function get_someitems (line 8) | def get_someitems(clear=False):
function get_userprefs (line 28) | def get_userprefs(user, clear=False):
FILE: app/models.py
class UserPrefs (line 11) | class UserPrefs(db.Model):
method from_user (line 60) | def from_user(user):
method _from_user (line 69) | def _from_user(user):
method put (line 112) | def put(self):
method delete (line 127) | def delete(self):
method _clear_cache (line 134) | def _clear_cache(self):
class YourCustomModel (line 142) | class YourCustomModel(db.Model):
FILE: app/services.py
class Cron1 (line 20) | class Cron1(webapp.RequestHandler):
method get (line 21) | def get(self):
class Cron1_Worker1 (line 30) | class Cron1_Worker1(webapp.RequestHandler):
method post (line 31) | def post(self, key):
function main (line 62) | def main():
FILE: app/static_dev/build/tools/csslint-rhino.js
function EventTarget (line 59) | function EventTarget(){
function StringReader (line 147) | function StringReader(text){
function SyntaxError (line 416) | function SyntaxError(message, line, col){
function SyntaxUnit (line 452) | function SyntaxUnit(text, line, col){
function TokenStreamBase (line 523) | function TokenStreamBase(input, tokenData){
function Combinator (line 1105) | function Combinator(text, line, col){
function MediaFeature (line 1317) | function MediaFeature(name, value){
function MediaQuery (line 1351) | function MediaQuery(modifier, mediaType, features, line, col){
function Parser (line 1393) | function Parser(options){
function PropertyName (line 3327) | function PropertyName(text, hack, line, col){
function PropertyValue (line 3355) | function PropertyValue(parts, line, col){
function PropertyValuePart (line 3382) | function PropertyValuePart(text, line, col){
function Selector (line 3526) | function Selector(parts, line, col){
function SelectorPart (line 3558) | function SelectorPart(elementName, modifiers, text, line, col){
function SelectorSubPart (line 3595) | function SelectorSubPart(text, type, line, col){
function isHexDigit (line 3629) | function isHexDigit(c){
function isDigit (line 3633) | function isDigit(c){
function isWhitespace (line 3637) | function isWhitespace(c){
function isNewLine (line 3641) | function isNewLine(c){
function isNameStart (line 3645) | function isNameStart(c){
function isNameChar (line 3649) | function isNameChar(c){
function isIdentStart (line 3653) | function isIdentStart(c){
function mix (line 3657) | function mix(receiver, supplier){
function TokenStream (line 3678) | function TokenStream(input){
function f (line 6409) | function f(){}
function xmlEscape (line 6933) | function xmlEscape(text){
function serializeToXML (line 6970) | function serializeToXML(results){
function serializeToJUnitXML (line 7009) | function serializeToJUnitXML(results){
function serializeToTAP (line 7088) | function serializeToTAP(results){
function simulateKeyEvent (line 7314) | function simulateKeyEvent(target /*:HTMLElement*/, type /*:String*/,
function simulateMouseEvent (line 7517) | function simulateMouseEvent(target /*:HTMLElement*/, type /*:String*/,
function simulateUIEvent (line 7717) | function simulateUIEvent(target /*:HTMLElement*/, type /*:String*/,
function inGroups (line 8382) | function inGroups(testGroups, filter){
function TestNode (line 8405) | function TestNode(testObject){
function TestRunner (line 8488) | function TestRunner(){
function Reporter (line 9525) | function Reporter(lines){
function mix (line 9664) | function mix(reciever, supplier){
function indexOf (line 9682) | function indexOf(values, value){
function reportProperty (line 10074) | function reportProperty(name, display, msg){
function startRule (line 10102) | function startRule(event){
function startRule (line 10734) | function startRule(){
function endRule (line 10740) | function endRule(event){
function gatherRules (line 10994) | function gatherRules(options){
function outputHelp (line 11030) | function outputHelp(){
function processFiles (line 11042) | function processFiles(files, options){
function getFiles (line 11074) | function getFiles(dir) {
FILE: app/static_dev/build/tools/fulljshint.js
function F (line 997) | function F() {} // Used by Object.create
function is_own (line 999) | function is_own(object, name) {
function combine (line 1093) | function combine(t, o) {
function assume (line 1102) | function assume() {
function quit (line 1128) | function quit(m, l, ch) {
function warning (line 1138) | function warning(m, t, a, b, c, d) {
function warningAt (line 1169) | function warningAt(m, l, ch, a, b, c, d) {
function error (line 1176) | function error(m, t, a, b, c, d) {
function errorAt (line 1181) | function errorAt(m, l, ch, a, b, c, d) {
function nextLine (line 1197) | function nextLine() {
function it (line 1222) | function it(type, value) {
function match (line 1313) | function match(x) {
function string (line 1326) | function string(x) {
function addlabel (line 1935) | function addlabel(t, type) {
function doOption (line 1965) | function doOption() {
function peek (line 2070) | function peek(p) {
function advance (line 2087) | function advance(id, t) {
function expression (line 2157) | function expression(rbp, initial) {
function adjacent (line 2204) | function adjacent(left, right) {
function nobreak (line 2214) | function nobreak(left, right) {
function nospace (line 2222) | function nospace(left, right) {
function nonadjacent (line 2232) | function nonadjacent(left, right) {
function nobreaknonadjacent (line 2243) | function nobreaknonadjacent(left, right) {
function indentation (line 2258) | function indentation(bias) {
function nolinebreak (line 2270) | function nolinebreak(t) {
function comma (line 2278) | function comma() {
function symbol (line 2294) | function symbol(s, p) {
function delim (line 2307) | function delim(s) {
function stmt (line 2312) | function stmt(s, f) {
function blockstmt (line 2320) | function blockstmt(s, f) {
function reserveName (line 2327) | function reserveName(x) {
function prefix (line 2336) | function prefix(s, f) {
function type (line 2356) | function type(s, f) {
function reserve (line 2364) | function reserve(s, f) {
function reservevar (line 2371) | function reservevar(s, v) {
function infix (line 2381) | function infix(s, f, p, w) {
function relation (line 2401) | function relation(s, f) {
function isPoorRelation (line 2426) | function isPoorRelation(node) {
function assignop (line 2437) | function assignop(s, f) {
function bitwise (line 2482) | function bitwise(s, f, p) {
function bitwiseassignop (line 2497) | function bitwiseassignop(s) {
function suffix (line 2523) | function suffix(s, f) {
function optionalidentifier (line 2539) | function optionalidentifier() {
function identifier (line 2553) | function identifier() {
function reachable (line 2567) | function reachable(s) {
function statement (line 2591) | function statement(noindent) {
function use_strict (line 2653) | function use_strict() {
function statements (line 2670) | function statements(begin) {
function block (line 2751) | function block(ordinary, stmt) {
function countMember (line 2804) | function countMember(m) {
function note_implied (line 2816) | function note_implied(token) {
function cssName (line 2832) | function cssName() {
function cssNumber (line 2840) | function cssNumber() {
function cssString (line 2853) | function cssString() {
function cssColor (line 2861) | function cssColor() {
function cssLength (line 2920) | function cssLength() {
function cssLineHeight (line 2942) | function cssLineHeight() {
function cssWidth (line 2960) | function cssWidth() {
function cssMargin (line 2975) | function cssMargin() {
function cssAttr (line 2986) | function cssAttr() {
function cssCommaList (line 3002) | function cssCommaList() {
function cssCounter (line 3016) | function cssCounter() {
function cssShape (line 3063) | function cssShape() {
function cssUrl (line 3082) | function cssUrl() {
function styleAttribute (line 3335) | function styleAttribute() {
function styleValue (line 3377) | function styleValue(v) {
function styleChild (line 3443) | function styleChild() {
function substyle (line 3470) | function substyle() {
function styleSelector (line 3510) | function styleSelector() {
function stylePattern (line 3629) | function stylePattern() {
function stylelist (line 3646) | function stylelist() {
function styles (line 3661) | function styles() {
function doBegin (line 3708) | function doBegin(n) {
function doAttribute (line 3734) | function doAttribute(n, a, v) {
function doTag (line 3796) | function doTag(n, a) {
function closetag (line 3919) | function closetag(n) {
function html (line 3923) | function html() {
function property_name (line 4742) | function property_name() {
function functionparams (line 4763) | function functionparams() {
function doFunction (line 4787) | function doFunction(i, statement) {
function jsonValue (line 5376) | function jsonValue() {
function detail (line 5722) | function detail(h, array) {
FILE: app/static_dev/build/tools/fulljslint.js
function return_this (line 1224) | function return_this() {
function F (line 1228) | function F() {} // Used by Object.create
function combine (line 1329) | function combine(a, b) {
function assume (line 1338) | function assume() {
function quit (line 1364) | function quit(message, line, character) {
function warn (line 1376) | function warn(message, offender, a, b, c, d) {
function warn_at (line 1404) | function warn_at(message, line, character, a, b, c, d) {
function fail (line 1411) | function fail(message, offender, a, b, c, d) {
function fail_at (line 1416) | function fail_at(message, line, character, a, b, c, d) {
function expected_at (line 1423) | function expected_at(at) {
function aint (line 1430) | function aint(it, name, expected) {
function collect_comment (line 1447) | function collect_comment(comment, quote, line, at) {
function next_line (line 1470) | function next_line() {
function it (line 1495) | function it(type, value, quote) {
function match (line 1589) | function match(x) {
function string (line 1602) | function string(x) {
function add_label (line 2226) | function add_label(symbol, type) {
function peek (line 2259) | function peek(distance) {
function discard (line 2278) | function discard(it) {
function advance (line 2299) | function advance(id, match) {
function directive (line 2399) | function directive() {
function edge (line 2550) | function edge(mode) {
function step_in (line 2555) | function step_in(mode) {
function step_out (line 2587) | function step_out(id, symbol) {
function one_space (line 2602) | function one_space(left, right) {
function one_space_only (line 2612) | function one_space_only(left, right) {
function no_space (line 2621) | function no_space(left, right) {
function no_space_only (line 2630) | function no_space_only(left, right) {
function spaces (line 2639) | function spaces(left, right) {
function comma (line 2649) | function comma() {
function semicolon (line 2663) | function semicolon() {
function use_strict (line 2678) | function use_strict() {
function are_similar (line 2696) | function are_similar(a, b) {
function expression (line 2759) | function expression(rbp, initial) {
function symbol (line 2808) | function symbol(s, p) {
function delim (line 2821) | function delim(s) {
function postscript (line 2826) | function postscript(x) {
function ultimate (line 2831) | function ultimate(s) {
function stmt (line 2842) | function stmt(s, f) {
function labeled_stmt (line 2849) | function labeled_stmt(s, f) {
function disrupt_stmt (line 2854) | function disrupt_stmt(s, f) {
function reserve_name (line 2860) | function reserve_name(x) {
function prefix (line 2869) | function prefix(s, f) {
function type (line 2894) | function type(s, arity, nud) {
function reserve (line 2904) | function reserve(s, f) {
function reservevar (line 2914) | function reservevar(s, v) {
function infix (line 2924) | function infix(s, p, f, w) {
function expected_relation (line 2944) | function expected_relation(node, message) {
function expected_condition (line 2951) | function expected_condition(node, message) {
function check_relation (line 2977) | function check_relation(node) {
function relation (line 3003) | function relation(s, eqeq) {
function assignop (line 3023) | function assignop(s, bit) {
function bitwise (line 3075) | function bitwise(s, p) {
function suffix (line 3087) | function suffix(s, f) {
function optional_identifier (line 3105) | function optional_identifier() {
function identifier (line 3118) | function identifier() {
function statement (line 3131) | function statement(no_indent) {
function statements (line 3210) | function statements() {
function block (line 3240) | function block(ordinary) {
function tally_property (line 3283) | function tally_property(name) {
function note_implied (line 3295) | function note_implied(token) {
function paren_check (line 3522) | function paren_check(that) {
function property_name (line 4051) | function property_name() {
function function_params (line 4074) | function function_params() {
function do_function (line 4103) | function do_function(func, name) {
function json_value (line 4795) | function json_value() {
function css_name (line 4882) | function css_name() {
function css_number (line 4890) | function css_number() {
function css_string (line 4902) | function css_string() {
function css_color (line 4909) | function css_color() {
function css_length (line 4965) | function css_length() {
function css_line_height (line 4985) | function css_line_height() {
function css_width (line 5003) | function css_width() {
function css_margin (line 5018) | function css_margin() {
function css_attr (line 5029) | function css_attr() {
function css_comma_list (line 5044) | function css_comma_list() {
function css_counter (line 5057) | function css_counter() {
function css_shape (line 5100) | function css_shape() {
function css_url (line 5118) | function css_url() {
function style_attribute (line 5370) | function style_attribute() {
function style_value (line 5408) | function style_value(v) {
function style_child (line 5474) | function style_child() {
function substyle (line 5498) | function substyle() {
function style_selector (line 5537) | function style_selector() {
function style_pattern (line 5648) | function style_pattern() {
function style_list (line 5664) | function style_list() {
function styles (line 5679) | function styles() {
function do_begin (line 5724) | function do_begin(n) {
function do_attribute (line 5748) | function do_attribute(n, a, v) {
function do_tag (line 5808) | function do_tag(name, attribute) {
function closetag (line 5966) | function closetag(name) {
function html (line 5970) | function html() {
function detail (line 6470) | function detail(h, array) {
FILE: app/static_dev/js/libs/jquery-1.5.1.js
function jQuerySubclass (line 994) | function jQuerySubclass( selector, context ) {
function doScrollCheck (line 1069) | function doScrollCheck() {
function dataAttr (line 1596) | function dataAttr( elem, key, data ) {
function isEmptyDataObject (line 1626) | function isEmptyDataObject( obj ) {
function returnFalse (line 2726) | function returnFalse() {
function returnTrue (line 2729) | function returnTrue() {
function trigger (line 2977) | function trigger( type, elem, args ) {
function handler (line 3004) | function handler( e ) {
function liveHandler (line 3188) | function liveHandler( event ) {
function liveConvert (line 3271) | function liveConvert( type, selector ) {
function dirNodeCheck (line 4558) | function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
function dirCheck (line 4591) | function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
function isDisconnected (line 4848) | function isDisconnected( node ) {
function winnow (line 4970) | function winnow( elements, qualifier, keep ) {
function root (line 5341) | function root( elem, cur ) {
function cloneCopyEvent (line 5348) | function cloneCopyEvent( src, dest ) {
function cloneFixAttributes (line 5377) | function cloneFixAttributes(src, dest) {
function getAll (line 5491) | function getAll( elem ) {
function evalScript (line 5688) | function evalScript( i, elem ) {
function getWH (line 6004) | function getWH( elem, name, extra ) {
function addToPrefiltersOrTransports (line 6108) | function addToPrefiltersOrTransports( structure ) {
function inspectPrefiltersOrTransports (line 6144) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
function done (line 6508) | function done( status, statusText, responses, headers ) {
function buildParams (line 6817) | function buildParams( prefix, obj, traditional, add ) {
function ajaxHandleResponses (line 6874) | function ajaxHandleResponses( s, jqXHR, responses ) {
function ajaxConvert (line 6939) | function ajaxConvert( s, response ) {
function xhrOnUnloadAbort (line 7204) | function xhrOnUnloadAbort() {
function createStandardXHR (line 7214) | function createStandardXHR() {
function createActiveXHR (line 7220) | function createActiveXHR() {
function genFx (line 7664) | function genFx( type, num ) {
function t (line 7773) | function t( gotoEnd ) {
function defaultDisplay (line 7925) | function defaultDisplay( nodeName ) {
function getWindow (line 8239) | function getWindow( elem ) {
FILE: app/static_dev/js/libs/jquery-1.6.2.js
function jQuerySub (line 863) | function jQuerySub( selector, context ) {
function doScrollCheck (line 929) | function doScrollCheck() {
function resolveFunc (line 1119) | function resolveFunc( i ) {
function dataAttr (line 1689) | function dataAttr( elem, key, data ) {
function isEmptyDataObject (line 1721) | function isEmptyDataObject( obj ) {
function handleQueueMarkDefer (line 1734) | function handleQueueMarkDefer( elem, type, src ) {
function resolve (line 1884) | function resolve() {
function returnFalse (line 3133) | function returnFalse() {
function returnTrue (line 3136) | function returnTrue() {
function trigger (line 3378) | function trigger( type, elem, args ) {
function handler (line 3413) | function handler( donor ) {
function liveHandler (line 3617) | function liveHandler( event ) {
function liveConvert (line 3705) | function liveConvert( type, selector ) {
function dirNodeCheck (line 5020) | function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
function dirCheck (line 5053) | function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
function isDisconnected (line 5328) | function isDisconnected( node ) {
function winnow (line 5450) | function winnow( elements, qualifier, keep ) {
function root (line 5830) | function root( elem, cur ) {
function cloneCopyEvent (line 5837) | function cloneCopyEvent( src, dest ) {
function cloneFixAttributes (line 5866) | function cloneFixAttributes( src, dest ) {
function getAll (line 5998) | function getAll( elem ) {
function fixDefaultChecked (line 6011) | function fixDefaultChecked( elem ) {
function findInputs (line 6017) | function findInputs( elem ) {
function evalScript (line 6233) | function evalScript( i, elem ) {
function getWH (line 6560) | function getWH( elem, name, extra ) {
function addToPrefiltersOrTransports (line 6683) | function addToPrefiltersOrTransports( structure ) {
function inspectPrefiltersOrTransports (line 6719) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
function done (line 7085) | function done( status, statusText, responses, headers ) {
function buildParams (line 7397) | function buildParams( prefix, obj, traditional, add ) {
function ajaxHandleResponses (line 7447) | function ajaxHandleResponses( s, jqXHR, responses ) {
function ajaxConvert (line 7512) | function ajaxConvert( s, response ) {
function createStandardXHR (line 7778) | function createStandardXHR() {
function createActiveXHR (line 7784) | function createActiveXHR() {
function createFxNow (line 8266) | function createFxNow() {
function clearFxNow (line 8271) | function clearFxNow() {
function genFx (line 8276) | function genFx( type, num ) {
function t (line 8387) | function t( gotoEnd ) {
function defaultDisplay (line 8555) | function defaultDisplay( nodeName ) {
function getWindow (line 8901) | function getWindow( elem ) {
FILE: app/static_dev/js/plugins.js
function c (line 15) | function c(){}
FILE: app/static_dev/test/qunit/qunit.js
function done (line 562) | function done() {
function validTest (line 613) | function validTest( name ) {
function escapeHtml (line 641) | function escapeHtml(s) {
function push (line 655) | function push(result, actual, expected, message) {
function synchronize (line 673) | function synchronize( callback ) {
function process (line 681) | function process() {
function saveGlobal (line 695) | function saveGlobal() {
function checkPollution (line 705) | function checkPollution( name ) {
function diff (line 723) | function diff( a, b ) {
function fail (line 737) | function fail(message, exception, callback) {
function extend (line 748) | function extend(a, b) {
function addEvent (line 756) | function addEvent(elem, type, fn) {
function id (line 766) | function id(name) {
function bindCallbacks (line 782) | function bindCallbacks(o, callbacks, args) {
function useStrictEquality (line 796) | function useStrictEquality(b, a) {
function quote (line 947) | function quote( str ) {
function literal (line 950) | function literal( o ) {
function join (line 953) | function join( pre, arr, post ) {
function array (line 963) | function array( arr ) {
function getText (line 1109) | function getText( elems ) {
function diff (line 1143) | function diff(o, n){
FILE: app/tools/common.py
function is_testenv (line 8) | def is_testenv():
function decode (line 18) | def decode(var):
function slugify (line 25) | def slugify(value):
FILE: app/tools/decorators.py
function login_required (line 5) | def login_required(func):
FILE: app/tools/mailchimp.py
function mailchimp_subscribe (line 11) | def mailchimp_subscribe(email, list_id=None, double_optin=True):
function mailchimp_unsubscribe (line 24) | def mailchimp_unsubscribe(email, list_id=None, delete_member=False,
class MailSnake (line 40) | class MailSnake(object):
method __init__ (line 46) | def __init__(self, apikey='', extra_params={}):
method call (line 60) | def call(self, method, params={}):
method __getattr__ (line 71) | def __getattr__(self, method_name):
Condensed preview — 69 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,630K chars).
[
{
"path": ".gitignore",
"chars": 57,
"preview": "*.pyc\napp/static_dev/publish\napp/static_dev/intermediate\n"
},
{
"path": "README.md",
"chars": 5884,
"preview": "App Engine Boilerplate is a versatile yet minimalistic setup for new App Engine projects.\n\n* [html5-boilerplate 2.0](htt"
},
{
"path": "app/app.py",
"chars": 700,
"preview": "# -*- coding: utf-8 -*-\nimport os\nfrom google.appengine.dist import use_library\nos.environ['DJANGO_SETTINGS_MODULE'] = '"
},
{
"path": "app/app.yaml",
"chars": 1540,
"preview": "# Replace 'ae-boilerplate' with your application name\napplication: ae-boilerplate\nversion: 1 \nruntime: python\napi_versio"
},
{
"path": "app/common/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "app/common/templateaddons.py",
"chars": 927,
"preview": "# -*- coding: utf-8 -*-\nfrom google.appengine.ext import webapp\nfrom django.template import Node\n\n\"\"\"\nCustom template ta"
},
{
"path": "app/cron.yaml",
"chars": 196,
"preview": "cron:\n#- description: hourly 1\n# url: /services/task1\n# schedule: every 1 hours synchronized\n# \n#- description: every"
},
{
"path": "app/handlers/__init__.py",
"chars": 19,
"preview": "from main import *\n"
},
{
"path": "app/handlers/baserequesthandler.py",
"chars": 1441,
"preview": "# -*- coding: utf-8 -*-\nimport os\nfrom google.appengine.api import users\nfrom google.appengine.ext import webapp\n\nimport"
},
{
"path": "app/handlers/main.py",
"chars": 3978,
"preview": "# -*- coding: utf-8 -*-\nimport os\nimport logging\n\nfrom hashlib import md5\nfrom google.appengine.ext import db\nfrom googl"
},
{
"path": "app/mc/__init__.py",
"chars": 13,
"preview": "import cache\n"
},
{
"path": "app/mc/cache.py",
"chars": 1556,
"preview": "# -*- coding: utf-8 -*-\nimport logging\nfrom google.appengine.api import memcache\n\nimport models\n\n\ndef get_someitems(clea"
},
{
"path": "app/models.py",
"chars": 5303,
"preview": "# -*- coding: utf-8 -*-\nimport logging\n\nfrom hashlib import md5\nfrom google.appengine.ext import db\nfrom google.appengin"
},
{
"path": "app/queue.yaml",
"chars": 485,
"preview": "# Task queue configuration\n# http://code.google.com/appengine/docs/python/config/queue.html\n#\nqueue:\n#- name: fooqueue\n#"
},
{
"path": "app/services.py",
"chars": 1816,
"preview": "# -*- coding: utf-8 -*-\nimport os\nfrom google.appengine.dist import use_library\nos.environ['DJANGO_SETTINGS_MODULE'] = '"
},
{
"path": "app/settings.py",
"chars": 409,
"preview": "#\n# Update the settings as needed\n#\nTEMPLATE_DIR = \"templates/\"\n\n# MailChimp settings to subscribe users after signup\nMA"
},
{
"path": "app/static_dev/.htaccess",
"chars": 16857,
"preview": "# Apache configuration file\n# httpd.apache.org/docs/2.2/mod/quickreference.html\n\n# Note .htaccess files are an overhead,"
},
{
"path": "app/static_dev/404.html",
"chars": 1356,
"preview": "<!doctype html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>Page Not Found :(</title> \n <style>\n\t body { text-alig"
},
{
"path": "app/static_dev/README.md",
"chars": 30205,
"preview": "# HTML5 Boilerplate [http://html5boilerplate.com](http://html5boilerplate.com)\n\n## Changelog:\n\n### v.2.0 : August 10th,"
},
{
"path": "app/static_dev/build/build.xml",
"chars": 45297,
"preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE project>\n<project name=\"Boilerplate Build\" default=\"build\" basedir=\"../\"> <!-- one back"
},
{
"path": "app/static_dev/build/config/default.properties",
"chars": 3481,
"preview": "#\n# Default Build Settings\n# you can override these settings on a project basis in a project.properties file\n# so probab"
},
{
"path": "app/static_dev/build/config/manifest.appcache",
"chars": 109,
"preview": "CACHE MANIFEST\n\n# version xxxxxxxxx\n\nCACHE:\n# html files\n\n\n# css files\n\n\n\n# js files\n\n\n\nFALLBACK:\n\nNETWORK:\n*"
},
{
"path": "app/static_dev/build/config/project.properties",
"chars": 2978,
"preview": "# project.properties file defines overrides for default.properties\n\n# Explanation: This file should be created by each u"
},
{
"path": "app/static_dev/build/createproject.sh",
"chars": 1119,
"preview": "#!/usr/bin/env bash\n\n#Generate a new project from your HTML5 Boilerplate repo clone\n#by: Rick Waldron & Michael Cetrulo\n"
},
{
"path": "app/static_dev/build/runbuildscript.bat",
"chars": 141,
"preview": "# This is for windows users only.\n# If you're on a mac or linux, just run `ant build` from this folder in Terminal\n\nset "
},
{
"path": "app/static_dev/build/tools/csslint-rhino.js",
"chars": 371724,
"preview": "/*! \nCSSLint\nCopyright (c) 2011 Nicole Sullivan and Nicholas C. Zakas. All rights reserved.\n\nPermission is hereby grante"
},
{
"path": "app/static_dev/build/tools/fulljshint.js",
"chars": 208516,
"preview": "/*\n * JSHint, by JSHint Community.\n *\n * Licensed under the same slightly modified MIT license that JSLint is.\n * It sto"
},
{
"path": "app/static_dev/build/tools/fulljslint.js",
"chars": 240588,
"preview": "/*global quit:false, readFile: false */\n\n// Rhino Edition\n\n\n// jslint.js\n// 2011-03-29\n\n// Copyright (c) 2002 Douglas Cr"
},
{
"path": "app/static_dev/build/tools/optipng-0.6.4-exe/LICENSE.txt",
"chars": 865,
"preview": "\nCopyright (C) 2001-2010 Cosmin Truta.\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In "
},
{
"path": "app/static_dev/crossdomain.xml",
"chars": 797,
"preview": "<?xml version=\"1.0\"?>\n<!DOCTYPE cross-domain-policy SYSTEM \"http://www.adobe.com/xml/dtds/cross-domain-policy.dtd\">\n<cro"
},
{
"path": "app/static_dev/css/custom.css",
"chars": 3501,
"preview": "/**\n * Place custom styles in this file. Right now these are the presets for\n * appengine-boilerplate.com\n *\n * Author: "
},
{
"path": "app/static_dev/css/fonts.css",
"chars": 138,
"preview": "/* Add your custom web fonts here. http://www.google.com/webfonts */\n@import url(http://fonts.googleapis.com/css?family="
},
{
"path": "app/static_dev/css/libs/openid.css",
"chars": 1008,
"preview": "/*\n\tSimple OpenID Plugin\n\thttp://code.google.com/p/openid-selector/\n\t\n\tThis code is licensed under the New BSD License.\n"
},
{
"path": "app/static_dev/css/normalize.css",
"chars": 9855,
"preview": "/* \n * HTML5 ✰ Boilerplate\n *\n * What follows is the result of much research on cross-browser styling. \n * Credit left i"
},
{
"path": "app/static_dev/css/style.css",
"chars": 116,
"preview": "@import url(\"fonts.css\");\n@import url(\"normalize.css\");\n@import url(\"libs/openid.css\");\n@import url(\"custom.css\");\n\n"
},
{
"path": "app/static_dev/demo/elements.html",
"chars": 18235,
"preview": "<!doctype html> \n<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->\n<!--[if lt IE 7]> <ht"
},
{
"path": "app/static_dev/demo/hack.css",
"chars": 2640,
"preview": "/* \n style.css contains a reset, font normalization and some base styles.\n \n credit is left where credit is due.\n ad"
},
{
"path": "app/static_dev/demo/hack2.css",
"chars": 1493,
"preview": "body { font:13px/1.231 sans-serif; *font-size:small; } /* hack retained to preserve specificity */\n\t\tbody, select, input"
},
{
"path": "app/static_dev/demo/tests.html",
"chars": 11623,
"preview": "<!doctype html>\n<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->\n<!--[if lt IE 7]> <html"
},
{
"path": "app/static_dev/humans.txt",
"chars": 1647,
"preview": "/* the humans responsible & colophon */\n/* humanstxt.org */\n\n\n/* TEAM */\n <your title>: <your name>\n Site: \n Twitter:"
},
{
"path": "app/static_dev/img/.gitignore",
"chars": 13,
"preview": "!.gitignore\n\n"
},
{
"path": "app/static_dev/index.html",
"chars": 3440,
"preview": "<!doctype html>\n<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->\n<!--[if lt IE 7]> <html"
},
{
"path": "app/static_dev/js/libs/dd_belatedpng.js",
"chars": 7019,
"preview": "/**\n* DD_belatedPNG: Adds IE6 support: PNG images for CSS background-image and HTML <IMG/>.\n* Author: Drew Diller\n* Emai"
},
{
"path": "app/static_dev/js/libs/jquery-1.5.1.js",
"chars": 216839,
"preview": "/*!\n * jQuery JavaScript Library v1.5.1\n * http://jquery.com/\n *\n * Copyright 2011, John Resig\n * Dual licensed under th"
},
{
"path": "app/static_dev/js/libs/jquery-1.6.2.js",
"chars": 236202,
"preview": "/*!\n * jQuery JavaScript Library v1.6.2\n * http://jquery.com/\n *\n * Copyright 2011, John Resig\n * Dual licensed under th"
},
{
"path": "app/static_dev/js/mylibs/.gitignore",
"chars": 13,
"preview": "!.gitignore\n\n"
},
{
"path": "app/static_dev/js/mylibs/openid-en.js",
"chars": 7911,
"preview": "\n/*\n\tSimple OpenID Plugin\n\thttp://code.google.com/p/openid-selector/\n\t\n\tThis code is licensed under the New BSD License."
},
{
"path": "app/static_dev/js/plugins.js",
"chars": 939,
"preview": "\n// usage: log('inside coolFunc', this, arguments);\n// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/\nwind"
},
{
"path": "app/static_dev/js/script.js",
"chars": 39,
"preview": "/* Author: \n\n*/\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
},
{
"path": "app/static_dev/robots.txt",
"chars": 107,
"preview": "# www.robotstxt.org/\n# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449\n\nUser-agent: *\n\n"
},
{
"path": "app/static_dev/test/index.html",
"chars": 796,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>QUnit Tests</title>\n\t<link rel=\"stylesheet\" href=\"qunit/qunit.css\" media"
},
{
"path": "app/static_dev/test/qunit/qunit.css",
"chars": 3273,
"preview": "/** Font Family and Sizes */\n\n#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #"
},
{
"path": "app/static_dev/test/qunit/qunit.js",
"chars": 33208,
"preview": "/*\n * QUnit - A JavaScript Unit Testing Framework\n * \n * http://docs.jquery.com/QUnit\n *\n * Copyright (c) 2009 John Resi"
},
{
"path": "app/static_dev/test/tests.js",
"chars": 776,
"preview": "\n// documentation on writing tests here: http://docs.jquery.com/QUnit\n// example tests: https://github.com/jquery/qunit/"
},
{
"path": "app/templates/account.html",
"chars": 724,
"preview": "{% extends \"base.html\" %}\n\n{% block main %}\n<h2>Account</h2>\n\n\n<form action=\"/account/setup\" method=\"post\">\n <input t"
},
{
"path": "app/templates/account_setup.html",
"chars": 748,
"preview": "{% extends \"base.html\" %}\n\n{% block main %}\n<h2>Account Setup</h2>\n\n<form action=\"/account/setup\" method=\"post\">\n <in"
},
{
"path": "app/templates/footer.html",
"chars": 0,
"preview": ""
},
{
"path": "app/templates/header.html",
"chars": 60,
"preview": "<h1><a href=\"/\">App Engine <i>★</i> Boilerplate</a></h1> \n \n"
},
{
"path": "app/templates/index.html",
"chars": 4563,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}App Engine Boilerplate - A rock-solid default template for App Engine{% endb"
},
{
"path": "app/templates/login.html",
"chars": 1191,
"preview": "{% extends \"base.html\" %}\n\n{% block scripts %}\n\t<script> \n $(document).ready(function() {\n \topenid.init('o"
},
{
"path": "app/tools/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "app/tools/common.py",
"chars": 1075,
"preview": "# -*- coding: utf-8 -*-\nimport re\nimport logging\nimport unicodedata\nfrom os import environ\n\n\ndef is_testenv():\n \"\"\"\n "
},
{
"path": "app/tools/decorators.py",
"chars": 640,
"preview": "# -*- coding: utf-8 -*-\nfrom google.appengine.api import users\n\n\ndef login_required(func):\n \"\"\"You can use the @login"
},
{
"path": "app/tools/mailchimp.py",
"chars": 2599,
"preview": "import logging\nimport urllib2\nimport settings\n\ntry:\n import simplejson as json\nexcept ImportError:\n import json\n\n\n"
},
{
"path": "upload_to_appengine.sh",
"chars": 1116,
"preview": "#!/bin/bash\n\n# Point CMD_APPCFG to your local appengine-sdk/appcfg.py\nCMD_APPCFG=\"\"\n\n# Cache current directory\nDIR=$( pw"
}
]
// ... and 4 more files (download for full content)
About this extraction
This page contains the full source code of the metachris/appengine-boilerplate GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 69 files (1.5 MB), approximately 359.9k tokens, and a symbol index with 355 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.