move to middleman-templates

This commit is contained in:
Karl Freeman 2014-03-11 11:07:55 +00:00
parent 23b1b8464d
commit 772de85ce3
126 changed files with 153 additions and 107 deletions

View file

@ -1,4 +1,4 @@
require 'middleman-cli/templates'
require 'middleman-templates'
# CLI Module
module Middleman::Cli

View file

@ -1,99 +0,0 @@
# Use thor for template generation
require 'thor'
require 'thor/group'
# Templates namespace
module Middleman::Templates
# Static methods
class << self
# Get list of registered templates and add new ones
#
# Middleman::Templates.register(:ext_name, klass)
#
# @param [Symbol] name The name of the template
# @param [Class] klass The class to be executed for this template
# @return [Hash] List of registered templates
def register(name=nil, klass=nil)
@_template_mappings ||= {}
@_template_mappings[name] = klass if name && klass
@_template_mappings
end
# Middleman::Templates.register(name, klass)
alias :registered :register
end
# Base Template class. Handles basic options and paths.
class Base < ::Thor::Group
include Thor::Actions
def initialize(names, options)
super
source_paths << File.join(File.dirname(__FILE__), 'templates')
end
# The gemfile template to use. Individual templates can define this class
# method to override the template path.
def self.gemfile_template
'shared/Gemfile.tt'
end
# Required path for the new project to be generated
argument :location, :type => :string
# Name of the template being used to generate the project.
class_option :template, :default => 'default'
# Output a config.ru file for Rack if --rack is passed
class_option :rack, :type => :boolean, :default => false
# Write a Rack config.ru file for project
# @return [void]
def generate_rack!
return unless options[:rack]
template 'shared/config.ru', File.join(location, 'config.ru')
end
class_option :'skip-bundle', :type => :boolean, :default => false
class_option :'skip-gemfile', :type => :boolean, :default => false
# Write a Bundler Gemfile file for project
# @return [void]
def generate_bundler!
return if options[:'skip-gemfile']
template self.class.gemfile_template, File.join(location, 'Gemfile')
return if options[:'skip-bundle']
inside(location) do
::Middleman::Cli::Bundle.new.invoke(:bundle)
end unless ENV['TEST']
end
# Output a .gitignore file
class_option :'skip-git', :type => :boolean, :default => false
# Write a .gitignore file for project
# @return [void]
def generate_gitignore!
return if options[:'skip-git']
copy_file 'shared/gitignore', File.join(location, '.gitignore')
end
end
end
# Default template
require 'middleman-cli/templates/default'
# HTML5 template
require 'middleman-cli/templates/html5'
# HTML5 Mobile template
require 'middleman-cli/templates/mobile'
# Local templates
require 'middleman-cli/templates/local'
# Barebones template
require 'middleman-cli/templates/empty'

View file

@ -1,38 +0,0 @@
# Default Middleman template
class Middleman::Templates::Default < Middleman::Templates::Base
class_option 'css_dir',
:default => 'stylesheets',
:desc => 'The path to the css files'
class_option 'js_dir',
:default => 'javascripts',
:desc => 'The path to the javascript files'
class_option 'images_dir',
:default => 'images',
:desc => 'The path to the image files'
# Template files are relative to this file
# @return [String]
def self.source_root
File.dirname(__FILE__)
end
# Actually output the files
# @return [void]
def build_scaffold!
template 'shared/config.tt', File.join(location, 'config.rb')
copy_file 'default/source/index.html.erb', File.join(location, 'source/index.html.erb')
copy_file 'default/source/layouts/layout.erb', File.join(location, 'source/layouts/layout.erb')
empty_directory File.join(location, 'source', options[:css_dir])
copy_file 'default/source/stylesheets/all.css', File.join(location, 'source', options[:css_dir], 'all.css')
copy_file 'default/source/stylesheets/normalize.css', File.join(location, 'source', options[:css_dir], 'normalize.css')
empty_directory File.join(location, 'source', options[:js_dir])
copy_file 'default/source/javascripts/all.js', File.join(location, 'source', options[:js_dir], 'all.js')
empty_directory File.join(location, 'source', options[:images_dir])
copy_file 'default/source/images/background.png', File.join(location, 'source', options[:images_dir], 'background.png')
copy_file 'default/source/images/middleman.png', File.join(location, 'source', options[:images_dir], 'middleman.png')
end
end
# Register this template
Middleman::Templates.register(:default, Middleman::Templates::Default)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -1,10 +0,0 @@
---
title: Welcome to Middleman
---
<div class="welcome">
<h1>Middleman is Watching</h1>
<p class="doc">
<%= link_to "Read Online Documentation", "http://middlemanapp.com/" %>
</p><!-- .doc -->
</div><!-- .welcome -->

View file

@ -1,19 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<!-- Always force latest IE rendering engine or request Chrome Frame -->
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<!-- Use title if it's in the page YAML frontmatter -->
<title><%= data.page.title || "The Middleman" %></title>
<%= stylesheet_link_tag "normalize", "all" %>
<%= javascript_include_tag "all" %>
</head>
<body class="<%= page_classes %>">
<%= yield %>
</body>
</html>

View file

@ -1,55 +0,0 @@
@charset "utf-8";
body {
background: #d4d4d4 url("../images/background.png");
text-align: center;
font-family: sans-serif; }
h1 {
color: rgba(0, 0, 0, .3);
font-weight: bold;
font-size: 32px;
letter-spacing: -1px;
text-transform: uppercase;
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
background: url("../images/middleman.png") no-repeat center 100px;
padding: 350px 0 10px;
margin: 0; }
.doc {
font-size: 14px;
margin: 0; }
.doc:before,
.doc:after {
opacity: .2;
padding: 6px;
font-style: normal;
position: relative;
content: "•"; }
.doc a {
color: rgba(0, 0, 0, 0.3); }
.doc a:hover {
color: #666; }
.welcome {
-webkit-animation-name: welcome;
-webkit-animation-duration: .9s; }
@-webkit-keyframes welcome {
from {
-webkit-transform: scale(0);
opacity: 0;
}
50% {
-webkit-transform: scale(0);
opacity: 0;
}
82.5% {
-webkit-transform: scale(1.03);
-webkit-animation-timing-function: ease-out;
opacity: 1;
}
to {
-webkit-transform: scale(1);
}
}

View file

@ -1,375 +0,0 @@
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 8/9.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 8/9.
*/
audio,
canvas,
video {
display: inline-block;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 8/9.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Sets default font family to sans-serif.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Removes default margin.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Sets consistent quote types.
*/
q {
quotes: "\201C" "\201D" "\2018" "\2019";
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* Removes border when inside `a` element in IE 8/9.
*/
img {
border: 0;
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 8/9 and Safari 5.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 8/9.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Corrects font family not being inherited in all browsers.
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
*/
button,
input,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 2 */
margin: 0; /* 3 */
}
/*
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to `content-box` in IE 8/9.
* 2. Removes excess padding in IE 8/9.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

View file

@ -1,23 +0,0 @@
# A barebones template with nothing much in it
class Middleman::Templates::Empty < Middleman::Templates::Base
# Template files are relative to this file
# @return [String]
def self.source_root
File.dirname(__FILE__)
end
def self.gemfile_template
'empty/Gemfile.tt'
end
# Actually output the files
# @return [void]
def build_scaffold!
create_file File.join(location, 'config.rb'), "\n"
empty_directory File.join(location, 'source')
end
end
# Register this template
Middleman::Templates.register(:empty, Middleman::Templates::Empty)

View file

@ -1,6 +0,0 @@
source 'https://rubygems.org'
gem "middleman", "~><%= Middleman::VERSION %>"
# For faster file watcher updates on Windows:
gem "wdm", "~> 0.1.0", :platforms => [:mswin, :mingw]

View file

@ -1,30 +0,0 @@
# HTML5 Boilerplate template
class Middleman::Templates::Html5 < Middleman::Templates::Base
class_option 'css_dir',
:default => 'css',
:desc => 'The path to the css files'
class_option 'js_dir',
:default => 'js',
:desc => 'The path to the javascript files'
class_option 'images_dir',
:default => 'img',
:desc => 'The path to the image files'
# Templates are relative to this file
# @return [String]
def self.source_root
File.dirname(__FILE__)
end
# Output the files
# @return [void]
def build_scaffold!
template 'shared/config.tt', File.join(location, 'config.rb')
directory 'html5/source', File.join(location, 'source')
empty_directory File.join(location, 'source')
end
end
# Register the template
Middleman::Templates.register(:html5, Middleman::Templates::Html5)

View file

@ -1,540 +0,0 @@
# 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
# ----------------------------------------------------------------------
# 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_headers.c>
Header set X-UA-Compatible "IE=Edge,chrome=1"
# mod_headers can't match by content-type, but we don't want to send this header on *everything*...
<FilesMatch "\.(js|css|gif|png|jpe?g|pdf|xml|oga|ogg|m4a|ogv|mp4|m4v|webm|svg|svgz|eot|ttf|otf|woff|ico|webp|appcache|manifest|htc|crx|oex|xpi|safariextz|vcf)$" >
Header unset X-UA-Compatible
</FilesMatch>
</IfModule>
# ----------------------------------------------------------------------
# Cross-domain AJAX requests
# ----------------------------------------------------------------------
# Serve cross-domain Ajax requests, disabled by default.
# enable-cors.org
# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# <IfModule mod_headers.c>
# Header set Access-Control-Allow-Origin "*"
# </IfModule>
# ----------------------------------------------------------------------
# CORS-enabled images (@crossorigin)
# ----------------------------------------------------------------------
# Send CORS headers if browsers request them; enabled by default for images.
# developer.mozilla.org/en/CORS_Enabled_Image
# blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
# hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
# wiki.mozilla.org/Security/Reviews/crossoriginAttribute
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
# mod_headers, y u no match by Content-Type?!
<FilesMatch "\.(gif|png|jpe?g|svg|svgz|ico|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
# ----------------------------------------------------------------------
# Webfont access
# ----------------------------------------------------------------------
# Allow access from all domains for webfonts.
# Alternatively you could only whitelist your
# subdomains like "subdomain.example.com".
<IfModule mod_headers.c>
<FilesMatch "\.(ttf|ttc|otf|eot|woff|font.css)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
# ----------------------------------------------------------------------
# Proper MIME type for all files
# ----------------------------------------------------------------------
# JavaScript
# Normalize to standard type (it's sniffed in IE anyways)
# tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript js jsonp
AddType application/json json
# Audio
AddType audio/ogg oga ogg
AddType audio/mp4 m4a f4a f4b
# Video
AddType video/ogg ogv
AddType video/mp4 mp4 m4v f4v f4p
AddType video/webm webm
AddType video/x-flv flv
# SVG
# 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 application/x-font-ttf ttf ttc
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/xml rss atom xml rdf
AddType application/x-chrome-extension crx
AddType application/x-opera-extension oex
AddType application/x-xpinstall xpi
AddType application/octet-stream safariextz
AddType application/x-web-app-manifest+json webapp
AddType text/x-vcard vcf
AddType application/x-shockwave-flash swf
AddType text/vtt vtt
# ----------------------------------------------------------------------
# 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 use this technique if you do not have a build process.
#<FilesMatch "\.combined\.js$">
# Options +Includes
# AddOutputFilterByType INCLUDES application/javascript application/json
# SetOutputFilter INCLUDES
#</FilesMatch>
#<FilesMatch "\.combined\.css$">
# Options +Includes
# AddOutputFilterByType INCLUDES text/css
# 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*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
</IfModule>
</IfModule>
# Compress all output labeled with one of the following MIME-types
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/atom+xml \
application/javascript \
application/json \
application/rss+xml \
application/vnd.ms-fontobject \
application/x-font-ttf \
application/xhtml+xml \
application/xml \
font/opentype \
image/svg+xml \
image/x-icon \
text/css \
text/html \
text/plain \
text/x-component \
text/xml
</IfModule>
</IfModule>
# ----------------------------------------------------------------------
# Expires headers (for better cache control)
# ----------------------------------------------------------------------
# These are pretty far-future expires headers.
# They assume you control versioning with filename-based cache busting
# 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".
<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 (thanks 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"
# Feed
ExpiresByType application/rss+xml "access plus 1 hour"
ExpiresByType application/atom+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/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 application/x-font-ttf "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 1 year"
ExpiresByType application/javascript "access plus 1 year"
</IfModule>
# ----------------------------------------------------------------------
# Prevent mobile network providers from modifying your site
# ----------------------------------------------------------------------
# The following header prevents modification of your code over 3G on some
# European providers.
# This is the official 'bypass' suggested by O2 in the UK.
# <IfModule mod_headers.c>
# Header set Cache-Control "no-transform"
# </IfModule>
# ----------------------------------------------------------------------
# ETag removal
# ----------------------------------------------------------------------
# FileETag None is not enough for every server.
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
# 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).
# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary
# ----------------------------------------------------------------------
# Set Keep-Alive Header
# ----------------------------------------------------------------------
# Keep-Alive allows the server to send multiple requests through one
# TCP-connection. Be aware of possible disadvantages of this setting. Turn on
# if you serve a lot of static content.
# <IfModule mod_headers.c>
# Header set Connection Keep-Alive
# </IfModule>
# ----------------------------------------------------------------------
# Cookie setting from iframes
# ----------------------------------------------------------------------
# Allow cookies to be set from iframes (for IE only)
# If needed, specify a path or regex in the Location directive.
# <IfModule mod_headers.c>
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\""
# </IfModule>
# ----------------------------------------------------------------------
# Start rewrite engine
# ----------------------------------------------------------------------
# Turning on the rewrite engine is necessary for the following rules and
# features. FollowSymLinks must be enabled for this to work.
# Some cloud hosting services require RewriteBase to be set: goo.gl/HOcPN
# If using the h5bp in a subdirectory, use `RewriteBase /foo` instead where
# 'foo' is your directory.
# If your web host doesn't allow the FollowSymlinks option, you may need to
# comment it out and use `Options +SymLinksOfOwnerMatch`, but be aware of the
# performance impact: http://goo.gl/Mluzd
<IfModule mod_rewrite.c>
Options +FollowSymlinks
# Options +SymLinksIfOwnerMatch
RewriteEngine On
# RewriteBase /
</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.
# no-www.org/faq.php?q=class_b
# If you'd prefer 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.example.com -> example.com".
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
</IfModule>
# ----------------------------------------------------------------------
# Option 2:
# Rewrite "example.com -> www.example.com".
# 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]
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [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/h5bp/html5-boilerplate/wiki/cachebusting
# <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.example.com when your cert only allows https://secure.example.com
# <IfModule mod_rewrite.c>
# RewriteCond %{SERVER_PORT} !^443
# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [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.
# 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.
# If you are hosting your site in subdirectory, adjust this accordingly
# e.g. ErrorDocument 404 /subdir/404.html
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 .atom .css .js .json .rss .vtt .xml
# ----------------------------------------------------------------------
# A little more security
# ----------------------------------------------------------------------
# To avoid displaying the exact version number of Apache being used, add the
# following to 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).
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
# Block access to "hidden" directories or files whose names begin with a
# period. This includes directories used by version control systems such as
# Subversion or Git.
<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>
# Block access to backup and source files. These files may be left by some
# text/html editors and pose a great security danger, when anyone can access
# them.
<FilesMatch "(\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$">
Order allow,deny
Deny from all
Satisfy All
</FilesMatch>
# 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
# Rename session cookie to something else, than PHPSESSID
# php_value session.name sid
# Disable magic quotes (This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.)
# php_flag magic_quotes_gpc Off
# Do not show you are using PHP
# Note: Move this line to php.ini since it won't work in .htaccess
# php_flag expose_php Off
# Level of log detail - log all errors
# php_value error_reporting -1
# Write errors to log file
# php_flag log_errors On
# Do not display errors in browser (production - Off, development - On)
# php_flag display_errors Off
# Do not display startup errors (production - Off, development - On)
# php_flag display_startup_errors Off
# Format errors in plain text
# Note: Leave this setting 'On' for xdebug's var_dump() output
# php_flag html_errors Off
# Show multiple occurrence of error
# php_flag ignore_repeated_errors Off
# Show same errors from different sources
# php_flag ignore_repeated_source Off
# Size limit for error messages
# php_value log_errors_max_len 1024
# Don't precede error with string (doesn't accept empty string, use whitespace if you need)
# php_value error_prepend_string " "
# Don't prepend to error (doesn't accept empty string, use whitespace if you need)
# php_value error_append_string " "
# Increase cookie security
<IfModule php5_module>
php_value session.cookie_httponly true
</IfModule>

View file

@ -1,157 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Page Not Found :(</title>
<style>
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
::selection {
background: #b3d4fc;
text-shadow: none;
}
html {
padding: 30px 10px;
font-size: 20px;
line-height: 1.4;
color: #737373;
background: #f0f0f0;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
html,
input {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
body {
max-width: 500px;
_width: 500px;
padding: 30px 20px 50px;
border: 1px solid #b3b3b3;
border-radius: 4px;
margin: 0 auto;
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff;
background: #fcfcfc;
}
h1 {
margin: 0 10px;
font-size: 50px;
text-align: center;
}
h1 span {
color: #bbb;
}
h3 {
margin: 1.5em 0 0.5em;
}
p {
margin: 1em 0;
}
ul {
padding: 0 0 0 40px;
margin: 1em 0;
}
.container {
max-width: 380px;
_width: 380px;
margin: 0 auto;
}
/* google search */
#goog-fixurl ul {
list-style: none;
padding: 0;
margin: 0;
}
#goog-fixurl form {
margin: 0;
}
#goog-wm-qt,
#goog-wm-sb {
border: 1px solid #bbb;
font-size: 16px;
line-height: normal;
vertical-align: top;
color: #444;
border-radius: 2px;
}
#goog-wm-qt {
width: 220px;
height: 20px;
padding: 5px;
margin: 5px 10px 0 0;
box-shadow: inset 0 1px 1px #ccc;
}
#goog-wm-sb {
display: inline-block;
height: 32px;
padding: 0 10px;
margin: 5px 0 0;
white-space: nowrap;
cursor: pointer;
background-color: #f5f5f5;
background-image: -webkit-linear-gradient(rgba(255,255,255,0), #f1f1f1);
background-image: -moz-linear-gradient(rgba(255,255,255,0), #f1f1f1);
background-image: -ms-linear-gradient(rgba(255,255,255,0), #f1f1f1);
background-image: -o-linear-gradient(rgba(255,255,255,0), #f1f1f1);
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
*overflow: visible;
*display: inline;
*zoom: 1;
}
#goog-wm-sb:hover,
#goog-wm-sb:focus {
border-color: #aaa;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
background-color: #f8f8f8;
}
#goog-wm-qt:hover,
#goog-wm-qt:focus {
border-color: #105cb6;
outline: 0;
color: #222;
}
input::-moz-focus-inner {
padding: 0;
border: 0;
}
</style>
</head>
<body>
<div class="container">
<h1>Not found <span>:(</span></h1>
<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>
<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>
</div>
</body>
</html>

View file

@ -1,19 +0,0 @@
Copyright (c) HTML5 Boilerplate
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.

View file

@ -1,63 +0,0 @@
# [HTML5 Boilerplate](http://html5boilerplate.com)
HTML5 Boilerplate is a professional front-end template for building fast,
robust, and adaptable web apps or sites.
This project is the product of many years of iterative development and combined
community knowledge. It does not impose a specific development philosophy or
framework, so you're free to architect your code in the way that you want.
* Source: [https://github.com/h5bp/html5-boilerplate](https://github.com/h5bp/html5-boilerplate)
* Homepage: [http://html5boilerplate.com](http://html5boilerplate.com)
* Twitter: [@h5bp](http://twitter.com/h5bp)
## Quick start
Choose one of the following options:
1. Download the latest stable release from
[html5boilerplate.com](http://html5boilerplate.com/) or a custom build from
[Initializr](http://www.initializr.com).
2. Clone the git repo — `git clone
https://github.com/h5bp/html5-boilerplate.git` - and checkout the tagged
release you'd like to use.
## Features
* HTML5 ready. Use the new elements with confidence.
* Cross-browser compatible (Chrome, Opera, Safari, Firefox 3.6+, IE6+).
* Designed with progressive enhancement in mind.
* Includes [Normalize.css](http://necolas.github.com/normalize.css/) for CSS
normalizations and common bug fixes.
* The latest [jQuery](http://jquery.com/) via CDN, with a local fallback.
* The latest [Modernizr](http://modernizr.com/) build for feature detection.
* IE-specific classes for easier cross-browser control.
* Placeholder CSS Media Queries.
* Useful CSS helpers.
* Default print CSS, performance optimized.
* Protection against any stray `console.log` causing JavaScript errors in
IE6/7.
* An optimized Google Analytics snippet.
* Apache server caching, compression, and other configuration defaults for
Grade-A performance.
* Cross-domain Ajax and Flash.
* "Delete-key friendly." Easy to strip out parts you don't need.
* Extensive inline and accompanying documentation.
## Documentation
Take a look at the [documentation table of
contents](/h5bp/html5-boilerplate/blob/master/doc/README.md). This
documentation is bundled with the project, which makes it readily available for
offline reading and provides a useful starting point for any documentation
you want to write about your project.
## Contributing
Anyone and everyone is welcome to
[contribute](/h5bp/html5-boilerplate/blob/master/doc/contribute.md). Hundreds
of developers have helped make the HTML5 Boilerplate what it is today.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,15 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
<!-- Most restrictive policy: -->
<site-control permitted-cross-domain-policies="none"/>
<!-- Least restrictive policy: -->
<!--
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
-->
</cross-domain-policy>

View file

@ -1,298 +0,0 @@
/*
* HTML5 Boilerplate
*
* What follows is the result of much research on cross-browser styling.
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
* Kroc Camen, and the H5BP dev community and team.
*/
/* ==========================================================================
Base styles: opinionated defaults
========================================================================== */
html,
button,
input,
select,
textarea {
color: #222;
}
body {
font-size: 1em;
line-height: 1.4;
}
/*
* Remove text-shadow in selection highlight: h5bp.com/i
* These selection declarations have to be separate.
* Customize the background color to match your design.
*/
::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
::selection {
background: #b3d4fc;
text-shadow: none;
}
/*
* A better looking default horizontal rule
*/
hr {
display: block;
height: 1px;
border: 0;
border-top: 1px solid #ccc;
margin: 1em 0;
padding: 0;
}
/*
* Remove the gap between images and the bottom of their containers: h5bp.com/i/440
*/
img {
vertical-align: middle;
}
/*
* Remove default fieldset styles.
*/
fieldset {
border: 0;
margin: 0;
padding: 0;
}
/*
* Allow only vertical resizing of textareas.
*/
textarea {
resize: vertical;
}
/* ==========================================================================
Chrome Frame prompt
========================================================================== */
.chromeframe {
margin: 0.2em 0;
background: #ccc;
color: #000;
padding: 0.2em 0;
}
/* ==========================================================================
Author's custom styles
========================================================================== */
/* ==========================================================================
Helper classes
========================================================================== */
/*
* Image replacement
*/
.ir {
background-color: transparent;
border: 0;
overflow: hidden;
/* IE 6/7 fallback */
*text-indent: -9999px;
}
.ir:before {
content: "";
display: block;
width: 0;
height: 100%;
}
/*
* Hide from both screenreaders and browsers: h5bp.com/u
*/
.hidden {
display: none !important;
visibility: hidden;
}
/*
* Hide only visually, but have it available for screenreaders: h5bp.com/v
*/
.visuallyhidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
/*
* Extends the .visuallyhidden class to allow the element to be focusable
* when navigated to via the keyboard: h5bp.com/p
*/
.visuallyhidden.focusable:active,
.visuallyhidden.focusable:focus {
clip: auto;
height: auto;
margin: 0;
overflow: visible;
position: static;
width: auto;
}
/*
* Hide visually and from screenreaders, but maintain layout
*/
.invisible {
visibility: hidden;
}
/*
* Clearfix: contain floats
*
* For modern browsers
* 1. The space content is one way to avoid an Opera bug when the
* `contenteditable` attribute is included anywhere else in the document.
* Otherwise it causes space to appear at the top and bottom of elements
* that receive the `clearfix` class.
* 2. The use of `table` rather than `block` is only necessary if using
* `:before` to contain the top-margins of child elements.
*/
.clearfix:before,
.clearfix:after {
content: " "; /* 1 */
display: table; /* 2 */
}
.clearfix:after {
clear: both;
}
/*
* For IE 6/7 only
* Include this rule to trigger hasLayout and contain floats.
*/
.clearfix {
*zoom: 1;
}
/* ==========================================================================
EXAMPLE Media Queries for Responsive Design.
Theses examples override the primary ('mobile first') styles.
Modify as content requires.
========================================================================== */
@media only screen and (min-width: 35em) {
/* Style adjustments for viewports that meet the condition */
}
@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-resolution: 144dpi) {
/* Style adjustments for high resolution devices */
}
/* ==========================================================================
Print styles.
Inlined to avoid required HTTP connection: h5bp.com/r
========================================================================== */
@media print {
* {
background: transparent !important;
color: #000 !important; /* Black prints faster: h5bp.com/s */
box-shadow:none !important;
text-shadow: none !important;
}
a,
a:visited {
text-decoration: underline;
}
a[href]:after {
content: " (" attr(href) ")";
}
abbr[title]:after {
content: " (" attr(title) ")";
}
/*
* Don't show links for images, or javascript/internal links
*/
.ir a:after,
a[href^="javascript:"]:after,
a[href^="#"]:after {
content: "";
}
pre,
blockquote {
border: 1px solid #999;
page-break-inside: avoid;
}
thead {
display: table-header-group; /* h5bp.com/t */
}
tr,
img {
page-break-inside: avoid;
}
img {
max-width: 100% !important;
}
@page {
margin: 0.5cm;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}

View file

@ -1,504 +0,0 @@
/*! normalize.css v1.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3,
* and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Addresses `font-family` inconsistency between `textarea` and other form
* elements.
*/
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/*
* Addresses margins handled incorrectly in IE 6/7.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses font sizes and margins set differently in IE 6/7.
* Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5,
* and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
h3 {
font-size: 1.17em;
margin: 1em 0;
}
h4 {
font-size: 1em;
margin: 1.33em 0;
}
h5 {
font-size: 0.83em;
margin: 1.67em 0;
}
h6 {
font-size: 0.75em;
margin: 2.33em 0;
}
/*
* Addresses styling not present in IE 7/8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 6/7/8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Addresses margins set differently in IE 6/7.
*/
p,
pre {
margin: 1em 0;
}
/*
* Corrects font family set oddly in IE 6, Safari 4/5, and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: monospace, serif;
_font-family: 'courier new', monospace;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Addresses CSS quotes not supported in IE 6/7.
*/
q {
quotes: none;
}
/*
* Addresses `quotes` property not supported in Safari 4.
*/
q:before,
q:after {
content: '';
content: none;
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Lists
========================================================================== */
/*
* Addresses margins set differently in IE 6/7.
*/
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
/*
* Addresses paddings set differently in IE 6/7.
*/
menu,
ol,
ul {
padding: 0 0 0 40px;
}
/*
* Corrects list images handled incorrectly in IE 7.
*/
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improves image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Corrects margin displayed oddly in IE 6/7.
*/
form {
margin: 0;
}
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 6/7/8/9.
* 2. Corrects text not wrapping in Firefox 3.
* 3. Corrects alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/*
* 1. Corrects font size not being inherited in all browsers.
* 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improves appearance and consistency in all browsers.
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/*
* Addresses Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
* 4. Removes inner spacing in IE 7 without affecting normal text inputs.
* Known issue: inner spacing remains in IE 6.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
*overflow: visible; /* 4 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to content-box in IE 8/9.
* 2. Removes excess padding in IE 8/9.
* 3. Removes excess padding in IE 7.
* Known issue: excess padding remains in IE 6.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 6/7/8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

View file

@ -1,15 +0,0 @@
# humanstxt.org/
# The humans responsible & technology colophon
# TEAM
<name> -- <role> -- <twitter>
# THANKS
<name>
# TECHNOLOGY COLOPHON
HTML5, CSS3
jQuery, Modernizr

View file

@ -1,6 +0,0 @@
---
title: HTML5 Boilerplate Middleman
---
<!-- Add your site or application content here -->
<p>Hello world! This is HTML5 Boilerplate.</p>

View file

@ -1,14 +0,0 @@
// Avoid `console` errors in browsers that lack a console.
if (!(window.console && console.log)) {
(function() {
var noop = function() {};
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
var length = methods.length;
var console = window.console = {};
while (length--) {
console[methods[length]] = noop;
}
}());
}
// Place any jQuery/helper plugins in here.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,40 +0,0 @@
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<!-- Use title if it's in the page YAML frontmatter -->
<title><%= current_page.data.title || "HTML5 Boilerplate" %></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.1.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an outdated browser. <a href="http://browsehappy.com/">Upgrade your browser today</a> or <a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to better experience this site.</p>
<![endif]-->
<%= yield %>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.0.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<script>
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
</html>

View file

@ -1,3 +0,0 @@
# robotstxt.org/
User-agent: *

View file

@ -1,28 +0,0 @@
# Local templates
class Middleman::Templates::Local < Middleman::Templates::Base
# Look for templates in ~/.middleman
# @return [String]
def self.source_root
File.join(File.expand_path('~/'), '.middleman')
end
# Just copy from the template path
# @return [void]
def build_scaffold!
directory options[:template].to_s, location
end
end
# Iterate over the directories in the templates path and register each one.
Dir[File.join(Middleman::Templates::Local.source_root, '*')].each do |dir|
next unless File.directory?(dir)
template_file = File.join(dir, 'template.rb')
if File.exists?(template_file)
require template_file
else
Middleman::Templates.register(File.basename(dir).to_sym, Middleman::Templates::Local)
end
end

View file

@ -1,25 +0,0 @@
# Mobile HTML5 Boilerplate
class Middleman::Templates::Mobile < Middleman::Templates::Base
# Slightly different paths
class_option :css_dir, :default => 'css'
class_option :js_dir, :default => 'js'
class_option :images_dir, :default => 'img'
# Template files are relative to this file
# @return [String]
def self.source_root
File.dirname(__FILE__)
end
# Output the files
# @return [void]
def build_scaffold!
template 'shared/config.tt', File.join(location, 'config.rb')
directory 'mobile/source', File.join(location, 'source')
empty_directory File.join(location, 'source')
end
end
# Register the template
Middleman::Templates.register(:mobile, Middleman::Templates::Mobile)

View file

@ -1,37 +0,0 @@
<!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>

View file

@ -1,21 +0,0 @@
#Mobile Boilerplate http://html5boilerplate.com
v1.1
##Summary:
A baseline for 'mobile first' web development. Read more at the wiki here: https://github.com/shichuan/mobile-html5-boilerplate/wiki
##License:
###Major components:
respond.js: Public Domain<br />
Bookmark bubble library: Apache License, Version 2.0<br />
Web Storage Portability Layer: Apache License, Version 2.0<br />
Modernizr: MIT/BSD license<br />
jQuery: MIT/GPL license<br />
HTML5Doctor CSS reset: Creative Commons 3.0 <br />
CSS Reset Reloaded: Public Domain
###Everything else:
The Unlicense (aka: public domain)

View file

@ -1,25 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html -->
<!-- Most restrictive policy: -->
<site-control permitted-cross-domain-policies="none"/>
<!-- Least restrictive policy: -->
<!--
<site-control permitted-cross-domain-policies="all"/>
<allow-access-from domain="*" to-ports="*" secure="false"/>
<allow-http-request-headers-from domain="*" headers="*" secure="false"/>
-->
<!--
If you host a crossdomain.xml file with allow-access-from domain="*"
and dont understand all of the points described here, you probably
have a nasty security vulnerability. ~ simon willison
-->
</cross-domain-policy>

View file

@ -1,315 +0,0 @@
/*
* HTML5 Boilerplate
*
* What follows is the result of much research on cross-browser styling.
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
* Kroc Camen, and the H5BP dev community and team.
*
* Detailed information about this CSS: h5bp.com/css
*
* ==|== normalize ==========================================================
*/
/* =============================================================================
HTML5 display definitions
========================================================================== */
article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; }
audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; }
audio:not([controls]) { display: none; }
[hidden] { display: none; }
/* =============================================================================
Base
========================================================================== */
/*
* 1. Correct text resizing oddly in IE6/7 when body font-size is set using em units
* 2. Force vertical scrollbar in non-IE
* 3. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g
*/
html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }
body { margin: 0; font-size: 13px; line-height: 1.231; }
body, button, input, select, textarea { font-family: sans-serif; color: #222; }
/*
* Remove text-shadow in selection highlight: h5bp.com/i
* These selection declarations have to be separate
* Also: hot pink! (or customize the background color to match your design)
*/
::-moz-selection { background: #fe57a1; color: #fff; text-shadow: none; }
::selection { background: #fe57a1; color: #fff; text-shadow: none; }
/* =============================================================================
Links
========================================================================== */
a { color: #00e; }
a:visited { color: #551a8b; }
a:hover { color: #06e; }
a:focus { outline: thin dotted; }
/* Improve readability when focused and hovered in all browsers: h5bp.com/h */
a:hover, a:active { outline: 0; }
/* =============================================================================
Typography
========================================================================== */
abbr[title] { border-bottom: 1px dotted; }
b, strong { font-weight: bold; }
blockquote { margin: 1em 40px; }
dfn { font-style: italic; }
hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
ins { background: #ff9; color: #000; text-decoration: none; }
mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; }
/* Redeclare monospace font family: h5bp.com/j */
pre, code, kbd, samp { font-family: monospace, serif; _font-family: 'courier new', monospace; font-size: 1em; }
/* Improve readability of pre-formatted text in all browsers */
pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; }
q { quotes: none; }
q:before, q:after { content: ""; content: none; }
small { font-size: 85%; }
/* Position subscript and superscript content without affecting line-height: h5bp.com/k */
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sup { top: -0.5em; }
sub { bottom: -0.25em; }
/* =============================================================================
Lists
========================================================================== */
ul, ol { margin: 1em 0; padding: 0 0 0 40px; }
dd { margin: 0 0 0 40px; }
nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; }
/* =============================================================================
Embedded content
========================================================================== */
/*
* 1. Improve image quality when scaled in IE7: h5bp.com/d
* 2. Remove the gap between images and borders on image containers: h5bp.com/e
*/
img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; }
/*
* Correct overflow not hidden in IE9
*/
svg:not(:root) { overflow: hidden; }
/* =============================================================================
Figures
========================================================================== */
figure { margin: 0; }
/* =============================================================================
Forms
========================================================================== */
form { margin: 0; }
fieldset { border: 0; margin: 0; padding: 0; }
/* Indicate that 'label' will shift focus to the associated form element */
label { cursor: pointer; }
/*
* 1. Correct color not inheriting in IE6/7/8/9
* 2. Correct alignment displayed oddly in IE6/7
*/
legend { border: 0; *margin-left: -7px; padding: 0; }
/*
* 1. Correct font-size not inheriting in all browsers
* 2. Remove margins in FF3/4 S5 Chrome
* 3. Define consistent vertical alignment display in all browsers
*/
button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; }
/*
* 1. Define line-height as normal to match FF3/4 (set using !important in the UA stylesheet)
* 2. Correct inner spacing displayed oddly in IE6/7
*/
button, input { line-height: normal; *overflow: visible; }
/*
* Reintroduce inner spacing in 'table' to avoid overlap and whitespace issues in IE6/7
*/
table button, table input { *overflow: auto; }
/*
* 1. Display hand cursor for clickable form elements
* 2. Allow styling of clickable form elements in iOS
*/
button, input[type="button"], input[type="reset"], input[type="submit"], [role="button"] { cursor: pointer; -webkit-appearance: button; }
/*
* Consistent box sizing and appearance
*/
input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; }
input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; }
input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
/*
* Remove inner padding and border in FF3/4: h5bp.com/l
*/
button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
/*
* 1. Remove default vertical scrollbar in IE6/7/8/9
* 2. Allow only vertical resizing
*/
textarea { overflow: auto; vertical-align: top; resize: vertical; }
/* Colors for form validity */
input:valid, textarea:valid { }
input:invalid, textarea:invalid { background-color: #f0dddd; }
/* =============================================================================
Tables
========================================================================== */
table { border-collapse: collapse; border-spacing: 0; }
td { vertical-align: top; }
/* ==|== primary styles =====================================================
Author:
========================================================================== */
/*
* Media queries for responsive design https://github.com/shichuan/mobile-html5-boilerplate/wiki/The-Style
*/
/* Styles for desktop and large screen ----------- */
/*styles for 800px and up!*/
@media only screen and (min-width: 800px) {
/* Styles */
}/*/mediaquery*/
/* iPhone 4, Opera Mobile 11 and other high pixel ratio devices ----------- */
@media
only screen and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (-o-min-device-pixel-ratio: 3/2),
only screen and (min--moz-device-pixel-ratio: 1.5),
only screen and (min-device-pixel-ratio: 1.5) {
/* Styles */
}
/* ==|== non-semantic helper classes ========================================
Please define your styles before this section.
========================================================================== */
/* prevent callout */
.nocallout {-webkit-touch-callout: none;}
/* Text overflow with ellipsis */
.ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/* A hack for HTML5 contenteditable attribute on mobile */
textarea[contenteditable] {-webkit-appearance: none;}
/* A workaround for S60 3.x and 5.0 devices which do not animated gif images if they have been set as display: none */
.gifhidden {position: absolute; left: -100%;}
/* For image replacement */
.ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; }
.ir br { display: none; }
/* Hide from both screenreaders and browsers: h5bp.com/u */
.hidden { display: none !important; visibility: hidden; }
/* Hide only visually, but have it available for screenreaders: h5bp.com/v */
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
/* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */
.visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; }
/* Hide visually and from screenreaders, but maintain layout */
.invisible { visibility: hidden; }
/* Contain floats: h5bp.com/q */
.clearfix:before, .clearfix:after { content: ""; display: table; }
.clearfix:after { clear: both; }
.clearfix { *zoom: 1; }
/* ==|== print styles =======================================================
Print styles.
Inlined to avoid required HTTP connection: h5bp.com/r
========================================================================== */
@media print {
* { background: transparent !important; color: black !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } /* Black prints faster: h5bp.com/s */
a, a:visited { text-decoration: underline; }
a[href]:after { content: " (" attr(href) ")"; }
abbr[title]:after { content: " (" attr(title) ")"; }
.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */
pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
thead { display: table-header-group; } /* h5bp.com/t */
tr, img { page-break-inside: avoid; }
img { max-width: 100% !important; }
@page { margin: 0.5cm; }
p, h2, h3 { orphans: 3; widows: 3; }
h2, h3 { page-break-after: avoid; }
}

View file

@ -1,43 +0,0 @@
/* the humans responsible & colophon */
/* humanstxt.org */
/* TEAM */
<your title>: <your name>
Site:
Twitter:
Location:
/* THANKS */
Names (& URL):
/* SITE */
Standards: HTML5, CSS3
Components: Modernizr, jQuery
Software:
-o/-
+oo//-
:ooo+//:
-ooooo///-
/oooooo//:
:ooooooo+//-
-+oooooooo///-
-://////////////+oooooooooo++////////////::
:+ooooooooooooooooooooooooooooooooooooo+:::-
-/+ooooooooooooooooooooooooooooooo+/::////:-
-:+oooooooooooooooooooooooooooo/::///////:-
--/+ooooooooooooooooooooo+::://////:-
-:+ooooooooooooooooo+:://////:--
/ooooooooooooooooo+//////:-
-ooooooooooooooooooo////-
/ooooooooo+oooooooooo//:
:ooooooo+/::/+oooooooo+//-
-oooooo/::///////+oooooo///-
/ooo+::://////:---:/+oooo//:
-o+/::///////:- -:/+o+//-
:-:///////:- -:/://
-////:- --//:
-- -:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -1,92 +0,0 @@
<!doctype html>
<!-- Conditional comment for mobile ie7 http://blogs.msdn.com/b/iemobile/ -->
<!--[if IEMobile 7 ]> <html class="no-js iem7"> <![endif]-->
<!--[if (gt IEMobile 7)|!(IEMobile)]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="author" content="">
<!-- Mobile viewport optimization http://goo.gl/b9SaQ -->
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Home screen icon Mathias Bynens http://goo.gl/6nVq0 -->
<!-- For iPhone 4 with high-resolution Retina display: -->
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="img/h/apple-touch-icon.png">
<!-- For first-generation iPad: -->
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="img/m/apple-touch-icon.png">
<!-- For non-Retina iPhone, iPod Touch, and Android 2.1+ devices: -->
<link rel="apple-touch-icon-precomposed" href="img/l/apple-touch-icon-precomposed.png">
<!-- For nokia devices: -->
<link rel="shortcut icon" href="img/l/apple-touch-icon.png">
<!--iOS web app, deletable if not needed -->
<!--the script prevents links from opening in mobile safari. https://gist.github.com/1042026
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<script>(function(a,b,c){if(c in b&&b[c]){var d,e=a.location,f=/^(a|html)$/i;a.addEventListener("click",function(a){d=a.target;while(!f.test(d.nodeName))d=d.parentNode;"href"in d&&(d.href.indexOf("http")||~d.href.indexOf(e.host))&&(a.preventDefault(),e.href=d.href)},!1)}})(document,window.navigator,"standalone")</script>
<link rel="apple-touch-startup-image" href="img/l/splash.png">-->
<!-- Mobile IE allows us to activate ClearType technology for smoothing fonts for easy reading -->
<meta http-equiv="cleartype" content="on">
<!-- more tags for your 'head' to consider https://gist.github.com/849231 -->
<!-- Main Stylesheet -->
<link rel="stylesheet" href="css/style.css?v=1">
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
<script src="js/libs/modernizr-custom.js"></script>
<!-- Media Queries Polyfill https://github.com/shichuan/mobile-html5-boilerplate/wiki/Media-Queries-Polyfill -->
<script>Modernizr.mq('(min-width:0)') || document.write('<script src="js/libs/respond.min.js">\x3C/script>')</script>
</head>
<body>
<div id="container">
<header>
</header>
<div id="main" role="main">
</div>
<footer>
</footer>
</div> <!--! end of #container -->
<!-- JavaScript at the bottom for fast page loading -->
<!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if necessary -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.2.min.js"><\/script>')</script>
<!-- scripts concatenated and minified via ant build script -->
<script src="js/mylibs/helper.js"></script>
<!-- end concatenated and minified scripts-->
<script>
// iPhone Scale Bug Fix, read this when using http://www.blog.highub.com/mobile-2/a-fix-for-iphone-viewport-scale-bug/
MBP.scaleFix();
</script>
<!-- Debugger - remove for production -->
<!-- <script src="https://getfirebug.com/firebug-lite.js"></script> -->
<!-- mathiasbynens.be/notes/async-analytics-snippet Change UA-XXXXX-X to be your site's ID -->
<script>
var _gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.async=1;
g.src=("https:"==location.protocol?"//ssl":"//www")+".google-analytics.com/ga.js";
s.parentNode.insertBefore(g,s)}(document,"script"));
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

View file

@ -1,7 +0,0 @@
/*
* respond.js - A small and fast polyfill for min/max-width CSS3 Media Queries
* Copyright 2011, Scott Jehl, scottjehl.com
* Dual licensed under the MIT or GPL Version 2 licenses.
* Usage: Check out the readme file or github.com/scottjehl/respond
*/
(function(e,h){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=h;if(h){return}var u=e.document,r=u.documentElement,i=[],k=[],p=[],o={},g=30,f=u.getElementsByTagName("head")[0]||r,b=f.getElementsByTagName("link"),d=[],a=function(){var B=b,w=B.length;for(var z=0;z<w;z++){var y=B[z],x=y.href,A=y.media,v=y.rel&&y.rel.toLowerCase()==="stylesheet";if(!!x&&v&&!o[x]){if(!/^([a-zA-Z]+?:(\/\/)?(www\.)?)/.test(x)||x.replace(RegExp.$1,"").split("/")[0]===e.location.host){d.push({href:x,media:A})}else{o[x]=true}}}t()},t=function(){if(d.length){var v=d.shift();n(v.href,function(w){m(w,v.href,v.media);o[v.href]=true;t()})}},m=function(G,v,x){var E=G.match(/@media ([^\{]+)\{([\S\s]+?)(?=\}[\s]*\/\*\/mediaquery\*\/)/gmi),H=E&&E.length||0,v=v.substring(0,v.lastIndexOf("/")),w=function(I){return I.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+v+"$2$3")},y=!H&&x;if(v.length){v+="/"}if(y){H=1}for(var B=0;B<H;B++){var C;if(y){C=x;k.push(w(G))}else{C=E[B].match(/@media ([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1;k.push(RegExp.$2&&w(RegExp.$2))}var z=C.split(","),F=z.length;for(var A=0;A<F;A++){var D=z[A];i.push({media:D.match(/(only\s+)?([a-zA-Z]+)(\sand)?/)&&RegExp.$2,rules:k.length-1,minw:D.match(/\(min\-width:[\s]*([\s]*[0-9]+)px[\s]*\)/)&&parseFloat(RegExp.$1),maxw:D.match(/\(max\-width:[\s]*([\s]*[0-9]+)px[\s]*\)/)&&parseFloat(RegExp.$1)})}}j()},l,q,j=function(E){var v="clientWidth",x=r[v],D=u.compatMode==="CSS1Compat"&&x||u.body[v]||x,z={},C=u.createDocumentFragment(),B=b[b.length-1],w=(new Date()).getTime();if(E&&l&&w-l<g){clearTimeout(q);q=setTimeout(j,g);return}else{l=w}for(var y in i){var F=i[y];if(!F.minw&&!F.maxw||(!F.minw||F.minw&&D>=F.minw)&&(!F.maxw||F.maxw&&D<=F.maxw)){if(!z[F.media]){z[F.media]=[]}z[F.media].push(k[F.rules])}}for(var y in p){if(p[y]&&p[y].parentNode===f){f.removeChild(p[y])}}for(var y in z){var G=u.createElement("style"),A=z[y].join("\n");G.type="text/css";G.media=y;if(G.styleSheet){G.styleSheet.cssText=A}else{G.appendChild(u.createTextNode(A))}C.appendChild(G);p.push(G)}f.insertBefore(C,B.nextSibling)},n=function(v,x){var w=c();if(!w){return}w.open("GET",v,true);w.onreadystatechange=function(){if(w.readyState!=4||w.status!=200&&w.status!=304){return}x(w.responseText)};if(w.readyState==4){return}w.send()},c=(function(){var v=false,w=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new ActiveXObject("Msxml3.XMLHTTP")},function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()}],y=w.length;while(y--){try{v=w[y]()}catch(x){continue}break}return function(){return v}})();a();respond.update=a;function s(){j(true)}if(e.addEventListener){e.addEventListener("resize",s,false)}else{if(e.attachEvent){e.attachEvent("onresize",s)}}})(this,(function(f){if(f.matchMedia){return true}var e,i=document,c=i.documentElement,g=c.firstElementChild||c.firstChild,h=!i.body,d=i.body||i.createElement("body"),b=i.createElement("div"),a="only all";b.id="mq-test-1";b.style.cssText="position:absolute;top:-99em";d.appendChild(b);b.innerHTML='_<style media="'+a+'"> #mq-test-1 { width: 9px; }</style>';if(h){c.insertBefore(d,g)}b.removeChild(b.firstChild);e=b.offsetWidth==9;if(h){c.removeChild(d)}else{d.removeChild(b)}return e})(this));

View file

@ -1,171 +0,0 @@
/*
* MBP - Mobile boilerplate helper functions
*/
(function(document){
window.MBP = window.MBP || {};
// Fix for iPhone viewport scale bug
// http://www.blog.highub.com/mobile-2/a-fix-for-iphone-viewport-scale-bug/
MBP.viewportmeta = document.querySelector && document.querySelector('meta[name="viewport"]');
MBP.ua = navigator.userAgent;
MBP.scaleFix = function () {
if (MBP.viewportmeta && /iPhone|iPad/.test(MBP.ua) && !/Opera Mini/.test(MBP.ua)) {
MBP.viewportmeta.content = "width=device-width, minimum-scale=1.0, maximum-scale=1.0";
document.addEventListener("gesturestart", MBP.gestureStart, false);
}
};
MBP.gestureStart = function () {
MBP.viewportmeta.content = "width=device-width, minimum-scale=0.25, maximum-scale=1.6";
};
// Hide URL Bar for iOS and Android by Scott Jehl
// https://gist.github.com/1183357
MBP.hideUrlBar = function () {
var win = window,
doc = win.document;
// If there's a hash, or addEventListener is undefined, stop here
if( !location.hash || !win.addEventListener ){
//scroll to 1
window.scrollTo( 0, 1 );
var scrollTop = 1,
//reset to 0 on bodyready, if needed
bodycheck = setInterval(function(){
if( doc.body ){
clearInterval( bodycheck );
scrollTop = "scrollTop" in doc.body ? doc.body.scrollTop : 1;
win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
}
}, 15 );
win.addEventListener( "load", function(){
setTimeout(function(){
//reset to hide addr bar at onload
win.scrollTo( 0, scrollTop === 1 ? 0 : 1 );
}, 0);
}, false );
}
};
// Fast Buttons - read wiki below before using
// https://github.com/shichuan/mobile-html5-boilerplate/wiki/JavaScript-Helper
MBP.fastButton = function (element, handler) {
this.element = element;
this.handler = handler;
if (element.addEventListener) {
element.addEventListener('touchstart', this, false);
element.addEventListener('click', this, false);
}
};
MBP.fastButton.prototype.handleEvent = function(event) {
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onClick(event); break;
case 'click': this.onClick(event); break;
}
};
MBP.fastButton.prototype.onTouchStart = function(event) {
event.stopPropagation();
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = event.touches[0].clientX;
this.startY = event.touches[0].clientY;
this.element.style.backgroundColor = "rgba(0,0,0,.7)";
};
MBP.fastButton.prototype.onTouchMove = function(event) {
if(Math.abs(event.touches[0].clientX - this.startX) > 10 ||
Math.abs(event.touches[0].clientY - this.startY) > 10 ) {
this.reset();
}
};
MBP.fastButton.prototype.onClick = function(event) {
event.stopPropagation();
this.reset();
this.handler(event);
if(event.type == 'touchend') {
MBP.preventGhostClick(this.startX, this.startY);
}
this.element.style.backgroundColor = "";
};
MBP.fastButton.prototype.reset = function() {
this.element.removeEventListener('touchend', this, false);
document.body.removeEventListener('touchmove', this, false);
this.element.style.backgroundColor = "";
};
MBP.preventGhostClick = function (x, y) {
MBP.coords.push(x, y);
window.setTimeout(function (){
MBP.coords.splice(0, 2);
}, 2500);
};
MBP.ghostClickHandler = function (event) {
for(var i = 0, len = MBP.coords.length; i < len; i += 2) {
var x = MBP.coords[i];
var y = MBP.coords[i + 1];
if(Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
event.stopPropagation();
event.preventDefault();
}
}
};
if (document.addEventListener) {
document.addEventListener('click', MBP.ghostClickHandler, true);
}
MBP.coords = [];
// iOS Startup Image
// https://github.com/shichuan/mobile-html5-boilerplate/issues#issue/2
MBP.splash = function () {
var filename = navigator.platform === 'iPad' ? 'h/' : 'l/';
document.write('<link rel="apple-touch-startup-image" href="/img/' + filename + 'splash.png" />' );
};
// Autogrow
// http://googlecode.blogspot.com/2009/07/gmail-for-mobile-html5-series.html
MBP.autogrow = function (element, lh) {
function handler(e){
var newHeight = this.scrollHeight,
currentHeight = this.clientHeight;
if (newHeight > currentHeight) {
this.style.height = newHeight + 3 * textLineHeight + "px";
}
}
var setLineHeight = (lh) ? lh : 12,
textLineHeight = element.currentStyle ? element.currentStyle.lineHeight :
getComputedStyle(element, null).lineHeight;
textLineHeight = (textLineHeight.indexOf("px") == -1) ? setLineHeight :
parseInt(textLineHeight, 10);
element.style.overflow = "hidden";
element.addEventListener ? element.addEventListener('keyup', handler, false) :
element.attachEvent('onkeyup', handler);
};
})(document);

View file

@ -1,20 +0,0 @@
// usage: log('inside coolFunc', this, arguments);
// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
window.log = function(){
log.history = log.history || []; // store logs to an array for reference
log.history.push(arguments);
if(this.console) {
arguments.callee = arguments.callee.caller;
var newarr = [].slice.call(arguments);
(typeof console.log === 'object' ? log.apply.call(console.log, console, newarr) : console.log.apply(console, newarr));
}
};
// make it safe to use console.log always
(function(b){function c(){}for(var d="assert,clear,count,debug,dir,dirxml,error,exception,firebug,group,groupCollapsed,groupEnd,info,log,memoryProfile,memoryProfileEnd,profile,profileEnd,table,time,timeEnd,timeStamp,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try
{console.log();return window.console;}catch(err){return window.console={};}})());
// place any jQuery/helper plugins in here, instead of separate, slower script files.

View file

@ -1,4 +0,0 @@
# www.robotstxt.org/
# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
User-agent: *

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0">
<!-- http://www.google.com/support/webmasters/bin/answer.py?answer=34648 -->
<url>
<!--<loc>http://mobile.example.com/index.html</loc>-->
<mobile:mobile/>
</url>
</urlset>

View file

@ -1,31 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>QUnit Tests</title>
<link rel="stylesheet" href="qunit/qunit.css" media="screen">
<!-- reference your own javascript files here -->
<script src="../js/modernizr-custom.js"></script>
<script src="../js/libs/jquery-1.6.2.min.js"></script>
<script src="../js/mylibs/helper.js"></script>
<script src="../js/script.js"></script>
<!-- test runner files -->
<script src="qunit/qunit.js"></script>
<script src="tests.js"></script>
</head>
<body class="flora">
<h1 id="qunit-header">QUnit Test Suite</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup</div>
</body>
</html>

View file

@ -1,148 +0,0 @@
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests li ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
background-color: #0d3349;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0em 0 0.5em 2em;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
}
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
background-color: #fff;
border-left: 26px solid #C6E746;
}
#qunit-tests li.pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests li.pass span.test-name { color: #366097; }
#qunit-tests li li.pass span.test-actual,
#qunit-tests li li.pass span.test-expected { color: #999999; }
strong b.pass { color: #5E740B; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
}
#qunit-tests li.fail { color: #000000; background-color: #EE5757; }
#qunit-tests li.fail span.test-name,
#qunit-tests li.fail span.module-name { color: #000000; }
#qunit-tests li li.fail span.test-actual { color: #EE5757; }
#qunit-tests li li.fail span.test-expected { color: green; }
strong b.fail { color: #710909; }
#qunit-banner.qunit-fail,
#qunit-testrunner-toolbar { background-color: #EE5757; }
/** Footer */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
}

View file

@ -1,21 +0,0 @@
// documentation on writing tests here: http://docs.jquery.com/QUnit
// example tests: https://github.com/jquery/qunit/blob/master/test/same.js
// below are some general tests but feel free to delete them.
module("example tests");
test('HTML5 Boilerplate is sweet',function(){
expect(1);
equals('boilerplate'.replace('boilerplate','sweet'),'sweet','Yes. HTML5 Boilerplate is, in fact, sweet');
});
// these test things from helper.js
test('Environment is good',function(){
expect(2);
ok( !!window.MBP, 'Mobile Boilder Plate helper is present');
notEqual( window.MBP.ua, null, "we have a user agent. winning, duh.");
});

View file

@ -1,31 +0,0 @@
<%@ Page Language="C#" %>
<script language="C#" runat="server">
// Copyright 2009 Google Inc. All Rights Reserved.
private const string GaAccount = "ACCOUNT ID GOES HERE";
private const string GaPixel = "ga.aspx";
private string GoogleAnalyticsGetImageUrl() {
System.Text.StringBuilder url = new System.Text.StringBuilder();
url.Append(GaPixel + "?");
url.Append("utmac=").Append(GaAccount);
Random RandomClass = new Random();
url.Append("&utmn=").Append(RandomClass.Next(0x7fffffff));
string referer = "-";
if (Request.UrlReferrer != null
&& "" != Request.UrlReferrer.ToString()) {
referer = Request.UrlReferrer.ToString();
}
url.Append("&utmr=").Append(HttpUtility.UrlEncode(referer));
if (HttpContext.Current.Request.Url != null) {
url.Append("&utmp=").Append(HttpUtility.UrlEncode(Request.Url.PathAndQuery));
}
url.Append("&guid=ON");
return url.ToString();
}
</script>

View file

@ -1,2 +0,0 @@
<% string googleAnalyticsImageUrl = GoogleAnalyticsGetImageUrl(); %>
<img src="<%= googleAnalyticsImageUrl %>" />

View file

@ -1,195 +0,0 @@
<% @Page Language="C#" ContentType="image/gif"%><%
@Import Namespace="System.Net" %><%
@Import Namespace="System.Security.Cryptography" %><%
@Import Namespace="System.Text" %><script runat="server" language="c#">
/**
Copyright 2009 Google Inc. All Rights Reserved.
**/
// Tracker version.
private const string Version = "4.4sa";
private const string CookieName = "__utmmobile";
// The path the cookie will be available to, edit this to use a different
// cookie path.
private const string CookiePath = "/";
// Two years in seconds.
private readonly TimeSpan CookieUserPersistence = TimeSpan.FromSeconds(63072000);
// 1x1 transparent GIF
private readonly byte[] GifData = {
0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
0x01, 0x00, 0x01, 0x00, 0x80, 0xff,
0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x02,
0x02, 0x44, 0x01, 0x00, 0x3b
};
private static readonly Regex IpAddressMatcher =
new Regex(@"^([^.]+\.[^.]+\.[^.]+\.).*");
// A string is empty in our terms, if it is null, empty or a dash.
private static bool IsEmpty(string input) {
return input == null || "-" == input || "" == input;
}
// The last octect of the IP address is removed to anonymize the user.
private static string GetIP(string remoteAddress) {
if (IsEmpty(remoteAddress)) {
return "";
}
// Capture the first three octects of the IP address and replace the forth
// with 0, e.g. 124.455.3.123 becomes 124.455.3.0
Match m = IpAddressMatcher.Match(remoteAddress);
if (m.Success) {
return m.Groups[1] + "0";
} else {
return "";
}
}
// Generate a visitor id for this hit.
// If there is a visitor id in the cookie, use that, otherwise
// use the guid if we have one, otherwise use a random number.
private static string GetVisitorId(
string guid, string account, string userAgent, HttpCookie cookie) {
// If there is a value in the cookie, don't change it.
if (cookie != null && cookie.Value != null) {
return cookie.Value;
}
String message;
if (!IsEmpty(guid)) {
// Create the visitor id using the guid.
message = guid + account;
} else {
// otherwise this is a new user, create a new random id.
message = userAgent + GetRandomNumber() + Guid.NewGuid().ToString();
}
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
byte[] sum = md5.ComputeHash(messageBytes);
string md5String = BitConverter.ToString(sum);
md5String = md5String.Replace("-","");
md5String = md5String.PadLeft(32, '0');
return "0x" + md5String.Substring(0, 16);
}
// Get a random number string.
private static String GetRandomNumber() {
Random RandomClass = new Random();
return RandomClass.Next(0x7fffffff).ToString();
}
// Writes the bytes of a 1x1 transparent gif into the response.
private void WriteGifData() {
Response.AddHeader(
"Cache-Control",
"private, no-cache, no-cache=Set-Cookie, proxy-revalidate");
Response.AddHeader("Pragma", "no-cache");
Response.AddHeader("Expires", "Wed, 17 Sep 1975 21:32:10 GMT");
Response.Buffer = false;
Response.OutputStream.Write(GifData, 0, GifData.Length);
}
// Make a tracking request to Google Analytics from this server.
// Copies the headers from the original request to the new one.
// If request containg utmdebug parameter, exceptions encountered
// communicating with Google Analytics are thown.
private void SendRequestToGoogleAnalytics(string utmUrl) {
try {
WebRequest connection = WebRequest.Create(utmUrl);
((HttpWebRequest)connection).UserAgent = Request.UserAgent;
connection.Headers.Add("Accepts-Language",
Request.Headers.Get("Accepts-Language"));
using (WebResponse resp = connection.GetResponse()) {
// Ignore response
}
} catch (Exception ex) {
if (Request.QueryString.Get("utmdebug") != null) {
throw new Exception("Error contacting Google Analytics", ex);
}
}
}
// Track a page view, updates all the cookies and campaign tracker,
// makes a server side request to Google Analytics and writes the transparent
// gif byte data to the response.
private void TrackPageView() {
TimeSpan timeSpan = (DateTime.Now - new DateTime(1970, 1, 1).ToLocalTime());
string timeStamp = timeSpan.TotalSeconds.ToString();
string domainName = Request.ServerVariables["SERVER_NAME"];
if (IsEmpty(domainName)) {
domainName = "";
}
// Get the referrer from the utmr parameter, this is the referrer to the
// page that contains the tracking pixel, not the referrer for tracking
// pixel.
string documentReferer = Request.QueryString.Get("utmr");
if (IsEmpty(documentReferer)) {
documentReferer = "-";
} else {
documentReferer = HttpUtility.UrlDecode(documentReferer);
}
string documentPath = Request.QueryString.Get("utmp");
if (IsEmpty(documentPath)) {
documentPath = "";
} else {
documentPath = HttpUtility.UrlDecode(documentPath);
}
string account = Request.QueryString.Get("utmac");
string userAgent = Request.UserAgent;
if (IsEmpty(userAgent)) {
userAgent = "";
}
// Try and get visitor cookie from the request.
HttpCookie cookie = Request.Cookies.Get(CookieName);
string visitorId = GetVisitorId(
Request.Headers.Get("X-DCMGUID"), account, userAgent, cookie);
// Always try and add the cookie to the response.
HttpCookie newCookie = new HttpCookie(CookieName);
newCookie.Value = visitorId;
newCookie.Expires = DateTime.Now + CookieUserPersistence;
newCookie.Path = CookiePath;
Response.Cookies.Add(newCookie);
string utmGifLocation = "http://www.google-analytics.com/__utm.gif";
// Construct the gif hit url.
string utmUrl = utmGifLocation + "?" +
"utmwv=" + Version +
"&utmn=" + GetRandomNumber() +
"&utmhn=" + HttpUtility.UrlEncode(domainName) +
"&utmr=" + HttpUtility.UrlEncode(documentReferer) +
"&utmp=" + HttpUtility.UrlEncode(documentPath) +
"&utmac=" + account +
"&utmcc=__utma%3D999.999.999.999.999.1%3B" +
"&utmvid=" + visitorId +
"&utmip=" + GetIP(Request.ServerVariables["REMOTE_ADDR"]);
SendRequestToGoogleAnalytics(utmUrl);
// If the debug parameter is on, add a header to the response that contains
// the url that was used to contact Google Analytics.
if (Request.QueryString.Get("utmdebug") != null) {
Response.AddHeader("X-GA-MOBILE-URL", utmUrl);
}
// Finally write the gif data to the response.
WriteGifData();
}
</script><% TrackPageView(); %>

View file

@ -1,44 +0,0 @@
<%@ Page Language="C#" %>
<script language="C#" runat="server">
private const string GaAccount = "MO-3845491-5";
private const string GaPixel = "ga.aspx";
private string GoogleAnalyticsGetImageUrl() {
System.Text.StringBuilder url = new System.Text.StringBuilder();
url.Append(GaPixel + "?");
url.Append("utmac=").Append(GaAccount);
Random RandomClass = new Random();
url.Append("&utmn=").Append(RandomClass.Next(0x7fffffff));
string referer = "-";
if (Request.UrlReferrer != null
&& "" != Request.UrlReferrer.ToString()) {
referer = Request.UrlReferrer.ToString();
}
url.Append("&utmr=").Append(HttpUtility.UrlEncode(referer));
if (HttpContext.Current.Request.Url != null) {
url.Append("&utmp=").Append(HttpUtility.UrlEncode(Request.Url.PathAndQuery));
}
url.Append("&guid=ON");
return url.ToString();
}
</script>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Sample Mobile Analytics Page</title>
</head>
<body>
Publisher's content here.
<%
string googleAnalyticsImageUrl = GoogleAnalyticsGetImageUrl();
%>
<img src="<%= googleAnalyticsImageUrl %>" />
Testing: <%= googleAnalyticsImageUrl %>
</body>
</html>

View file

@ -1,225 +0,0 @@
<%@ page language="java"
contentType="image/gif"
import="java.io.ByteArrayOutputStream,
java.io.IOException,
java.io.OutputStream,
java.io.UnsupportedEncodingException,
java.math.BigInteger,
java.net.HttpURLConnection,
java.net.URLConnection,
java.net.URL,
java.net.URLEncoder,
java.net.URLDecoder,
java.security.MessageDigest,
java.security.NoSuchAlgorithmException,
javax.servlet.http.Cookie,
java.util.regex.Pattern,
java.util.regex.Matcher,
java.util.UUID" %><%!
/**
Copyright 2009 Google Inc. All Rights Reserved.
**/
// Tracker version.
private static final String version = "4.4sj";
private static final String COOKIE_NAME = "__utmmobile";
// The path the cookie will be available to, edit this to use a different
// cookie path.
private static final String COOKIE_PATH = "/";
// Two years in seconds.
private static final int COOKIE_USER_PERSISTENCE = 63072000;
// 1x1 transparent GIF
private static final byte[] GIF_DATA = new byte[] {
(byte)0x47, (byte)0x49, (byte)0x46, (byte)0x38, (byte)0x39, (byte)0x61,
(byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x80, (byte)0xff,
(byte)0x00, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0x00, (byte)0x00,
(byte)0x00, (byte)0x2c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
(byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x02,
(byte)0x02, (byte)0x44, (byte)0x01, (byte)0x00, (byte)0x3b
};
// A string is empty in our terms, if it is null, empty or a dash.
private static boolean isEmpty(String in) {
return in == null || "-".equals(in) || "".equals(in);
}
// The last octect of the IP address is removed to anonymize the user.
private static String getIP(String remoteAddress) {
if (isEmpty(remoteAddress)) {
return "";
}
// Capture the first three octects of the IP address and replace the forth
// with 0, e.g. 124.455.3.123 becomes 124.455.3.0
String regex = "^([^.]+\\.[^.]+\\.[^.]+\\.).*";
Pattern getFirstBitOfIPAddress = Pattern.compile(regex);
Matcher m = getFirstBitOfIPAddress.matcher(remoteAddress);
if (m.matches()) {
return m.group(1) + "0";
} else {
return "";
}
}
// Generate a visitor id for this hit.
// If there is a visitor id in the cookie, use that, otherwise
// use the guid if we have one, otherwise use a random number.
private static String getVisitorId(
String guid, String account, String userAgent, Cookie cookie)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
// If there is a value in the cookie, don't change it.
if (cookie != null && cookie.getValue() != null) {
return cookie.getValue();
}
String message;
if (!isEmpty(guid)) {
// Create the visitor id using the guid.
message = guid + account;
} else {
// otherwise this is a new user, create a new random id.
message = userAgent + getRandomNumber() + UUID.randomUUID().toString();
}
MessageDigest m = MessageDigest.getInstance("MD5");
m.update(message.getBytes("UTF-8"), 0, message.length());
byte[] sum = m.digest();
BigInteger messageAsNumber = new BigInteger(1, sum);
String md5String = messageAsNumber.toString(16);
// Pad to make sure id is 32 characters long.
while (md5String.length() < 32) {
md5String = "0" + md5String;
}
return "0x" + md5String.substring(0, 16);
}
// Get a random number string.
private static String getRandomNumber() {
return Integer.toString((int) (Math.random() * 0x7fffffff));
}
// Writes the bytes of a 1x1 transparent gif into the response.
private void writeGifData(HttpServletResponse response) throws IOException {
response.addHeader(
"Cache-Control",
"private, no-cache, no-cache=Set-Cookie, proxy-revalidate");
response.addHeader("Pragma", "no-cache");
response.addHeader("Expires", "Wed, 17 Sep 1975 21:32:10 GMT");
ServletOutputStream output = response.getOutputStream();
output.write(GIF_DATA);
output.flush();
}
// Make a tracking request to Google Analytics from this server.
// Copies the headers from the original request to the new one.
// If request containg utmdebug parameter, exceptions encountered
// communicating with Google Analytics are thown.
private void sendRequestToGoogleAnalytics(
String utmUrl, HttpServletRequest request) throws Exception {
try {
URL url = new URL(utmUrl);
URLConnection connection = url.openConnection();
connection.setUseCaches(false);
connection.addRequestProperty("User-Agent",
request.getHeader("User-Agent"));
connection.addRequestProperty("Accepts-Language",
request.getHeader("Accepts-Language"));
connection.getContent();
} catch (Exception e) {
if (request.getParameter("utmdebug") != null) {
throw new Exception(e);
}
}
}
// Track a page view, updates all the cookies and campaign tracker,
// makes a server side request to Google Analytics and writes the transparent
// gif byte data to the response.
private void trackPageView(
HttpServletRequest request, HttpServletResponse response)
throws Exception {
String timeStamp = Long.toString(System.currentTimeMillis() / 1000);
String domainName = request.getServerName();
if (isEmpty(domainName)) {
domainName = "";
}
// Get the referrer from the utmr parameter, this is the referrer to the
// page that contains the tracking pixel, not the referrer for tracking
// pixel.
String documentReferer = request.getParameter("utmr");
if (isEmpty(documentReferer)) {
documentReferer = "-";
} else {
documentReferer = URLDecoder.decode(documentReferer, "UTF-8");
}
String documentPath = request.getParameter("utmp");
if (isEmpty(documentPath)) {
documentPath = "";
} else {
documentPath = URLDecoder.decode(documentPath, "UTF-8");
}
String account = request.getParameter("utmac");
String userAgent = request.getHeader("User-Agent");
if (isEmpty(userAgent)) {
userAgent = "";
}
// Try and get visitor cookie from the request.
Cookie[] cookies = request.getCookies();
Cookie cookie = null;
if (cookies != null) {
for(int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals(COOKIE_NAME)) {
cookie = cookies[i];
}
}
}
String visitorId = getVisitorId(
request.getHeader("X-DCMGUID"), account, userAgent, cookie);
// Always try and add the cookie to the response.
Cookie newCookie = new Cookie(COOKIE_NAME, visitorId);
newCookie.setMaxAge(COOKIE_USER_PERSISTENCE);
newCookie.setPath(COOKIE_PATH);
response.addCookie(newCookie);
String utmGifLocation = "http://www.google-analytics.com/__utm.gif";
// Construct the gif hit url.
String utmUrl = utmGifLocation + "?" +
"utmwv=" + version +
"&utmn=" + getRandomNumber() +
"&utmhn=" + URLEncoder.encode(domainName, "UTF-8") +
"&utmr=" + URLEncoder.encode(documentReferer, "UTF-8") +
"&utmp=" + URLEncoder.encode(documentPath, "UTF-8") +
"&utmac=" + account +
"&utmcc=__utma%3D999.999.999.999.999.1%3B" +
"&utmvid=" + visitorId +
"&utmip=" + getIP(request.getRemoteAddr());
sendRequestToGoogleAnalytics(utmUrl, request);
// If the debug parameter is on, add a header to the response that contains
// the url that was used to contact Google Analytics.
if (request.getParameter("utmdebug") != null) {
response.setHeader("X-GA-MOBILE-URL", utmUrl);
}
// Finally write the gif data to the response.
writeGifData(response);
}
%><%
// Let exceptions bubble up to container, for better debugging.
trackPageView(request, response);
%>

View file

@ -1,35 +0,0 @@
<%@ page import="java.io.UnsupportedEncodingException,
java.net.URLEncoder" %>
<%!
// Copyright 2009 Google Inc. All Rights Reserved.
private static final String GA_ACCOUNT = "ACCOUNT ID GOES HERE";
private static final String GA_PIXEL = "ga.jsp";
private String googleAnalyticsGetImageUrl(
HttpServletRequest request) throws UnsupportedEncodingException {
StringBuilder url = new StringBuilder();
url.append(GA_PIXEL + "?");
url.append("utmac=").append(GA_ACCOUNT);
url.append("&utmn=").append(Integer.toString((int) (Math.random() * 0x7fffffff)));
String referer = request.getHeader("referer");
String query = request.getQueryString();
String path = request.getRequestURI();
if (referer == null || "".equals(referer)) {
referer = "-";
}
url.append("&utmr=").append(URLEncoder.encode(referer, "UTF-8"));
if (path != null) {
if (query != null) {
path += "?" + query;
}
url.append("&utmp=").append(URLEncoder.encode(path, "UTF-8"));
}
url.append("&guid=ON");
return url.toString();
}
%>

View file

@ -1,2 +0,0 @@
<% String googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(request); %>
<img src="<%= googleAnalyticsImageUrl %>" />

View file

@ -1,51 +0,0 @@
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import="java.io.UnsupportedEncodingException,
java.net.URLEncoder" %>
<%!
private static final String GA_ACCOUNT = "MO-3845491-5";
private static final String GA_PIXEL = "ga.jsp";
private String googleAnalyticsGetImageUrl(
HttpServletRequest request) throws UnsupportedEncodingException {
StringBuilder url = new StringBuilder();
url.append(GA_PIXEL + "?");
url.append("utmac=").append(GA_ACCOUNT);
url.append("&utmn=").append(Integer.toString((int) (Math.random() * 0x7fffffff)));
String referer = request.getHeader("referer");
String query = request.getQueryString();
String path = request.getRequestURI();
if (referer == null || "".equals(referer)) {
referer = "-";
}
url.append("&utmr=").append(URLEncoder.encode(referer, "UTF-8"));
if (path != null) {
if (query != null) {
path += "?" + query;
}
url.append("&utmp=").append(URLEncoder.encode(path, "UTF-8"));
}
url.append("&guid=ON");
return url.toString();
}
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Sample Mobile Analytics Page</title>
</head>
<body>
Publishers content here.
<%
String googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(request);
%>
<img src="<%= googleAnalyticsImageUrl %>" />
Testing: <%= googleAnalyticsImageUrl %>
</body>
</html>

View file

@ -1,176 +0,0 @@
<?php
/**
Copyright 2009 Google Inc. All Rights Reserved.
**/
// Tracker version.
define("VERSION", "4.4sh");
define("COOKIE_NAME", "__utmmobile");
// The path the cookie will be available to, edit this to use a different
// cookie path.
define("COOKIE_PATH", "/");
// Two years in seconds.
define("COOKIE_USER_PERSISTENCE", 63072000);
// 1x1 transparent GIF
$GIF_DATA = array(
chr(0x47), chr(0x49), chr(0x46), chr(0x38), chr(0x39), chr(0x61),
chr(0x01), chr(0x00), chr(0x01), chr(0x00), chr(0x80), chr(0xff),
chr(0x00), chr(0xff), chr(0xff), chr(0xff), chr(0x00), chr(0x00),
chr(0x00), chr(0x2c), chr(0x00), chr(0x00), chr(0x00), chr(0x00),
chr(0x01), chr(0x00), chr(0x01), chr(0x00), chr(0x00), chr(0x02),
chr(0x02), chr(0x44), chr(0x01), chr(0x00), chr(0x3b)
);
// The last octect of the IP address is removed to anonymize the user.
function getIP($remoteAddress) {
if (empty($remoteAddress)) {
return "";
}
// Capture the first three octects of the IP address and replace the forth
// with 0, e.g. 124.455.3.123 becomes 124.455.3.0
$regex = "/^([^.]+\.[^.]+\.[^.]+\.).*/";
if (preg_match($regex, $remoteAddress, $matches)) {
return $matches[1] . "0";
} else {
return "";
}
}
// Generate a visitor id for this hit.
// If there is a visitor id in the cookie, use that, otherwise
// use the guid if we have one, otherwise use a random number.
function getVisitorId($guid, $account, $userAgent, $cookie) {
// If there is a value in the cookie, don't change it.
if (!empty($cookie)) {
return $cookie;
}
$message = "";
if (!empty($guid)) {
// Create the visitor id using the guid.
$message = $guid . $account;
} else {
// otherwise this is a new user, create a new random id.
$message = $userAgent . uniqid(getRandomNumber(), true);
}
$md5String = md5($message);
return "0x" . substr($md5String, 0, 16);
}
// Get a random number string.
function getRandomNumber() {
return rand(0, 0x7fffffff);
}
// Writes the bytes of a 1x1 transparent gif into the response.
function writeGifData() {
global $GIF_DATA;
header("Content-Type: image/gif");
header("Cache-Control: " .
"private, no-cache, no-cache=Set-Cookie, proxy-revalidate");
header("Pragma: no-cache");
header("Expires: Wed, 17 Sep 1975 21:32:10 GMT");
echo join($GIF_DATA);
}
// Make a tracking request to Google Analytics from this server.
// Copies the headers from the original request to the new one.
// If request containg utmdebug parameter, exceptions encountered
// communicating with Google Analytics are thown.
function sendRequestToGoogleAnalytics($utmUrl) {
$options = array(
"http" => array(
"method" => "GET",
"user_agent" => $_SERVER["HTTP_USER_AGENT"],
"header" => ("Accepts-Language: " . $_SERVER["HTTP_ACCEPT_LANGUAGE"]))
);
if (!empty($_GET["utmdebug"])) {
$data = file_get_contents(
$utmUrl, false, stream_context_create($options));
} else {
$data = @file_get_contents(
$utmUrl, false, stream_context_create($options));
}
}
// Track a page view, updates all the cookies and campaign tracker,
// makes a server side request to Google Analytics and writes the transparent
// gif byte data to the response.
function trackPageView() {
$timeStamp = time();
$domainName = $_SERVER["SERVER_NAME"];
if (empty($domainName)) {
$domainName = "";
}
// Get the referrer from the utmr parameter, this is the referrer to the
// page that contains the tracking pixel, not the referrer for tracking
// pixel.
$documentReferer = $_GET["utmr"];
if (empty($documentReferer) && $documentReferer !== "0") {
$documentReferer = "-";
} else {
$documentReferer = urldecode($documentReferer);
}
$documentPath = $_GET["utmp"];
if (empty($documentPath)) {
$documentPath = "";
} else {
$documentPath = urldecode($documentPath);
}
$account = $_GET["utmac"];
$userAgent = $_SERVER["HTTP_USER_AGENT"];
if (empty($userAgent)) {
$userAgent = "";
}
// Try and get visitor cookie from the request.
$cookie = $_COOKIE[COOKIE_NAME];
$visitorId = getVisitorId(
$_SERVER["HTTP_X_DCMGUID"], $account, $userAgent, $cookie);
// Always try and add the cookie to the response.
setrawcookie(
COOKIE_NAME,
$visitorId,
$timeStamp + COOKIE_USER_PERSISTENCE,
COOKIE_PATH);
$utmGifLocation = "http://www.google-analytics.com/__utm.gif";
// Construct the gif hit url.
$utmUrl = $utmGifLocation . "?" .
"utmwv=" . VERSION .
"&utmn=" . getRandomNumber() .
"&utmhn=" . urlencode($domainName) .
"&utmr=" . urlencode($documentReferer) .
"&utmp=" . urlencode($documentPath) .
"&utmac=" . $account .
"&utmcc=__utma%3D999.999.999.999.999.1%3B" .
"&utmvid=" . $visitorId .
"&utmip=" . getIP($_SERVER["REMOTE_ADDR"]);
sendRequestToGoogleAnalytics($utmUrl);
// If the debug parameter is on, add a header to the response that contains
// the url that was used to contact Google Analytics.
if (!empty($_GET["utmdebug"])) {
header("X-GA-MOBILE-URL:" . $utmUrl);
}
// Finally write the gif data to the response.
writeGifData();
}
?><?php
trackPageView();
?>

View file

@ -1,30 +0,0 @@
<?php
// Copyright 2009 Google Inc. All Rights Reserved.
$GA_ACCOUNT = "ACCOUNT ID GOES HERE";
$GA_PIXEL = "ga.php";
function googleAnalyticsGetImageUrl() {
global $GA_ACCOUNT, $GA_PIXEL;
$url = "";
$url .= $GA_PIXEL . "?";
$url .= "utmac=" . $GA_ACCOUNT;
$url .= "&utmn=" . rand(0, 0x7fffffff);
$referer = $_SERVER["HTTP_REFERER"];
$query = $_SERVER["QUERY_STRING"];
$path = $_SERVER["REQUEST_URI"];
if (empty($referer)) {
$referer = "-";
}
$url .= "&utmr=" . urlencode($referer);
if (!empty($path)) {
$url .= "&utmp=" . urlencode($path);
}
$url .= "&guid=ON";
return $url;
}
?>

View file

@ -1,4 +0,0 @@
<?php
$googleAnalyticsImageUrl = googleAnalyticsGetImageUrl();
?>
<img src="<?= $googleAnalyticsImageUrl ?>" />

View file

@ -1,44 +0,0 @@
<?php
$GA_ACCOUNT = "MO-3845491-5";
$GA_PIXEL = "ga.php";
function googleAnalyticsGetImageUrl() {
global $GA_ACCOUNT, $GA_PIXEL;
$url = "";
$url .= $GA_PIXEL . "?";
$url .= "utmac=" . $GA_ACCOUNT;
$url .= "&utmn=" . rand(0, 0x7fffffff);
$referer = $_SERVER["HTTP_REFERER"];
$query = $_SERVER["QUERY_STRING"];
$path = $_SERVER["REQUEST_URI"];
if (empty($referer)) {
$referer = "-";
}
$url .= "&utmr=" . urlencode($referer);
if (!empty($path)) {
$url .= "&utmp=" . urlencode($path);
}
$url .= "&guid=ON";
return $url;
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Sample Mobile Analytics Page</title>
</head>
<body>
Publishers content here.
<?php
$googleAnalyticsImageUrl = googleAnalyticsGetImageUrl();
?>
<img src="<?= $googleAnalyticsImageUrl ?>" />
Testing: <?= $googleAnalyticsImageUrl ?>
</body>
</html>

View file

@ -1,195 +0,0 @@
#!/usr/bin/perl -w
#
# Copyright 2009 Google Inc. All Rights Reserved.
use CGI;
use Digest::MD5 qw(md5_hex);
use LWP::UserAgent;
use URI::Escape;
use strict;
# Tracker version.
use constant VERSION => '4.4sp';
use constant COOKIE_NAME => '__utmmobile';
# The path the cookie will be available to, edit this to use a different
# cookie path.
use constant COOKIE_PATH => '/';
# Two years.
use constant COOKIE_USER_PERSISTENCE => '+2y';
# 1x1 transparent GIF
my @GIF_DATA = (
0x47, 0x49, 0x46, 0x38, 0x39, 0x61,
0x01, 0x00, 0x01, 0x00, 0x80, 0xff,
0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x02,
0x02, 0x44, 0x01, 0x00, 0x3b);
my $query = new CGI;
# The last octect of the IP address is removed to anonymize the user.
sub get_ip {
my ($remote_address) = @_;
if ($remote_address eq "") {
return "";
}
# Capture the first three octects of the IP address and replace the forth
# with 0, e.g. 124.455.3.123 becomes 124.455.3.0
if ($remote_address =~ /^((\d{1,3}\.){3})\d{1,3}$/) {
return $1 . "0";
} else {
return "";
}
}
# Generate a visitor id for this hit.
# If there is a visitor id in the cookie, use that, otherwise
# use the guid if we have one, otherwise use a random number.
sub get_visitor_id {
my ($guid, $account, $user_agent, $cookie) = @_;
# If there is a value in the cookie, don't change it.
if ($cookie ne "") {
return $cookie;
}
my $message = "";
if ($guid ne "") {
# Create the visitor id using the guid.
$message = $guid . $account;
} else {
# otherwise this is a new user, create a new random id.
$message = $user_agent . get_random_number();
}
my $md5_string = md5_hex($message);
return "0x" . substr($md5_string, 0, 16);
}
# Get a random number string.
sub get_random_number {
return int(rand(0x7fffffff));
}
# Writes the bytes of a 1x1 transparent gif into the response.
sub write_gif_data {
my ($cookie, $utm_url) = @_;
my @header_args = (
-type => 'image/gif',
-Cache_Control =>
'private, no-cache, no-cache=Set-Cookie, proxy-revalidate',
-Pragma => 'no-cache',
-cookie => $cookie,
-expires => '-1d');
# If the debug parameter is on, add a header to the response that contains
# the url that was used to contact Google Analytics.
if (defined($query->param('utmdebug'))) {
push(@header_args, -X_GA_MOBILE_URL => $utm_url);
}
print $query->header(@header_args);
print pack("C35", @GIF_DATA);
}
# Make a tracking request to Google Analytics from this server.
# Copies the headers from the original request to the new one.
# If request containg utmdebug parameter, exceptions encountered
# communicating with Google Analytics are thown.
sub send_request_to_google_analytics {
my ($utm_url) = @_;
my $ua = LWP::UserAgent->new;
if (exists($ENV{'HTTP_ACCEPT_LANGUAGE'})) {
$ua->default_header('Accepts-Language' => $ENV{'HTTP_ACCEPT_LANGUAGE'});
}
if (exists($ENV{'HTTP_USER_AGENT'})) {
$ua->agent($ENV{'HTTP_USER_AGENT'});
}
my $ga_output = $ua->get($utm_url);
if (defined($query->param('utmdebug')) && !$ga_output->is_success) {
print $ga_output->status_line;
}
}
# Track a page view, updates all the cookies and campaign tracker,
# makes a server side request to Google Analytics and writes the transparent
# gif byte data to the response.
sub track_page_view {
my $domain_name = "";
if (exists($ENV{'SERVER_NAME'})) {
$domain_name = $ENV{'SERVER_NAME'};
}
# Get the referrer from the utmr parameter, this is the referrer to the
# page that contains the tracking pixel, not the referrer for tracking
# pixel.
my $document_referer = "-";
if (defined($query->param('utmr'))) {
$document_referer = uri_unescape($query->param('utmr'));
}
my $document_path = "";
if (defined($query->param('utmp'))) {
$document_path = uri_unescape($query->param('utmp'));
}
my $account = $query->param('utmac');
my $user_agent = "";
if (exists($ENV{'HTTP_USER_AGENT'})) {
$user_agent = $ENV{'HTTP_USER_AGENT'};
}
# Try and get visitor cookie from the request.
my $cookie = "";
if (defined($query->cookie(COOKIE_NAME))) {
$cookie = $query->cookie(COOKIE_NAME);
}
my $guid = "";
if (exists($ENV{'HTTP_X_DCMGUID'})) {
$guid = $ENV{'HTTP_X_DCMGUID'};
}
my $visitor_id = get_visitor_id($guid, $account, $user_agent, $cookie);
# Always try and add the cookie to the response.
my $new_cookie = $query->cookie(
-name => COOKIE_NAME,
-value => $visitor_id,
-path => COOKIE_PATH,
-expires => COOKIE_USER_PERSISTENCE);
my $utm_gif_location = "http://www.google-analytics.com/__utm.gif";
my $remote_address = "";
if (exists($ENV{'REMOTE_ADDR'})) {
$remote_address = $ENV{'REMOTE_ADDR'};
}
# Construct the gif hit url.
my $utm_url = $utm_gif_location . '?' .
'utmwv=' . VERSION .
'&utmn=' . get_random_number() .
'&utmhn=' . uri_escape($domain_name) .
'&utmr=' . uri_escape($document_referer) .
'&utmp=' . uri_escape($document_path) .
'&utmac=' . $account .
'&utmcc=__utma%3D999.999.999.999.999.1%3B' .
'&utmvid=' . $visitor_id .
'&utmip=' . get_ip($remote_address);
send_request_to_google_analytics($utm_url);
# Finally write the gif data to the response.
write_gif_data($new_cookie, $utm_url);
}
track_page_view();

View file

@ -1,27 +0,0 @@
# Copyright 2009 Google Inc. All Rights Reserved.
use URI::Escape;
use constant GA_ACCOUNT => 'ACCOUNT ID GOES HERE';
use constant GA_PIXEL => 'ga.pl';
sub google_analytics_get_image_url {
my $url = '';
$url .= GA_PIXEL . '?';
$url .= 'utmac=' . GA_ACCOUNT;
$url .= '&utmn=' . int(rand(0x7fffffff));
my $referer = $ENV{'HTTP_REFERER'};
my $query = $ENV{'QUERY_STRING'};
my $path = $ENV{'REQUEST_URI'};
if ($referer eq "") {
$referer = '-';
}
$url .= '&utmr=' . uri_escape($referer);
$url .= '&utmp=' . uri_escape($path);
$url .= '&guid=ON';
$url;
}

View file

@ -1 +0,0 @@
print '<img src="' . google_analytics_get_image_url() . '" />';

View file

@ -1,38 +0,0 @@
#!/usr/bin/perl -w
#
# Copyright 2009 Google Inc. All Rights Reserved.
use strict;
use URI::Escape;
use constant GA_ACCOUNT => 'MO-3845491-5';
use constant GA_PIXEL => 'ga.pl';
sub google_analytics_get_image_url {
my $url = '';
$url .= GA_PIXEL . '?';
$url .= 'utmac=' . GA_ACCOUNT;
$url .= '&utmn=' . int(rand(0x7fffffff));
my $referer = $ENV{'HTTP_REFERER'};
my $query = $ENV{'QUERY_STRING'};
my $path = $ENV{'REQUEST_URI'};
if ($referer eq "") {
$referer = '-';
}
$url .= '&utmr=' . uri_escape($referer);
$url .= '&utmp=' . uri_escape($path);
$url .= '&guid=ON';
$url;
}
print "Content-Type: text/html; charset=ISO-8859-1\n\n";
print '<html><head></head><body>';
print '<img src="' . google_analytics_get_image_url() . '" />';
print google_analytics_get_image_url();
print '</body></html>';
exit (0);

View file

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,559 +0,0 @@
/*
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview Bookmark bubble library. This is meant to be included in the
* main JavaScript binary of a mobile web application.
*
* Supported browsers: iPhone / iPod / iPad Safari 3.0+
*/
var google = google || {};
google.bookmarkbubble = google.bookmarkbubble || {};
/**
* Binds a context object to the function.
* @param {Function} fn The function to bind to.
* @param {Object} context The "this" object to use when the function is run.
* @return {Function} A partially-applied form of fn.
*/
google.bind = function(fn, context) {
return function() {
return fn.apply(context, arguments);
};
};
/**
* Function used to define an abstract method in a base class. If a subclass
* fails to override the abstract method, then an error will be thrown whenever
* that method is invoked.
*/
google.abstractMethod = function() {
throw Error('Unimplemented abstract method.');
};
/**
* The bubble constructor. Instantiating an object does not cause anything to
* be rendered yet, so if necessary you can set instance properties before
* showing the bubble.
* @constructor
*/
google.bookmarkbubble.Bubble = function() {
/**
* Handler for the scroll event. Keep a reference to it here, so it can be
* unregistered when the bubble is destroyed.
* @type {function()}
* @private
*/
this.boundScrollHandler_ = google.bind(this.setPosition, this);
/**
* The bubble element.
* @type {Element}
* @private
*/
this.element_ = null;
/**
* Whether the bubble has been destroyed.
* @type {boolean}
* @private
*/
this.hasBeenDestroyed_ = false;
};
/**
* Shows the bubble if allowed. It is not allowed if:
* - The browser is not Mobile Safari, or
* - The user has dismissed it too often already, or
* - The hash parameter is present in the location hash, or
* - The application is in fullscreen mode, which means it was already loaded
* from a homescreen bookmark.
* @return {boolean} True if the bubble is being shown, false if it is not
* allowed to show for one of the aforementioned reasons.
*/
google.bookmarkbubble.Bubble.prototype.showIfAllowed = function() {
if (!this.isAllowedToShow_()) {
return false;
}
this.show_();
return true;
};
/**
* Shows the bubble if allowed after loading the icon image. This method creates
* an image element to load the image into the browser's cache before showing
* the bubble to ensure that the image isn't blank. Use this instead of
* showIfAllowed if the image url is http and cacheable.
* This hack is necessary because Mobile Safari does not properly render
* image elements with border-radius CSS.
* @param {function()} opt_callback Closure to be called if and when the bubble
* actually shows.
* @return {boolean} True if the bubble is allowed to show.
*/
google.bookmarkbubble.Bubble.prototype.showIfAllowedWhenLoaded =
function(opt_callback) {
if (!this.isAllowedToShow_()) {
return false;
}
var self = this;
// Attach to self to avoid garbage collection.
var img = self.loadImg_ = document.createElement('img');
img.src = self.getIconUrl_();
img.onload = function() {
if (img.complete) {
delete self.loadImg_;
img.onload = null; // Break the circular reference.
self.show_();
opt_callback && opt_callback();
}
};
img.onload();
return true;
};
/**
* Sets the parameter in the location hash. As it is
* unpredictable what hash scheme is to be used, this method must be
* implemented by the host application.
*
* This gets called automatically when the bubble is shown. The idea is that if
* the user then creates a bookmark, we can later recognize on application
* startup whether it was from a bookmark suggested with this bubble.
*/
google.bookmarkbubble.Bubble.prototype.setHashParameter = google.abstractMethod;
/**
* Whether the parameter is present in the location hash. As it is
* unpredictable what hash scheme is to be used, this method must be
* implemented by the host application.
*
* Call this method during application startup if you want to log whether the
* application was loaded from a bookmark with the bookmark bubble promotion
* parameter in it.
*
* @return {boolean} Whether the bookmark bubble parameter is present in the
* location hash.
*/
google.bookmarkbubble.Bubble.prototype.hasHashParameter = google.abstractMethod;
/**
* The number of times the user must dismiss the bubble before we stop showing
* it. This is a public property and can be changed by the host application if
* necessary.
* @type {number}
*/
google.bookmarkbubble.Bubble.prototype.NUMBER_OF_TIMES_TO_DISMISS = 2;
/**
* Time in milliseconds. If the user does not dismiss the bubble, it will auto
* destruct after this amount of time.
* @type {number}
*/
google.bookmarkbubble.Bubble.prototype.TIME_UNTIL_AUTO_DESTRUCT = 15000;
/**
* The prefix for keys in local storage. This is a public property and can be
* changed by the host application if necessary.
* @type {string}
*/
google.bookmarkbubble.Bubble.prototype.LOCAL_STORAGE_PREFIX = 'BOOKMARK_';
/**
* The key name for the dismissed state.
* @type {string}
* @private
*/
google.bookmarkbubble.Bubble.prototype.DISMISSED_ = 'DISMISSED_COUNT';
/**
* The arrow image in base64 data url format.
* @type {string}
* @private
*/
google.bookmarkbubble.Bubble.prototype.IMAGE_ARROW_DATA_URL_ = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAATCAMAAABSrFY3AAABKVBMVEUAAAD///8AAAAAAAAAAAAAAAAAAADf398AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD09PQAAAAAAAAAAAC9vb0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD19fUAAAAAAAAAAAAAAADq6uoAAAAAAAAAAAC8vLzU1NTT09MAAADg4OAAAADs7OwAAAAAAAAAAAD///+cueenwerA0vC1y+3a5fb5+/3t8vr4+v3w9PuwyOy3zO3h6vfh6vjq8Pqkv+mat+fE1fHB0/Cduuifu+iuxuuivemrxOvC1PDz9vzJ2fKpwuqmwOrb5vapw+q/0vDf6ffK2vLN3PPprJISAAAAQHRSTlMAAAEGExES7FM+JhUoQSxIRwMbNfkJUgXXBE4kDQIMHSA0Tw4xIToeTSc4Chz4OyIjPfI3QD/X5OZR6zzwLSUPrm1y3gAAAQZJREFUeF5lzsVyw0AURNE3IMsgmZmZgszQZoeZOf//EYlG5Yrhbs+im4Dj7slM5wBJ4OJ+undAUr68gK/Hyb6Bcp5yBR/w8jreNeAr5Eg2XE7g6e2/0z6cGw1JQhpmHP3u5aiPPnTTkIK48Hj9Op7bD3btAXTfgUdwYjwSDCVXMbizO0O4uDY/x4kYC5SWFnfC6N1a9RCO7i2XEmQJj2mHK1Hgp9Vq3QBRl9shuBLGhcNtHexcdQCnDUoUGetxDD+H2DQNG2xh6uAWgG2/17o1EmLqYH0Xej0UjHAaFxZIV6rJ/WK1kg7QZH8HU02zmdJinKZJaDV3TVMjM5Q9yiqYpUwiMwa/1apDXTNESjsAAAAASUVORK5CYII=';
/**
* The close image in base64 data url format.
* @type {string}
* @private
*/
google.bookmarkbubble.Bubble.prototype.IMAGE_CLOSE_DATA_URL_ = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAALVBMVEXM3fm+1Pfb5/rF2fjw9f23z/aavPOhwfTp8PyTt/L3+v7T4vqMs/K7zP////+qRWzhAAAAXElEQVQIW2O4CwUM996BwVskxtOqd++2rwMyPI+ve31GD8h4Madqz2mwms5jZ/aBGS/mHIDoen3m+DowY8/hOVUgxusz+zqPg7SvPA1UxQfSvu/du0YUK2AMmDMA5H1qhVX33T8AAAAASUVORK5CYII=';
/**
* The link used to locate the application's home screen icon to display inside
* the bubble. The default link used here is for an iPhone home screen icon
* without gloss. If your application uses a glossy icon, change this to
* 'apple-touch-icon'.
* @type {string}
* @private
*/
google.bookmarkbubble.Bubble.prototype.REL_ICON_ =
'apple-touch-icon-precomposed';
/**
* Regular expression for detecting an iPhone or iPod or iPad.
* @type {!RegExp}
* @private
*/
google.bookmarkbubble.Bubble.prototype.MOBILE_SAFARI_USERAGENT_REGEX_ =
/iPhone|iPod|iPad/;
/**
* Regular expression for detecting an iPad.
* @type {!RegExp}
* @private
*/
google.bookmarkbubble.Bubble.prototype.IPAD_USERAGENT_REGEX_ = /iPad/;
/**
* Determines whether the bubble should be shown or not.
* @return {boolean} Whether the bubble should be shown or not.
* @private
*/
google.bookmarkbubble.Bubble.prototype.isAllowedToShow_ = function() {
return this.isMobileSafari_() &&
!this.hasBeenDismissedTooManyTimes_() &&
!this.isFullscreen_() &&
!this.hasHashParameter();
};
/**
* Builds and shows the bubble.
* @private
*/
google.bookmarkbubble.Bubble.prototype.show_ = function() {
this.element_ = this.build_();
document.body.appendChild(this.element_);
this.element_.style.WebkitTransform =
'translateY(' + this.getHiddenYPosition_() + 'px)';
this.setHashParameter();
window.setTimeout(this.boundScrollHandler_, 1);
window.addEventListener('scroll', this.boundScrollHandler_, false);
// If the user does not dismiss the bubble, slide out and destroy it after
// some time.
window.setTimeout(google.bind(this.autoDestruct_, this),
this.TIME_UNTIL_AUTO_DESTRUCT);
};
/**
* Destroys the bubble by removing its DOM nodes from the document.
*/
google.bookmarkbubble.Bubble.prototype.destroy = function() {
if (this.hasBeenDestroyed_) {
return;
}
window.removeEventListener('scroll', this.boundScrollHandler_, false);
if (this.element_ && this.element_.parentNode == document.body) {
document.body.removeChild(this.element_);
this.element_ = null;
}
this.hasBeenDestroyed_ = true;
};
/**
* Remember that the user has dismissed the bubble once more.
* @private
*/
google.bookmarkbubble.Bubble.prototype.rememberDismissal_ = function() {
if (window.localStorage) {
try {
var key = this.LOCAL_STORAGE_PREFIX + this.DISMISSED_;
var value = Number(window.localStorage[key]) || 0;
window.localStorage[key] = String(value + 1);
} catch (ex) {
// Looks like we've hit the storage size limit. Currently we have no
// fallback for this scenario, but we could use cookie storage instead.
// This would increase the code bloat though.
}
}
};
/**
* Whether the user has dismissed the bubble often enough that we will not
* show it again.
* @return {boolean} Whether the user has dismissed the bubble often enough
* that we will not show it again.
* @private
*/
google.bookmarkbubble.Bubble.prototype.hasBeenDismissedTooManyTimes_ =
function() {
if (!window.localStorage) {
// If we can not use localStorage to remember how many times the user has
// dismissed the bubble, assume he has dismissed it. Otherwise we might end
// up showing it every time the host application loads, into eternity.
return true;
}
try {
var key = this.LOCAL_STORAGE_PREFIX + this.DISMISSED_;
// If the key has never been set, localStorage yields undefined, which
// Number() turns into NaN. In that case we'll fall back to zero for
// clarity's sake.
var value = Number(window.localStorage[key]) || 0;
return value >= this.NUMBER_OF_TIMES_TO_DISMISS;
} catch (ex) {
// If we got here, something is wrong with the localStorage. Make the same
// assumption as when it does not exist at all. Exceptions should only
// occur when setting a value (due to storage limitations) but let's be
// extra careful.
return true;
}
};
/**
* Whether the application is running in fullscreen mode.
* @return {boolean} Whether the application is running in fullscreen mode.
* @private
*/
google.bookmarkbubble.Bubble.prototype.isFullscreen_ = function() {
return !!window.navigator.standalone;
};
/**
* Whether the application is running inside Mobile Safari.
* @return {boolean} True if the current user agent looks like Mobile Safari.
* @private
*/
google.bookmarkbubble.Bubble.prototype.isMobileSafari_ = function() {
return this.MOBILE_SAFARI_USERAGENT_REGEX_.test(window.navigator.userAgent);
};
/**
* Whether the application is running on an iPad.
* @return {boolean} True if the current user agent looks like an iPad.
* @private
*/
google.bookmarkbubble.Bubble.prototype.isIpad_ = function() {
return this.IPAD_USERAGENT_REGEX_.test(window.navigator.userAgent);
};
/**
* Positions the bubble at the bottom of the viewport using an animated
* transition.
*/
google.bookmarkbubble.Bubble.prototype.setPosition = function() {
this.element_.style.WebkitTransition = '-webkit-transform 0.7s ease-out';
this.element_.style.WebkitTransform =
'translateY(' + this.getVisibleYPosition_() + 'px)';
};
/**
* Destroys the bubble by removing its DOM nodes from the document, and
* remembers that it was dismissed.
* @private
*/
google.bookmarkbubble.Bubble.prototype.closeClickHandler_ = function() {
this.destroy();
this.rememberDismissal_();
};
/**
* Gets called after a while if the user ignores the bubble.
* @private
*/
google.bookmarkbubble.Bubble.prototype.autoDestruct_ = function() {
if (this.hasBeenDestroyed_) {
return;
}
this.element_.style.WebkitTransition = '-webkit-transform 0.7s ease-in';
this.element_.style.WebkitTransform =
'translateY(' + this.getHiddenYPosition_() + 'px)';
window.setTimeout(google.bind(this.destroy, this), 700);
};
/**
* Gets the y offset used to show the bubble (i.e., position it on-screen).
* @return {number} The y offset.
* @private
*/
google.bookmarkbubble.Bubble.prototype.getVisibleYPosition_ = function() {
return this.isIpad_() ? window.pageYOffset + 17 :
window.pageYOffset - this.element_.offsetHeight + window.innerHeight - 17;
};
/**
* Gets the y offset used to hide the bubble (i.e., position it off-screen).
* @return {number} The y offset.
* @private
*/
google.bookmarkbubble.Bubble.prototype.getHiddenYPosition_ = function() {
return this.isIpad_() ? window.pageYOffset - this.element_.offsetHeight :
window.pageYOffset + window.innerHeight;
};
/**
* The url of the app's bookmark icon.
* @type {string|undefined}
* @private
*/
google.bookmarkbubble.Bubble.prototype.iconUrl_;
/**
* Scrapes the document for a link element that specifies an Apple favicon and
* returns the icon url. Returns an empty data url if nothing can be found.
* @return {string} A url string.
* @private
*/
google.bookmarkbubble.Bubble.prototype.getIconUrl_ = function() {
if (!this.iconUrl_) {
var link = this.getLink(this.REL_ICON_);
if (!link || !(this.iconUrl_ = link.href)) {
this.iconUrl_ = 'data:image/png;base64,';
}
}
return this.iconUrl_;
};
/**
* Gets the requested link tag if it exists.
* @param {string} rel The rel attribute of the link tag to get.
* @return {Element} The requested link tag or null.
*/
google.bookmarkbubble.Bubble.prototype.getLink = function(rel) {
rel = rel.toLowerCase();
var links = document.getElementsByTagName('link');
for (var i = 0; i < links.length; ++i) {
var currLink = /** @type {Element} */ (links[i]);
if (currLink.getAttribute('rel').toLowerCase() == rel) {
return currLink;
}
}
return null;
};
/**
* Creates the bubble and appends it to the document.
* @return {Element} The bubble element.
* @private
*/
google.bookmarkbubble.Bubble.prototype.build_ = function() {
var bubble = document.createElement('div');
var isIpad = this.isIpad_();
bubble.style.position = 'absolute';
bubble.style.zIndex = 1000;
bubble.style.width = '100%';
bubble.style.left = '0';
bubble.style.top = '0';
var bubbleInner = document.createElement('div');
bubbleInner.style.position = 'relative';
bubbleInner.style.width = '214px';
bubbleInner.style.margin = isIpad ? '0 0 0 82px' : '0 auto';
bubbleInner.style.border = '2px solid #fff';
bubbleInner.style.padding = '20px 20px 20px 10px';
bubbleInner.style.WebkitBorderRadius = '8px';
bubbleInner.style.WebkitBoxShadow = '0 0 8px rgba(0, 0, 0, 0.7)';
bubbleInner.style.WebkitBackgroundSize = '100% 8px';
bubbleInner.style.backgroundColor = '#b0c8ec';
bubbleInner.style.background = '#cddcf3 -webkit-gradient(linear, ' +
'left bottom, left top, ' + isIpad ?
'from(#cddcf3), to(#b3caed)) no-repeat top' :
'from(#b3caed), to(#cddcf3)) no-repeat bottom';
bubbleInner.style.font = '13px/17px sans-serif';
bubble.appendChild(bubbleInner);
// The "Add to Home Screen" text is intended to be the exact same size text
// that is displayed in the menu of Mobile Safari on iPhone.
bubbleInner.innerHTML = 'Install this web app on your phone: tap ' +
'<b style="font-size:15px">+</b> and then <b>\'Add to Home Screen\'</b>';
var icon = document.createElement('div');
icon.style['float'] = 'left';
icon.style.width = '55px';
icon.style.height = '55px';
icon.style.margin = '-2px 7px 3px 5px';
icon.style.background =
'#fff url(' + this.getIconUrl_() + ') no-repeat -1px -1px';
icon.style.WebkitBackgroundSize = '57px';
icon.style.WebkitBorderRadius = '10px';
icon.style.WebkitBoxShadow = '0 2px 5px rgba(0, 0, 0, 0.4)';
bubbleInner.insertBefore(icon, bubbleInner.firstChild);
var arrow = document.createElement('div');
arrow.style.backgroundImage = 'url(' + this.IMAGE_ARROW_DATA_URL_ + ')';
arrow.style.width = '25px';
arrow.style.height = '19px';
arrow.style.position = 'absolute';
arrow.style.left = '111px';
if (isIpad) {
arrow.style.WebkitTransform = 'rotate(180deg)';
arrow.style.top = '-19px';
} else {
arrow.style.bottom = '-19px';
}
bubbleInner.appendChild(arrow);
var close = document.createElement('a');
close.onclick = google.bind(this.closeClickHandler_, this);
close.style.position = 'absolute';
close.style.display = 'block';
close.style.top = '-3px';
close.style.right = '-3px';
close.style.width = '16px';
close.style.height = '16px';
close.style.border = '10px solid transparent';
close.style.background =
'url(' + this.IMAGE_CLOSE_DATA_URL_ + ') no-repeat';
bubbleInner.appendChild(close);
return bubble;
};

View file

@ -1,43 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Sample</title>
<meta name="viewport"
content="width=device-width,minimum-scale=1.0,maximum-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<link rel="apple-touch-icon-precomposed" href="../images/icon_calendar.png" />
<script type="text/javascript" src="../bookmark_bubble.js"></script>
<script type="text/javascript" src="example.js"></script>
</head>
<body style="height: 3000px;">
<p>The bookmark bubble will show after a second, if:</p>
<ul>
<li>It has not been previously dismissed too often</li>
<li>The application is not running in full screen mode</li>
<li>The bookmark bubble hash token is not present</li>
</ul>
<p>Supported browsers:</p>
<ul>
<li>iPhone / iPod / iPad Mobile Safari 3.0+</li>
</ul>
</body>
</html>

View file

@ -1,57 +0,0 @@
/*
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/** @fileoverview Example of how to use the bookmark bubble. */
window.addEventListener('load', function() {
window.setTimeout(function() {
var bubble = new google.bookmarkbubble.Bubble();
var parameter = 'bmb=1';
bubble.hasHashParameter = function() {
return window.location.hash.indexOf(parameter) != -1;
};
bubble.setHashParameter = function() {
if (!this.hasHashParameter()) {
window.location.hash += parameter;
}
};
bubble.getViewportHeight = function() {
window.console.log('Example of how to override getViewportHeight.');
return window.innerHeight;
};
bubble.getViewportScrollY = function() {
window.console.log('Example of how to override getViewportScrollY.');
return window.pageYOffset;
};
bubble.registerScrollHandler = function(handler) {
window.console.log('Example of how to override registerScrollHandler.');
window.addEventListener('scroll', handler, false);
};
bubble.deregisterScrollHandler = function(handler) {
window.console.log('Example of how to override deregisterScrollHandler.');
window.removeEventListener('scroll', handler, false);
};
bubble.showIfAllowed();
}, 1000);
}, false);

View file

@ -1,33 +0,0 @@
#!/bin/bash
# Copyright 2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generates base64 versions of images so they can be inlined using the 'data:'
# URI scheme.
declare -ra IMAGE_FILES=( close.png arrow.png )
for image in ${IMAGE_FILES[@]}; do
OUT="$image.base64"
cat "$image" \
| uuencode -m ignore-this \
| grep -v begin-base64 \
| grep -v "====" \
| xargs echo \
| sed -e 's/ //g' \
| xargs echo -n \
> $OUT
ls -l $OUT
done

View file

@ -1,27 +0,0 @@
How to run the tests:
* The tests themselves assume that jsunit is in a sibling directory
to the one containing the distrbution. If this is not so, it is sufficient
to edit the paths in the test files. (On UNIX, symlinks may be your
friend if this is not convenient.)
* jsmock.js (available from http://jsmock.sourceforge.net/) should
be placed in the distribution directory.
* Specify the test files via a URL parameter. (This might be an issue
with jsunit: http://digitalmihailo.blogspot.com/2008/06/make-jsunit-work-in-firefox-30.html)
For example, if the root of your downloaded of the distribution is /mypath:
file:///mypath/jsunit/testRunner.html?testpage=mypath/webstorageportabilitylayer/dbwrapper_gears_test.html
file:///mypath/jsunit/testRunner.html?testpage=mypath/webstorageportabilitylayer/dbwrapper_html5_test.html
file:///mypath/jsunit/testRunner.html?testpage=mypath/webstorageportabilitylayer/dbwrapperapi_test.html
NB: the leading / in a UNIX path is not included in mypath so setting
it via pwd will not deliver the desired effect.

View file

@ -1,45 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Namespace.
google.wspl.DatabaseFactory = google.wspl.DatabaseFactory || {};
/**
* Factory function to build databases in a cross-API manner.
* @param {string} dbName of the database
* @param {string} dbworkerUrl the URL for Gears worker.
* @return {google.wspl.Database} The database object.
*/
google.wspl.DatabaseFactory.createDatabase = function(dbName, dbworkerUrl) {
var dbms;
if (window.openDatabase) {
// We have HTML5 functionality.
dbms = new google.wspl.html5.Database(dbName);
} else {
// Try to use Google Gears.
var gearsDb = goog.gears.getFactory().create('beta.database');
var wp = goog.gears.getFactory().create('beta.workerpool');
// Note that Gears will not allow file based URLs when creating a worker.
dbms = new wireless.db.gears.Database();
dbms.openDatabase('', dbName, gearsDb);
wp.onmessage = google.bind(dbms.onMessage_, dbms);
// Comment this line out to use the synchronous database.
dbms.startWorker(wp, dbworkerUrl, 0);
}
return dbms;
};

View file

@ -1,324 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview A worker thread that performs synchronous queries against a
* Gears database on behalf of an asynchronous calling client.
*
* The worker replies to the sender with messages to pass results, errors, and
* notifications about completed transactions. The type field of the message
* body specifies the message type. For each successful statement, a RESULT
* message is sent with a result attribute containing the Gears result set. For
* the first unsuccessful statement, an ERROR message will be sent with details
* stored in the error field. After the transaction has been committed, a COMMIT
* message is sent. If the transaction is rolled back, a ROLLBACK message is
* sent.
*
* NB: The worker must be served over http. Further, to operate successfully,
* it requires the inclusion of global_functions.js and gearsutils.js.
*/
/**
* Creates a DbWorker to handle incoming messages, execute queries, and return
* results to the main thread.
*
* @param {GearsWorkerPool} wp The gears worker pool.
* @constructor
*/
google.wspl.gears.DbWorker = function(wp) {
/**
* An array of transaction ids representing the transactions that are open on
* the database.
* @type {Array.<number>}
* @private
*/
this.transactions_ = [];
/**
* The gears worker pool.
* @type {GearsWorkerPool}
* @private
*/
this.wp_ = wp;
this.wp_.onmessage = google.bind(this.onMessage_, this);
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.STARTED
});
};
/**
* The gears database that this worker thread will interact with.
* @type {GearsDatabase}
* @private
*/
google.wspl.gears.DbWorker.prototype.db_;
/**
* A singleton instance of DbWorker.
* @type {google.wspl.gears.DbWorker?}
* @private
*/
google.wspl.gears.DbWorker.instance_;
/**
* The sender ID of the incomming messages. Default to 0 for workerpool ID.
* @type {number}
* @private
*/
google.wspl.gears.DbWorker.prototype.senderId_ = 0;
/**
* Message type constants for worker command messages.
* @enum {number}
*/
google.wspl.gears.DbWorker.CommandTypes = {
OPEN: 1,
BEGIN: 2,
EXECUTE: 3,
COMMIT: 4,
ROLLBACK: 5
};
/**
* Message type constants for worker reply messages.
* @enum {number}
*/
google.wspl.gears.DbWorker.ReplyTypes = {
RESULT: 1,
FAILURE: 2,
COMMIT: 3,
ROLLBACK: 4,
STARTED: 5,
OPEN_SUCCESSFUL: 6,
OPEN_FAILED: 7,
LOG: 8
};
/**
* Starts the DbWorker.
*/
google.wspl.gears.DbWorker.start = function() {
var wp = google.gears.workerPool;
google.wspl.gears.DbWorker.instance_ = new google.wspl.gears.DbWorker(wp);
};
/**
* Handles an OPEN command from the main thread.
*
* @param {string} userId The user to which the database belongs.
* @param {string} name The database's name.
*/
google.wspl.gears.DbWorker.prototype.handleOpen_ = function(userId, name) {
this.log_('Attempting to create Gears database: userId=' + userId + ', name='
+ name);
try {
this.db_ = google.gears.factory.create('beta.database', '1.0');
google.wspl.GearsUtils.openDatabase(userId, name, this.db_, this.log_);
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.OPEN_SUCCESSFUL
});
} catch (ex) {
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.OPEN_FAILED,
'error': ex
});
}
};
/**
* Handles a EXECUTE command from the main thread.
*
* @param {!Array.<google.wspl.Statement>} statements The statements to execute.
* @param {number} callbackId The callback to invoke after each execution.
* @param {number} transactionId The transaction that the statements belong to.
* @private
*/
google.wspl.gears.DbWorker.prototype.handleExecute_ =
function(statements, callbackId, transactionId) {
var self = this;
try {
this.executeAll_(statements, function(results) {
self.sendMessageToWorker_(/** @type {string} */({
'type': google.wspl.gears.DbWorker.ReplyTypes.RESULT,
'results': results,
'callbackId': callbackId,
'transactionId': transactionId
}));
});
} catch (e) {
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.FAILURE,
'error': e,
'callbackId': callbackId,
'transactionId': transactionId
});
}
};
/**
* Executes all of the statements on the Gears database. The callback is
* invoked with the query results after each successful query execution.
*
* @param {!Array.<Object>} statements The statements to execute.
* @param {Function} callback The callback to invoke with query results.
* @private
*/
google.wspl.gears.DbWorker.prototype.executeAll_ =
function(statements, callback) {
var results = [];
for (var i = 0; i < statements.length; i++) {
var resultset = this.db_.execute(statements[i]['sql'],
statements[i]['params']);
var result = google.wspl.GearsUtils.resultSetToObjectArray(resultset);
results.push(result);
}
callback(results);
};
/**
* Handles a BEGIN command from the main thread.
*
* @param {number} transactionId The transaction that the statements belong to.
* @private
*/
google.wspl.gears.DbWorker.prototype.handleBegin_ = function(transactionId) {
this.transactions_.push(transactionId);
this.db_.execute('BEGIN IMMEDIATE');
};
/**
* Handles a COMMIT command from the main thread.
*
* @param {number} transactionId The transaction that the statements belong to.
* @private
*/
google.wspl.gears.DbWorker.prototype.handleCommit_ = function(transactionId) {
this.db_.execute('COMMIT');
this.postCommit_();
};
/**
* Handles a ROLLBACK command from the main thread.
*
* @param {number} transactionId The transaction that the statements belong to.
* @private
*/
google.wspl.gears.DbWorker.prototype.handleRollback_ = function(transactionId) {
this.db_.execute('ROLLBACK');
this.postRollback_();
};
/**
* Sends a COMMIT reply to the main thread for each transaction that was
* committed.
*
* @private
*/
google.wspl.gears.DbWorker.prototype.postCommit_ = function() {
for (var i = this.transactions_.length - 1; i >= 0; i--) {
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.COMMIT,
'transactionId': this.transactions_[i]
});
}
this.transactions_ = [];
};
/**
* Sends a ROLLBACK reply to the main thread for each transaction that was
* rolled back.
*
* @private
*/
google.wspl.gears.DbWorker.prototype.postRollback_ = function() {
for (var i = this.transactions_.length - 1; i >= 0; i --) {
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.ROLLBACK,
'transactionId': this.transactions_[i]
});
}
this.transactions_ = [];
};
/**
* Handles incomming messages.
* @param {string} a Deprecated.
* @param {number} b Deprecated.
* @param {Object} messageObject The message object.
* @private
*/
google.wspl.gears.DbWorker.prototype.onMessage_ =
function(a, b, messageObject) {
this.senderId_ = messageObject.sender;
var message = messageObject.body;
var type = message['type'];
var name = message['name'];
var statements = message['statements'];
var callbackId = message['callbackId'];
var transactionId = message['transactionId'];
var userId = message['userId'];
try {
switch(type) {
case google.wspl.gears.DbWorker.CommandTypes.OPEN:
this.handleOpen_(userId, name);
break;
case google.wspl.gears.DbWorker.CommandTypes.EXECUTE:
this.handleExecute_(statements, callbackId, transactionId);
break;
case google.wspl.gears.DbWorker.CommandTypes.BEGIN:
this.handleBegin_(transactionId);
break;
case google.wspl.gears.DbWorker.CommandTypes.COMMIT:
this.handleCommit_(transactionId);
break;
case google.wspl.gears.DbWorker.CommandTypes.ROLLBACK:
this.handleRollback_(transactionId);
break;
}
} catch (ex) {
this.log_('Database worker failed: ' + ex.message);
}
};
/**
* Sends a log message to the main thread to be logged.
* @param {string} msg The message to log.
* @private
*/
google.wspl.gears.DbWorker.prototype.log_ = function(msg) {
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.ReplyTypes.LOG,
'msg': msg
});
};
/**
* Sends a message to the main worker thread.
* @param {Object} msg The message object to send.
* @private
*/
google.wspl.gears.DbWorker.prototype.sendMessageToWorker_ = function(msg) {
this.wp_.sendMessage(msg, this.senderId_);
};

View file

@ -1,393 +0,0 @@
<!DOCTYPE html>
<!--
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Gears worker tests</title>
<script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
<script type="text/javascript" src="jsmock.js"></script>
<script type="text/javascript" src="global_functions.js"></script>
<script type="text/javascript" src="gearsutils.js"></script>
<script type="text/javascript" src="dbworker.js"></script>
</head>
<body>
<script type='text/javascript'>
var mockControl;
var db;
var wp;
var factory;
var callbackId = 10;
var transactionId = 15;
var name = 'name';
var userId = 'userId';
var utils;
function setUp() {
mockControl = new MockControl();
// Mock the Gears factory.
factory = mockControl.createMock();
factory.addMockMethod('create');
// Mock Google Gears.
google.gears = {};
google.gears.factory = {};
google.gears.factory.create = factory.create;
// Mock the Gears workerpool object.
wp = mockControl.createMock({
allowCrossOrigin: function(){},
sendMessage: function(){}
});
// Mock the Gears database object.
db = mockControl.createMock({
execute: function(){},
open: function(){},
close: function(){},
remove: function(){}
});
// Mock the Gears utility classes
utils = mockControl.createMock({
openDatabase: function(){},
});
google.wspl = google.wspl || {};
google.wspl.GearsUtils = google.wspl.GearsUtils || {};
google.wspl.GearsUtils.openDatabase = utils.openDatabase;
google.wspl.GearsUtils.resultSetToObjectArray = function(rs) {
return rs;
};
}
function buildWorker() {
wp.expects().sendMessage(TypeOf.isA(Object), 0).andStub(
function() {
var msg = arguments[0];
assertEquals('Wrong message type.',
google.wspl.gears.DbWorker.ReplyTypes.STARTED, msg.type);
});
var worker = new google.wspl.gears.DbWorker(wp);
worker.db_ = db;
worker.log_ = function() {};
return worker;
}
function testConstruction() {
var worker = buildWorker();
mockControl.verify();
}
function testHandleExecute_success() {
var worker = buildWorker();
var stat1 = {sql: 'sql1', params: [1, 2]};
var stat2 = {sql: 'sql2', params: [3, 4]};
var statements = [stat1, stat2];
var type = google.wspl.gears.DbWorker.ReplyTypes.RESULT;
db.expects().execute(stat1.sql, stat1.params).andReturn('result1');
db.expects().execute(stat2.sql, stat2.params).andReturn('result2');
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function() {
var msg = arguments[0];
assertEquals('Wrong message type.', type, msg.type);
assertEquals('Wrong results.length', 2, msg.results.length);
assertEquals('Wrong results[0].', 'result1', msg.results[0]);
assertEquals('Wrong results[1].', 'result2', msg.results[1]);
assertEquals('Wrong callbackId.', callbackId, msg.callbackId);
assertEquals('Wrong transactionId.', transactionId, msg.transactionId);
});
worker.handleExecute_(statements, callbackId, transactionId);
mockControl.verify();
}
function testHandleExecute_failure() {
var worker = buildWorker();
var stat1 = {sql: 'sql1', params: [1, 2]};
var stat2 = {sql: 'sql2', params: [3, 4]};
var stat3 = {sql: 'sql3', params: [5, 6]};
var statements = [stat1, stat2, stat3];
var type1 = google.wspl.gears.DbWorker.ReplyTypes.RESULT;
var type2 = google.wspl.gears.DbWorker.ReplyTypes.FAILURE;
var error = 'sql error';
db.expects().execute(stat1.sql, stat1.params).andReturn('result1');
db.expects().execute(stat2.sql, stat2.params).andThrow(error);
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function() {
var msg = arguments[0];
assertEquals('Wrong message type.', type2, msg.type);
assertEquals('Wrong result.', error, msg.error.message);
assertEquals('Wrong callbackId.', callbackId, msg.callbackId);
assertEquals('Wrong transactionId.', transactionId, msg.transactionId);
});
worker.handleExecute_(statements, callbackId, transactionId);
mockControl.verify();
}
function testHandleBegin() {
var worker = buildWorker();
// Expecting two transactions to begin.
db.expects().execute('BEGIN IMMEDIATE');
db.expects().execute('BEGIN IMMEDIATE');
worker.handleBegin_(transactionId);
worker.handleBegin_(22);
assertEquals('Did not save first transaction id', transactionId,
worker.transactions_[0]);
assertEquals('Did not save second transaction id', 22,
worker.transactions_[1]);
mockControl.verify();
}
function testHandleCommit() {
var worker = buildWorker();
db.expects().execute('COMMIT');
worker.handleCommit_(transactionId);
mockControl.verify();
}
function testHandleRollback() {
var worker = buildWorker();
db.expects().execute('ROLLBACK');
worker.handleRollback_(transactionId);
mockControl.verify();
}
function testHandleOpen_success() {
var worker = buildWorker();
worker.db_ = null;
factory.expects().create('beta.database', '1.0').andReturn(db);
utils.expects().openDatabase(userId, name, db, worker.log_).andReturn(db);
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function(msg) {
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.OPEN_SUCCESSFUL, msg.type);
});
worker.handleOpen_(userId, name);
assertEquals('Database wrongly set', db, worker.db_);
mockControl.verify();
}
function testHandleOpen_failure_gearsfactory() {
var worker = buildWorker();
worker.db_ = null;
factory.expects().create('beta.database', '1.0').andThrow('blah!');
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function(msg) {
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.OPEN_FAILED, msg.type);
});
worker.handleOpen_(userId, name);
mockControl.verify();
}
function testHandleOpen_failure_dbopen() {
var worker = buildWorker();
worker.db_ = null;
factory.expects().create('beta.database', '1.0').andReturn(null);
utils.expects().openDatabase(userId, name, null, worker.log_).andThrow('blah!');
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function(msg) {
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.OPEN_FAILED, msg.type);
});
worker.handleOpen_(userId, name);
mockControl.verify();
}
function testPostCommit() {
var worker = buildWorker();
worker.transactions_ = [4, 5];
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function() {
var msg = arguments[0];
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.COMMIT, msg.type);
assertEquals('Transaction id not set correctly.',
5, msg.transactionId);
});
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function() {
var msg = arguments[0];
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.COMMIT, msg.type);
assertEquals('Transaction id not set correctly.',
4, msg.transactionId);
});
worker.postCommit_();
assertEquals('Did not clear the transactions.', 0,
worker.transactions_.length);
mockControl.verify();
}
function testPostRollback() {
var worker = buildWorker();
worker.transactions_ = [4, 5];
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function() {
var msg = arguments[0];
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.ROLLBACK, msg.type);
assertEquals('Transaction id not set correctly.',
5, msg.transactionId);
});
wp.expects().sendMessage(TypeOf.isA(Object), worker.senderId_).andStub(
function() {
var msg = arguments[0];
assertEquals('Type not set correctly.',
google.wspl.gears.DbWorker.ReplyTypes.ROLLBACK, msg.type);
assertEquals('Transaction id not set correctly.',
4, msg.transactionId);
});
worker.postRollback_();
assertEquals('Did not clear the transactions.', 0,
worker.transactions_.length);
mockControl.verify();
}
function testOnmessage() {
var messageObject = {sender: 123, body: {}};
var worker = buildWorker();
worker.onMessage_(null, null, messageObject);
assertEquals('Wrong sender ID.', 123, worker.senderId_);
mockControl.verify();
}
function testOnmessage_open() {
var messageObject = {sender: 123, body: {
type: google.wspl.gears.DbWorker.CommandTypes.OPEN,
name: name,
userId: userId
}};
var worker = buildWorker();
var handler = mockControl.createMock();
handler.addMockMethod('open');
worker.handleOpen_ = handler.open;
handler.expects().open(userId, name);
worker.onMessage_(null, null, messageObject);
mockControl.verify();
}
function testOnmessage_execute() {
var worker = buildWorker();
var statements = ['stat1', 'stat2'];
var messageObject = {sender: 123, body: {
type: google.wspl.gears.DbWorker.CommandTypes.EXECUTE,
statements: statements,
callbackId: callbackId,
transactionId: transactionId
}};
var called = false;
worker.handleExecute_ = function(stat, call, trans) {
called = true;
assertEquals('Wrong statements.', statements, stat);
assertEquals('Wrong callback id.', callbackId, call);
assertEquals('Wrong transaction id.', transactionId, trans);
};
worker.onMessage_(null, null, messageObject);
assertTrue('handleExecute_ not called.', called);
mockControl.verify();
}
function testOnmessage_begin() {
var worker = buildWorker();
var messageObject = {sender: 123, body: {
type: google.wspl.gears.DbWorker.CommandTypes.BEGIN,
transactionId: transactionId
}};
var called = false;
worker.handleBegin_ = function(trans) {
called = true;
assertEquals('Wrong transaction id.', transactionId, trans);
};
worker.onMessage_(null, null, messageObject);
assertTrue('handleBegin_ not called.', called);
mockControl.verify();
}
function testOnmessage_commit() {
var worker = buildWorker();
var messageObject = {sender: 123, body: {
type: google.wspl.gears.DbWorker.CommandTypes.COMMIT,
transactionId: transactionId
}};
var called = false;
worker.handleCommit_ = function(trans) {
called = true;
assertEquals('Wrong transaction id.', transactionId, trans);
};
worker.onMessage_(null, null, messageObject);
assertTrue('handleCommit_ not called.', called);
mockControl.verify();
}
function testOnmessage_rollback() {
var worker = buildWorker();
var messageObject = {sender: 123, body: {
type: google.wspl.gears.DbWorker.CommandTypes.ROLLBACK,
transactionId: transactionId
}};
var called = false;
worker.handleRollback_ = function(trans) {
called = true;
assertEquals('Wrong transaction id.', transactionId, trans);
};
worker.onMessage_(null, null, messageObject);
assertTrue('handleRollback_ not called.', called);
mockControl.verify();
}
</script>
</body>
</html>

View file

@ -1,32 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview Starts the dbworker.
*
* When constructing the worker for execution, this needs to be the last
* file. The worker consists of the following source files combined together.
*
* globalfunctions.js
* gearsutils.js
* dbworker.js
* dbworkerstarter.js
*
* and then loaded into a Gears worker process as implemented in
* databasefactory.js
*/
google.wspl.gears.DbWorker.start();

View file

@ -1,595 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview A Gears implementation of dbwrapperapi Database.
*
* This implementation locks database access upon invoking the transaction's
* populate callback. Statements are then asynchronously sent to a worker
* thread for execution.
*/
/**
* @see google.wspl.Database#Database
* @param {boolean} opt_sync Perform all callbacks synchronously.
* @constructor
* @extends google.wspl.Database
*/
google.wspl.gears.Database = function(opt_sync) {
google.wspl.Database.call(this);
/**
* Begin transactions synchronously.
* @type {boolean}
* @private
*/
this.synchronous_ = !!opt_sync;
};
google.inherits(google.wspl.gears.Database, google.wspl.Database);
/**
* The time to wait for the dbworker to reply with STARTED.
* @type {number}
*/
google.wspl.gears.Database.TIMEOUT = 60000;
/**
* Whether the gears worker failed to reply with STARTED before TIMEOUT.
* @type {boolean}
* @private
*/
google.wspl.gears.Database.prototype.workerTimeout_ = false;
/**
* Flag set when the worker is ready with an open database connection.
* @type {boolean}
* @private
*/
google.wspl.gears.Database.prototype.workerReady_ = false;
/**
* Flag set when this database should use the worker to process transactions.
* @type {boolean}
* @private
*/
google.wspl.gears.Database.prototype.useWorker_ = false;
/**
* The user for this database.
* @type {string}
* @private
*/
google.wspl.gears.Database.prototype.userId_;
/**
* The name for this database.
* @type {string}
* @private
*/
google.wspl.gears.Database.prototype.name_;
/**
* A map of open transactions and their callbacks.
* @type {Object}
* @private
*/
google.wspl.gears.Database.prototype.transactions_ = {};
/**
* An array of transaction ids that should be executed in order as the lock
* becomes available.
* @type {Array.<number>}
* @private
*/
google.wspl.gears.Database.prototype.queuedTransactions_ = [];
/**
* The transaction lock for this database.
* @type {boolean}
* @private
*/
google.wspl.gears.Database.prototype.locked_ = false;
/**
* The number of transactions to be used as an index.
* @type {number}
* @private
*/
google.wspl.gears.Database.prototype.transCount_ = 1;
/**
* The id of the transaction being executed.
* @type {number}
* @private
*/
google.wspl.gears.Database.prototype.currentTransactionId_;
/**
* The Gears worker pool.
* @type {GearsWorkerPool}
* @private
*/
google.wspl.gears.Database.prototype.wp_;
/**
* The worker ID.
* @type {number}
* @private
*/
google.wspl.gears.Database.prototype.workerId_;
/**
* The Gears database object.
* @type {GearsDatabase}
* @private
*/
google.wspl.gears.Database.prototype.db_;
/**
* Opens a new Gears database. This operation can only be performed once.
* @param {string} userId The user for this database.
* @param {string} name The name for this database.
* @param {GearsDatabase} gearsDb The gears database.
*/
google.wspl.gears.Database.prototype.openDatabase = function(userId, name,
gearsDb) {
if (!this.db_) {
this.db_ = gearsDb;
this.userId_ = userId;
this.name_ = name;
google.wspl.GearsUtils.openDatabase(userId, name, this.db_,
google.logger);
} else {
google.logger('openDatabase already invoked.');
}
};
/**
* Starts a worker to handle the database interactions. The worker will be
* asynchronously started after the specified delay and will not be used until
* the completion of any pending transaction.
* @param {GearsWorkerPool} wp The Gears worker pool.
* @param {string} workerUrl The URL to find the gears database worker.
* @return {number} The worker ID.
*/
google.wspl.gears.Database.prototype.startWorker = function(wp, workerUrl) {
this.wp_ = wp;
google.logger('Starting dbworker thread.');
this.workerId_ = wp.createWorkerFromUrl(workerUrl);
this.timeoutId_ = window.setTimeout(google.bind(this.handleTimeout_, this),
google.wspl.gears.Database.TIMEOUT);
return this.workerId_;
};
/**
* @see google.wspl.Transaction#createTransaction
* @inheritDoc
*/
google.wspl.gears.Database.prototype.createTransaction = function(populate,
opt_callback) {
var transactionCallback = opt_callback || {
onSuccess : function() {},
onFailure : function() {}
};
var id = this.transCount_++;
var transaction = new google.wspl.gears.Transaction(id, this);
this.saveTransaction_(transaction, transactionCallback, populate);
this.queuedTransactions_.push(transaction.id_);
this.nextTransaction_();
};
/**
* Saves the transaction and transaction callback to be accessed later when a
* commit or rollback is performed.
*
* @param {google.wspl.gears.Transaction} transaction The transaction that the
* callback belongs to.
* @param {Object} callback A transaction callback with onSuccess and onFailure
* @private
*/
google.wspl.gears.Database.prototype.saveTransaction_ = function(
transaction, callback, populate) {
this.transactions_[transaction.id_] = {
transaction: transaction,
callback: callback,
populate: populate
};
};
/**
* Handles incomming messages.
* @param {string} a Deprecated.
* @param {number} b Deprecated.
* @param {Object} messageObject The message object.
* @private
*/
google.wspl.gears.Database.prototype.onMessage_ =
function(a, b, messageObject) {
var message = messageObject.body;
try {
switch(message['type']) {
case google.wspl.gears.DbWorker.ReplyTypes.RESULT:
this.handleResult_(message['results'], message['callbackId'],
message['transactionId']);
break;
case google.wspl.gears.DbWorker.ReplyTypes.FAILURE:
this.handleFailure_(message['error'], message['callbackId'],
message['transactionId']);
break;
case google.wspl.gears.DbWorker.ReplyTypes.COMMIT:
this.handleCommit_(message['transactionId']);
break;
case google.wspl.gears.DbWorker.ReplyTypes.ROLLBACK:
this.handleRollback_(message['transactionId']);
break;
case google.wspl.gears.DbWorker.ReplyTypes.STARTED:
this.handleStarted_();
break;
case google.wspl.gears.DbWorker.ReplyTypes.OPEN_SUCCESSFUL:
this.handleOpenSuccessful_();
break;
case google.wspl.gears.DbWorker.ReplyTypes.OPEN_FAILED:
this.handleOpenFailed_(message['error']);
break;
case google.wspl.gears.DbWorker.ReplyTypes.LOG:
google.logger(message['msg']);
break;
}
} catch (ex) {
google.logger('Gears database failed: ' + ex.message, ex);
}
};
/**
* Opens a new Gears database.
*
* @param {string} userId The user to which the database belongs.
* @param {string} name The name of the database.
*/
google.wspl.gears.Database.prototype.doOpen = function(userId, name) {
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.CommandTypes.OPEN,
'name': name,
'userId': userId
});
};
/**
* Begins a new transaction on the Gears database.
*
* @param {number} transactionId The id of the transaction being committed.
*/
google.wspl.gears.Database.prototype.doBegin = function(transactionId) {
if (!this.useWorker_) {
this.db_.execute('BEGIN IMMEDIATE');
return;
}
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.CommandTypes.BEGIN,
'transactionId': transactionId
});
};
/**
* Commits the current transaction on the Gears database. The transactionId
* is used to invoke the callback associated with the transaction.
*
* @param {number} transactionId The id of the transaction being committed.
*/
google.wspl.gears.Database.prototype.doCommit = function(transactionId) {
if (!this.useWorker_) {
this.db_.execute('COMMIT');
this.postCommit_();
return;
}
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.CommandTypes.COMMIT,
'transactionId': transactionId
});
};
/**
* Rolls the current transaction back on the Gears database. The transactionId
* is used to invoke the callback associated with the transaction.
*
* @param {number} transactionId The id of the transaction being rolled back.
*/
google.wspl.gears.Database.prototype.doRollback = function(transactionId) {
if (!this.useWorker_) {
this.db_.execute('ROLLBACK');
this.postRollback_();
return;
}
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.CommandTypes.ROLLBACK,
'transactionId': transactionId
});
};
/**
* Executes an array of statements on the Gears database. The transactionId and
* callbackId are used to identify the callback that should be invoked when
* handleResult or handleFailure is called.
*
* @param {Array.<google.wspl.Statement>} statements The group of statements to
* execute
* @param {number} callbackId The callback to invoke for each statement
* @param {number} transactionId The transaction that the statements belong to
*/
google.wspl.gears.Database.prototype.doExecute = function(statements,
callbackId,
transactionId) {
if (!this.useWorker_) {
this.doExecuteSynchronously_(statements, callbackId, transactionId);
return;
}
var newStatements = [];
for (var i = 0; i < statements.length; i++) {
newStatements[i] = {
'sql': statements[i].sql,
'params': statements[i].params
};
}
this.sendMessageToWorker_({
'type': google.wspl.gears.DbWorker.CommandTypes.EXECUTE,
'statements': newStatements,
'callbackId': callbackId,
'transactionId': transactionId
});
};
/**
* Executes an array of statements on the synchronous Gears databse.
* @param {Array.<google.wspl.Statement>} statements
* @param {number} callbackId
* @param {number} transactionId
* @private
*/
google.wspl.gears.Database.prototype.doExecuteSynchronously_ =
function(statements, callbackId, transactionId) {
var db = this;
var results = [];
for (var i = 0; i < statements.length; i++) {
try {
var resultset = this.db_.execute(statements[i].sql, statements[i].params);
var result = google.wspl.GearsUtils.resultSetToObjectArray(resultset);
results.push(result);
} catch (e) {
var error = e;
function failureCallback() {
db.handleFailure_(error, callbackId, transactionId);
};
this.setTimeout_(failureCallback, 0);
return;
}
}
function resultCallback() {
db.handleResult_(results, callbackId, transactionId);
};
this.setTimeout_(resultCallback, 0);
};
/**
* Handles a RESULT message from the worker thread.
*
* @param {!Array.<!Array.<Object>>} results A Gears result set.
* @param {number} callbackId The callback to invoke.
* @param {number} transactionId The transaction that the statement is executing
* in.
* @private
*/
google.wspl.gears.Database.prototype.handleResult_ = function(results,
callbackId, transactionId) {
var transInfo = this.transactions_[transactionId];
if (transInfo) {
for (var i = 0, l = results.length; i < l; i++) {
var resultSet = new google.wspl.gears.ResultSet(results[i]);
transInfo.transaction.success(resultSet, callbackId);
}
}
};
/**
* Handles a FAILURE message from the worker thread.
*
* @param {Error} error An error produced by the Gears database
* @param {number} callbackId The callback to invoke
* @param {number} transactionId The transaction that the statement is executing
* in
* @private
*/
google.wspl.gears.Database.prototype.handleFailure_ = function(error,
callbackId, transactionId) {
var transInfo = this.transactions_[transactionId];
if (transInfo) {
transInfo.error = error;
transInfo.transaction.failure(error, callbackId);
}
};
/**
* Handles a COMMIT message from the worker thread.
*
* @param {number} id The transaction id.
* @private
*/
google.wspl.gears.Database.prototype.handleCommit_ = function(id) {
var transaction = this.removeTransaction_(id);
if (transaction) {
transaction.callback.onSuccess();
}
this.nextTransaction_();
};
/**
* Handles the completion of a commit from the synchronous database.
* @private
*/
google.wspl.gears.Database.prototype.postCommit_ = function() {
this.handleCommit_(this.currentTransactionId_);
};
/**
* Handles a ROLLBACK message from the worker thread.
*
* @param {number} id The transaction id
* @private
*/
google.wspl.gears.Database.prototype.handleRollback_ = function(id) {
var transaction = this.removeTransaction_(id);
if (transaction) {
transaction.callback.onFailure(transaction.error);
}
this.nextTransaction_();
};
/**
* Handles the completion of a rollback from the synchronous database.
* @private
*/
google.wspl.gears.Database.prototype.postRollback_ = function() {
this.handleRollback_(this.currentTransactionId_);
};
/**
* Handles a STARTED message from the worker thread.
*
* @private
*/
google.wspl.gears.Database.prototype.handleStarted_ = function() {
if (!this.workerTimeout_) {
google.logger('Dbworker started.');
window.clearTimeout(this.timeoutId_);
this.timeoutId_ = 0;
this.doOpen(this.userId_, this.name_);
}
};
/**
* Handles a timeout of waiting for a STARTED message from the worker thread.
*
* @private
*/
google.wspl.gears.Database.prototype.handleTimeout_ = function() {
this.workerTimeout_ = true;
google.logger('Timed out while waiting for the dbworker to start.');
};
/**
* Handles a OPEN_SUCCESSFUL message from the worker thread.
*
* @private
*/
google.wspl.gears.Database.prototype.handleOpenSuccessful_ = function() {
this.workerReady_ = true;
};
/**
* Handles a OPEN_FAILED message from the worker thread.
* @param {string} error
* @private
*/
google.wspl.gears.Database.prototype.handleOpenFailed_ = function(error) {
google.logger('Worker failed to open Gears database.');
};
/**
* Executes the next transaction if there is one queued.
*
* @private
*/
google.wspl.gears.Database.prototype.nextTransaction_ = function() {
if (this.queuedTransactions_.length && !this.locked_) {
this.locked_ = true;
if (this.workerReady_ && !this.useWorker_) {
this.useWorker_ = true;
google.logger('Switching to asynchronous database interface.');
}
var id = this.queuedTransactions_.shift();
this.currentTransactionId_ = id;
var transactionData = this.transactions_[id];
var db = this;
function populate() {
transactionData.populate(transactionData.transaction);
// If populate did not execute statements on the database, invoke the
// success callback and process the next transaction.
if (!transactionData.transaction.isExecuting()) {
db.handleCommit_(id);
}
};
this.setTimeout_(populate, 0);
}
};
/**
* Cleans up the transaction and transaction callback for the id specified.
*
* @param {number} id The transaction id.
* @return {google.wspl.Transaction} The transaction and callback in an object.
* @private
*/
google.wspl.gears.Database.prototype.removeTransaction_ = function(id) {
this.locked_ = false;
var transaction = this.transactions_[id];
if (transaction) {
delete this.transactions_[id];
}
return transaction;
};
/**
* Execute a function using window's setTimeout.
* @param {Function} func The function to execute.
* @param {number} time The time delay before invocation.
* @private
*/
google.wspl.gears.Database.prototype.setTimeout_ = function(func, time) {
if (this.synchronous_) {
func();
} else {
window.setTimeout(func, time);
}
};
/**
* Sends a message to the database worker thread.
* @param {Object} msg The message object to send.
* @private
*/
google.wspl.gears.Database.prototype.sendMessageToWorker_ = function(msg) {
this.wp_.sendMessage(msg, this.workerId_);
};

View file

@ -1,404 +0,0 @@
<!DOCTYPE html>
<!--
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Gears database wrapper tests</title>
<script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
<script type="text/javascript" src="jsmock.js"></script>
<script type="text/javascript" src="global_functions.js"></script>
<script type="text/javascript" src="gearsutils.js"></script>
<script type="text/javascript" src="dbwrapperapi.js"></script>
<script type="text/javascript" src="gears_resultset.js"></script>
<script type="text/javascript" src="gears_transaction.js"></script>
<script type="text/javascript" src="dbworker.js"></script>
<script type="text/javascript" src="dbwrapper_gears.js"></script>
</head>
<body>
<script type='text/javascript'>
var mockControl;
var callbackMock;
var dbMock;
var wp;
var workerId = 123;
var transMock;
function setUp() {
mockControl = new MockControl();
callbackMock = mockControl.createMock({
onSuccess: function(){},
onFailure: function(){}
});
transMock = mockControl.createMock({
success: function(){},
failure: function(){}
});
wp = mockControl.createMock({
allowCrossOrigin: function(){},
createWorkerFromUrl: function(){},
sendMessage: function(){}
});
dbMock = mockControl.createMock({
execute: function(){},
open: function(){},
close: function(){},
remove: function(){}
});
google.wspl.GearsUtils.resultSetToObjectArray = function(rs) {
return rs;
};
}
function buildSyncDatabase() {
var database = new google.wspl.gears.Database(true);
database.userId_ = 'userId';
database.name_ = 'name';
database.db_ = dbMock;
return database;
}
function buildDatabase() {
var database = buildSyncDatabase();
var type = google.wspl.gears.DbWorker.CommandTypes.OPEN;
wp.expects().createWorkerFromUrl('workerUrl').andReturn(workerId);
wp.expects().sendMessage(TypeOf.isA(Object), workerId).andStub(function() {
assertEquals('Wrong message type.', type, arguments[0].type);
assertEquals('Incorrect name.', 'name', arguments[0].name);
assertEquals('Incorrect user id.', 'userId', arguments[0].userId);
});
database.startWorker(wp, 'workerUrl', 0);
database.handleStarted_();
database.handleOpenSuccessful_();
database.useWorker_ = true;
return database;
}
function testConstructor() {
var db = buildSyncDatabase();
mockControl.verify();
}
function testStartWorker() {
var db = buildDatabase();
assertTrue('Expected worker to be ready.', db.workerReady_);
mockControl.verify();
}
function testCreateTransaction() {
var db = buildDatabase();
var tx;
var populateMock = function(txa) {
tx = txa;
var transactions = db.transactions_;
assertEquals('missing transaction', tx, transactions[tx.id_].transaction);
assertEquals('missing callback', callbackMock,
transactions[tx.id_].callback);
assertEquals('database not saved', db, tx.db_);
assertTrue('database should be locked', db.locked_);
};
callbackMock.expects().onSuccess();
var transactions = db.transactions_;
db.createTransaction(populateMock, callbackMock);
assertEquals('failed to clean up transaction', undefined,
transactions[tx.id_]);
assertFalse('database should not be locked', db.locked_);
mockControl.verify();
}
function testMultipleTransactions() {
var db = buildDatabase();
var handler = mockControl.createMock();
handler.addMockMethod('populate');
handler.addMockMethod('onSuccess');
handler.addMockMethod('onFailure');
var trans;
handler.expects().populate(TypeOf.isA(google.wspl.gears.Transaction)).andStub(
function(tx) {
trans = tx;
tx.numActiveExecutes_ = 1;
});
db.createTransaction(handler.populate, handler);
db.createTransaction(handler.populate, handler);
mockControl.verify();
handler.expects().onSuccess();
handler.expects().populate(TypeOf.isA(google.wspl.gears.Transaction)).andStub(
function(tx) {
trans = tx;
tx.numActiveExecutes_ = 1;
});
db.handleCommit_(trans.id_);
mockControl.verify();
handler.expects().onFailure(undefined).andStub(function() {
db.createTransaction(handler.populate, handler);
});
handler.expects().populate(TypeOf.isA(google.wspl.gears.Transaction)).andStub(
function(tx) {
trans = tx;
tx.numActiveExecutes_ = 1;
});
db.handleRollback_(trans.id_);
mockControl.verify();
handler.expects().onSuccess();
db.handleCommit_(trans.id_);
mockControl.verify();
}
function testDoBegin() {
var db = buildDatabase();
var transactionId = 10;
var type = google.wspl.gears.DbWorker.CommandTypes.BEGIN;
wp.expects().sendMessage(TypeOf.isA(Object), workerId).andStub(function() {
assertEquals('wrong message type', type, arguments[0].type);
assertNotUndefined('Missing transaction id.', arguments[0].transactionId);
});
db.doBegin(transactionId);
mockControl.verify();
}
function testSynchronousBegin() {
var db = buildSyncDatabase();
dbMock.expects().execute('BEGIN IMMEDIATE');
db.doBegin(1);
mockControl.verify();
}
function testDoExecute() {
var statements = [{sql: 's1', params: 'p1'},
{sql: 's2', params: 'p2'},
{sql: 's3', params: 'p3'}];
var callbackId = 5;
var transactionId = 10;
var db = buildDatabase();
var type = google.wspl.gears.DbWorker.CommandTypes.EXECUTE;
wp.expects().sendMessage(TypeOf.isA(Object), workerId).andStub(function() {
assertEquals('wrong message type', type, arguments[0].type);
assertEquals('statements do not match', statements.length,
arguments[0].statements.length);
for (var i = 0; i < statements.length; i++) {
assertEquals('a statement sql does not match', statements[i].sql,
arguments[0].statements[i].sql);
assertEquals('a statement params does not match', statements[i].params,
arguments[0].statements[i].params);
}
assertEquals('missing callback id', callbackId, arguments[0].callbackId);
assertEquals('missing trans id', transactionId, arguments[0].transactionId);
});
db.doExecute(statements, callbackId, transactionId);
mockControl.verify();
}
function testSynchronousExecute() {
var statements = [{sql: 's1', params: 'p1'},
{sql: 's2', params: 'p2'},
{sql: 's3', params: 'p3'}];
var callbackId = 5;
var transactionId = 10;
var db = buildSyncDatabase();
for (var i = 0; i < 3; i++) {
var stat = statements[i];
dbMock.expects().execute(stat.sql, stat.params).andReturn('result' + i);
}
var handler = mockControl.createMock();
handler.addMockMethod('handleResult_');
db.handleResult_ = handler.handleResult_;
handler.expects().handleResult_(
TypeOf.isA(Array), callbackId, transactionId).andStub(function(results) {
var expected = ['result0', 'result1', 'result2'];
assertArrayEquals('Wrong results.', expected, results);
});
db.doExecute(statements, callbackId, transactionId);
mockControl.verify();
}
function testSynchronousExecute_failure() {
var statements = [{sql: 's1', params: 'p1'},
{sql: 's2', params: 'p2'},
{sql: 's3', params: 'p3'}];
var callbackId = 5;
var transactionId = 10;
var db = buildSyncDatabase();
dbMock.expects().execute('s1', 'p1').andReturn('result0');
dbMock.expects().execute('s2', 'p2').andThrow(Error('db error'));
var handler = mockControl.createMock();
handler.addMockMethod('handleFailure_');
db.handleFailure_ = handler.handleFailure_;
handler.expects().handleFailure_(
TypeOf.isA(Error), callbackId, transactionId);
db.doExecute(statements, callbackId, transactionId);
mockControl.verify();
}
function testDoCommit() {
var transactionId = 10;
var db = buildDatabase();
var type = google.wspl.gears.DbWorker.CommandTypes.COMMIT;
wp.expects().sendMessage(TypeOf.isA(Object), workerId).andStub(function() {
assertEquals('wrong message type', type, arguments[0].type);
assertEquals('missing trans id', transactionId, arguments[0].transactionId);
});
db.doCommit(transactionId);
mockControl.verify();
}
function testSynchronousCommit() {
var db = buildSyncDatabase();
dbMock.expects().execute('COMMIT');
db.doCommit(1);
mockControl.verify();
}
function testDoRollback() {
var transactionId = 10;
var db = buildDatabase();
var type = google.wspl.gears.DbWorker.CommandTypes.ROLLBACK;
wp.expects().sendMessage(TypeOf.isA(Object), workerId).andStub(function() {
assertEquals('wrong message type', type, arguments[0].type);
assertEquals('missing trans id', transactionId, arguments[0].transactionId);
});
db.doRollback(transactionId);
mockControl.verify();
}
function testSynchronousRollback() {
var db = buildSyncDatabase();
dbMock.expects().execute('ROLLBACK');
db.doRollback(1);
mockControl.verify();
}
function testHandleCommit() {
var db = buildDatabase();
db.transactions_[5] = {transaction: 'tx', callback: callbackMock};
callbackMock.expects().onSuccess();
db.handleCommit_(5);
assertUndefined('failed to remove transaction', db.transactions_[5]);
mockControl.verify();
}
function testHandleRollback() {
var db = buildDatabase();
db.transactions_[5] = {
transaction: 'tx',
callback: callbackMock,
error: Error('error')
};
callbackMock.expects().onFailure(TypeOf.isA(Error)).andStub(function() {
assertEquals('did not pass error', 'error', arguments[0].message);
});
db.handleRollback_(5);
assertUndefined('failed to remove transaction', db.transactions_[5]);
mockControl.verify();
}
function testHandleResult() {
var db = buildDatabase();
transMock.expects().success(TypeOf.isA(google.wspl.gears.ResultSet),
22).andStub(function() {
assertEquals('result not set', 'result1', arguments[0].resultArray_);
});
transMock.expects().success(TypeOf.isA(google.wspl.gears.ResultSet),
22).andStub(function() {
assertEquals('result not set', 'result2', arguments[0].resultArray_);
});
db.transactions_[5] = {transaction: transMock, callback: 'cb'};
db.handleResult_(['result1', 'result2'], 22, 5);
mockControl.verify();
}
function testHandleFailure() {
var db = buildDatabase();
transMock.expects().failure(TypeOf.isA(Error), 22).andStub(function() {
assertEquals('error not set', 'error', arguments[0].message);
});
db.transactions_[5] = {transaction: transMock, callback: 'cb'};
db.handleFailure_(Error('error'), 22, 5);
assertEquals('failed to save error', 'error',
db.transactions_[5].error.message);
mockControl.verify();
}
function testHandleStarted() {
var db = buildDatabase();
var type = google.wspl.gears.DbWorker.CommandTypes.OPEN;
wp.expects().sendMessage(TypeOf.isA(Object), workerId).andStub(function() {
assertEquals('wrong message type', type, arguments[0].type);
assertEquals('wrong db userId', 'userId', arguments[0].userId);
assertEquals('wrong db name', 'name', arguments[0].name);
});
db.handleStarted_();
mockControl.verify();
}
function testHandleStarted_afterTimeout() {
var db = buildDatabase();
db.handleTimeout_();
// This should do nothing.
db.handleStarted_();
mockControl.verify();
}
function testHandleTimeout() {
var db = buildDatabase();
db.handleTimeout_();
mockControl.verify();
}
function testHandleOpenSuccessful() {
var db = buildDatabase();
db.handleOpenSuccessful_();
assertTrue('Worker should be ready.', db.workerReady_);
mockControl.verify();
}
function testHandleOpenFailed() {
var db = buildDatabase();
db.handleOpenFailed_('error');
mockControl.verify();
}
</script>
</body>
</html>

View file

@ -1,203 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview Generic Database API.
*
* A small set of classes to define how we interact with databases that
* can easily be implemented on top of HTML5.
*/
google.wspl.html5 = google.wspl.html5 || {};
/**
* Specification's default largest database size in HTML5 databases.
* @type{number}
*/
google.wspl.LARGEST_SUPPORTED_DATABASE = 1024 * 1024 * 4;
/**
* Creates an HTML5 Transaction object.
* @see google.wspl.Transaction#Transaction
*
* @constructor
* @extends google.wspl.Transaction
*
* @param {SQLTransaction} html5tx The HTML5 implementation of transactions.
*/
google.wspl.html5.Transaction = function(html5tx) {
this.tx_ = html5tx;
};
google.inherits(google.wspl.html5.Transaction, google.wspl.Transaction);
/**
* Runs an array of statements in a single database transaction.
* Invokes the onSuccess callback once for each succeesfully executed
* statement and
* once for the first failed statement.
*
* @param {Array.<google.wspl.Statement>} statements The statements to
* execute.
* @param {Object?} opt_callback An object containing onSuccess and onFailure
* handlers.
*/
google.wspl.html5.Transaction.prototype.executeAll = function(statements,
opt_callback) {
if (statements.length == 0) {
throw Error('Possibly silly attempt to execute empty statement list.');
}
var self = this;
for (var i = 0; i < statements.length; ++i) {
var statement = statements[i];
google.logger('SQL: ' + statement.sql + ' PARAMS: ' + statement.params);
this.tx_.executeSql(statement.sql, statement.params,
function(tx, result) {
if (opt_callback && opt_callback.onSuccess) {
var resultSet = new google.wspl.html5.ResultSet(result);
opt_callback.onSuccess(self, resultSet);
}
},
function(tx, error) {
if (opt_callback && opt_callback.onFailure) {
opt_callback.onFailure(error);
}
// fail the whole transaction if any step fails
return true;
});
}
};
/**
* @see google.wspl.Database#Database
* @param {string} name The name for this database.
* @param {window} opt_window A window object for dependency injection.
* @constructor
* @extends google.wspl.Database
*/
google.wspl.html5.Database = function(name, opt_window) {
/**
* Sequence number for transactions.
* @type {number}
* @private
*/
this.sequenceNum_ = 1;
/**
* Map of transactionIds -> transaction start time in millis.
* @type {Object}
* @private
*/
this.inflightTransactions_ = {};
var win = opt_window || window;
this.db_ = win.openDatabase(name, '',
name, google.wspl.LARGEST_SUPPORTED_DATABASE);
if (this.db_ == null) {
throw Error('The returned database was null.');
}
};
google.inherits(google.wspl.html5.Database, google.wspl.Database);
/**
* @see google.wspl.Database#createTransaction
*/
google.wspl.html5.Database.prototype.createTransaction = function(populate,
opt_callback) {
var transactionCallback = opt_callback || {
onSuccess: function() {},
onFailure: function() {}
};
var transactionId = this.sequenceNum_++;
var inflightTransactions = this.inflightTransactions_;
inflightTransactions[transactionId] = this.getCurrentTime();
this.db_.transaction(
function(tx) {
// Delete the transaction before the executing it because our
// definition of an 'in-flight' transaction is the time between
// when the request was made and when the database starts to
// execute the transaction.
delete inflightTransactions[transactionId];
populate(new google.wspl.html5.Transaction(tx));
},
function(error) {transactionCallback.onFailure(error);},
function() {transactionCallback.onSuccess();});
};
/**
* Determine if there is an in-flight database transaction that's older than
* the given time period.
* @param {number} olderThanMillis The time period.
* @return {boolean} True if the database has an in-flight transaction older
* than the given time period, false otherwise.
*/
google.wspl.html5.Database.prototype.hasInflightTransactions =
function(olderThanMillis) {
for (var transactionId in this.inflightTransactions_) {
var startTime = this.inflightTransactions_[transactionId];
if (this.getCurrentTime() - startTime > olderThanMillis) {
return true;
}
}
return false;
};
/**
* Returns the current time.
* @return {number} The current time in millis.
*/
google.wspl.html5.Database.prototype.getCurrentTime = function() {
// The iPhone does not support Date.now()
var d = new Date();
return d.getTime();
};
/**
* Creates an HTML5 ResultSet object.
* @see google.wspl.ResultSet#ResultSet
*
* @constructor
* @extends google.wspl.ResultSet
*
* @param {Object} html5_result The HTML5 implementation of result set.
*/
google.wspl.html5.ResultSet = function(html5_result) {
this.result_ = html5_result;
this.index_ = 0;
};
google.inherits(google.wspl.html5.ResultSet, google.wspl.ResultSet);
/**
* @see google.wspl.ResultSet#isValidRow
*/
google.wspl.html5.ResultSet.prototype.isValidRow = function() {
return this.index_ >= 0 && this.index_ < this.result_.rows.length;
};
/**
* @see google.wspl.ResultSet#next
*/
google.wspl.html5.ResultSet.prototype.next = function() {
this.index_ ++;
};
/**
* @see google.wspl.ResultSet#getRow
*/
google.wspl.html5.ResultSet.prototype.getRow = function() {
return this.result_.rows.item(this.index_);
};

View file

@ -1,468 +0,0 @@
<!DOCTYPE html>
<!--
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Database wrapper tests</title>
<script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
<script type="text/javascript" src="jsmock.js"></script>
<script type="text/javascript" src="global_functions.js"></script>
<script type="text/javascript" src="dbwrapperapi.js"></script>
<script type="text/javascript" src="dbwrapper_html5.js"></script>
</head>
<body>
<script type='text/javascript'>
var mockControl;
var html5Tx;
var callbackMock;
var html5Window;
var html5Db;
var populateMock;
function setUp() {
mockControl = new MockControl();
html5Tx = mockControl.createMock({executeSql: function(){}});
callbackMock = mockControl.createMock({onSuccess: function(){},
onFailure: function(){}});
html5Window = mockControl.createMock({openDatabase: function(){}});
html5Db = mockControl.createMock({transaction: function(){}});
populateMock = mockControl.createMock({populate: function(){}});
dbTx = mockControl.createMock({execute: function(){}, executeAll:
function(){}});
dbRows = mockControl.createMock({item: function(){}});
}
function testConstructTransaction() {
var trans = new google.wspl.html5.Transaction('foo');
assertEquals('transaction instance not set correctly', 'foo', trans.tx_);
}
function testTransactionExecuteAll() {
var trans = new google.wspl.html5.Transaction(html5Tx);
try {
trans.executeAll([], callbackMock);
fail('Should never get here');
} catch(e) {
assertEquals(
'did not exception fault on empty statement list',
'Error: Possibly silly attempt to execute empty statement list.',
e.toString());
}
mockControl.verify();
}
/*
The sequence of digits at the end of the test names indicate as boolean
variables the success or failure of each statement.
*/
function testTransactionExecuteAll_1() {
var stat1 = new google.wspl.Statement('stat1');
var trans = new google.wspl.html5.Transaction(html5Tx);
var successCallback = null;
var failureCallback = null;
html5Tx.expects().executeSql('stat1', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback = arguments[2];
});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset', arguments[1].result_)});
trans.executeAll([stat1], callbackMock);
successCallback(html5Tx, 'resultset');
mockControl.verify();
}
function testTransactionExecuteAll_11() {
var stat1 = new google.wspl.Statement('stat1');
var stat2 = new google.wspl.Statement('stat2', [1]);
var trans = new google.wspl.html5.Transaction(html5Tx);
var successCallback1 = null;
var successCallback2 = null;
var failureCallback = null;
html5Tx.expects().executeSql('stat1', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback1 = arguments[2];
});
html5Tx.expects().executeSql('stat2', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback2 = arguments[2];
var params = arguments[1];
assertEquals('incorrect params to sql', 1, params[0]);
});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset1', arguments[1].result_)});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset2', arguments[1].result_)});
trans.executeAll([stat1, stat2], callbackMock);
successCallback1(html5Tx, 'resultset1');
successCallback2(html5Tx, 'resultset2');
mockControl.verify();
}
function testTransactionExecuteAll_111() {
var stat1 = new google.wspl.Statement('stat1');
var stat2 = new google.wspl.Statement('stat2', [1]);
var stat3 = new google.wspl.Statement('stat3', [2, 3]);
var trans = new google.wspl.html5.Transaction(html5Tx);
var successCallback1 = null;
var successCallback2 = null;
var successCallback3 = null;
var failureCallback = null;
html5Tx.expects().executeSql('stat1', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback1 = arguments[2];
});
html5Tx.expects().executeSql('stat2', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback2 = arguments[2];
var params = arguments[1];
assertEquals('incorrect params to sql', 1, params[0]);
});
html5Tx.expects().executeSql('stat3', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback3 = arguments[2];
var params = arguments[1];
assertEquals('incorrect params to sql', 2, params[0]);
assertEquals('incorrect params to sql', 3, params[1]);
});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset1', arguments[1].result_)});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset2', arguments[1].result_)});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset3', arguments[1].result_)});
trans.executeAll([stat1, stat2, stat3], callbackMock);
successCallback1(html5Tx, 'resultset1');
successCallback2(html5Tx, 'resultset2');
successCallback3(html5Tx, 'resultset3');
mockControl.verify();
}
function testTransactionExecuteAll_noCallback_111() {
var stat1 = new google.wspl.Statement('stat1');
var stat2 = new google.wspl.Statement('stat2', [1]);
var stat3 = new google.wspl.Statement('stat3', [2, 3]);
var trans = new google.wspl.html5.Transaction(html5Tx);
var successCallback1 = null;
var successCallback2 = null;
var successCallback3 = null;
var failureCallback = null;
html5Tx.expects().executeSql('stat1', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback1 = arguments[2];
});
html5Tx.expects().executeSql('stat2', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(
function() { successCallback2 = arguments[2];
var params = arguments[1];
assertEquals('incorrect params to sql', 1, params[0]);
});
html5Tx.expects().executeSql('stat3', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(
function() { successCallback3 = arguments[2];
var params = arguments[1];
assertEquals('incorrect params to sql', 2, params[0]);
assertEquals('incorrect params to sql', 3, params[1]);
});
trans.executeAll([stat1, stat2, stat3]);
successCallback1(html5Tx, 'resultset1');
successCallback2(html5Tx, 'resultset2');
successCallback3(html5Tx, 'resultset3');
mockControl.verify();
}
function testTransactionExecuteAll_110() {
var stat1 = new google.wspl.Statement('stat1');
var stat2 = new google.wspl.Statement('stat2', [1]);
var stat3 = new google.wspl.Statement('stat3', [2, 3]);
var trans = new google.wspl.html5.Transaction(html5Tx);
var successCallback1 = null;
var successCallback2 = null;
var successCallback3 = null;
var failureCallback3 = null;
html5Tx.expects().executeSql('stat1', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback1 = arguments[2];
});
html5Tx.expects().executeSql('stat2', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback2 = arguments[2];
var params = arguments[1];
assertEquals('incorrect params to sql', 1, params[0]);
});
html5Tx.expects().executeSql('stat3', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback3 = arguments[2];
failureCallback3 = arguments[3];
var params = arguments[1];
assertEquals('incorrect params to sql', 2, params[0]);
assertEquals('incorrect params to sql', 3, params[1]);
});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset1', arguments[1].result_)});
callbackMock.expects().onSuccess(trans,
TypeOf.isA(google.wspl.html5.ResultSet)).andStub(function() {
assertEquals('result not returned', 'resultset2', arguments[1].result_)});
callbackMock.expects().onFailure('error3');
trans.executeAll([stat1, stat2, stat3], callbackMock);
successCallback1(html5Tx, 'resultset1');
successCallback2(html5Tx, 'resultset2');
assertTrue('failure case callback not terminating transaction',
failureCallback3(html5Tx, 'error3'));
mockControl.verify();
}
function testTransactionExecute_nocallback() {
var stat1 = new google.wspl.Statement('stat1');
var trans = new google.wspl.html5.Transaction(html5Tx);
var successCallback = null;
var failureCallback = null;
html5Tx.expects().executeSql('stat1', TypeOf.isA(Array),
TypeOf.isA(Function), TypeOf.isA(Function)).andStub(function() {
successCallback = arguments[2];
});
trans.execute(stat1);
successCallback(html5Tx, 'resultset');
mockControl.verify();
}
function buildDatabase() {
html5Window.expects().openDatabase('name', '', 'name',
google.wspl.LARGEST_SUPPORTED_DATABASE).
andReturn(html5Db);
return new google.wspl.html5.Database('name', html5Window);
}
function testConstructDatabase() {
var db = buildDatabase();
mockControl.verify();
assertEquals('did not set db_ correctly', db.db_, html5Db);
}
function testConstructDatabase_null() {
html5Window.expects().openDatabase('name', '', 'name',
google.wspl.LARGEST_SUPPORTED_DATABASE).
andReturn(null);
try {
var db = google.wspl.html5.Database('name', html5Window);
fail('Should never get here');
} catch (e) {
if (e.isJsUnitException) {
throw e;
}
}
mockControl.verify();
}
function testConstructDatabase_undefined() {
html5Window.expects().openDatabase('name', '', 'name',
google.wspl.LARGEST_SUPPORTED_DATABASE).
andReturn(undefined);
try {
var db = google.wspl.html5.Database('name', html5Window);
fail('Should never get here');
} catch (e) {
if (e.isJsUnitException) {
throw e;
}
}
mockControl.verify();
}
function createTransactionCore() {
var db = buildDatabase();
var fun1 = null;
var failure = null;
var success = null;
html5Db.expects().transaction(TypeOf.isA(Function), TypeOf.isA(Function),
TypeOf.isA(Function)).andStub(function() {
fun1 = arguments[0];
failure = arguments[1];
success = arguments[2];
});
var tx = null;
var populateMock = function(txa) {
tx = txa;
};
db.createTransaction(populateMock, callbackMock);
fun1('transactionTest');
assertEquals('transaction not saved', 'transactionTest', tx.tx_);
assertEquals('did not set db_ correctly', db.db_, html5Db);
return {failure: failure, success: success};
}
function testCreateTransaction_success() {
cbs = createTransactionCore();
callbackMock.expects().onSuccess();
cbs.success('success');
mockControl.verify();
}
function testCreateTransaction_failure() {
cbs = createTransactionCore();
callbackMock.expects().onFailure('error');
cbs.failure('error');
mockControl.verify();
}
function testCreateTransaction_onlyPopulate() {
cbs = createTransactionCore();
mockControl.verify();
}
function testDatabaseExecute_nocallback() {
var db = buildDatabase();
dbTx.expects().execute('statementText', null);
db.execute('statementText');
}
function testDatabaseExecute_nocallback() {
var db = buildDatabase();
dbTx.expects().execute('statementText', null);
db.execute('statementText');
}
function testDatabaseExecuteAll() {
var db = buildDatabase();
dbTx.expects().executeAll(TypeOf.isA(Array));
}
function testResultSetNext() {
var res = {rows: {length: 4}};
var result = new google.wspl.html5.ResultSet(res);
for (var i = 0; i < 4; i++) {
assertTrue('expected valid row', result.isValidRow());
result.next();
}
assertFalse('expected invalid row', result.isValidRow());
}
function testResultSetIsValidRow() {
var res = {rows: {length: 0}};
var result = new google.wspl.html5.ResultSet(res);
assertFalse('expected invalid row', result.isValidRow());
res.rows.length = 1;
assertTrue('expected valid row', result.isValidRow());
result.next();
assertFalse('expected invalid row', result.isValidRow());
}
function testResultSetGetRow() {
var res = {rows: {
length: 3,
item: function(index) {
assertEquals('expected index of 1', 1, index);
return {h0: 'value0', h1: 'value1', h2: 'value2'};
}
}};
var result = new google.wspl.html5.ResultSet(res);
result.next();
var row = result.getRow();
assertEquals('first field is not valid', 'value0', row.h0);
assertEquals('first field is not valid', 'value1', row.h1);
assertEquals('first field is not valid', 'value2', row.h2);
}
function testHasInflightTransactions() {
var fakeTime = 1000;
var db = buildDatabase();
var dummyPopulate = function() {};
db.getCurrentTime = function() {return fakeTime;};
// Create a transaction, make sure it's kept track of.
var transId = db.sequenceNum_;
db.createTransaction(dummyPopulate);
assertEquals('There should be an inflight request.',
fakeTime, db.inflightTransactions_[transId]);
// Haven't advanced time, so hasInflightTransactions should return false.
assertFalse('Expected hasInflightTransactions=false.',
db.hasInflightTransactions(100));
// Advance time, now hasInflightTransactions should return true.
fakeTime += 10000;
var faqs = db.hasInflightTransactions(100);
assertTrue('Expected hasInflightTransactions=true.',
db.hasInflightTransactions(100));
// Now now have the transaction completed, hasInflightTransactions
// should return false.
delete db.inflightTransactions_[transId];
assertFalse('Expected hasInflightTransactions=false.',
db.hasInflightTransactions(100));
};
</script>
</body>
</html>

View file

@ -1,202 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview Generic Database API.
*
* A small set of classes to define how we interact with databases that can
* easily be implemented on top of HTML5 and Gears. The classes in this file
* should be extended to provide the missing method implementations and more
* sophisticated constructors where applicable.
*
*/
/**
* Constructs a Statement object. A Statement is an SQL statement paired
* with the parameters needed to execute it.
*
* @constructor
* @param {!string} sql The SQL statement.
* @param {Array.<Object>?} opt_params The parameters for the SQL statement.
*/
google.wspl.Statement = function(sql, opt_params) {
/**
* The SQL statement with '?' in place of parameters.
* @type {string}
*/
this.sql = sql;
/**
* The parameters to use with the SQL statement.
* @type {!Array}
*/
this.params = opt_params || [];
};
/**
* Returns a new statement object from the given statement with the parameters
* set as specified.
* @param {Array.<Object>} params The array of values for ? placeholders.
* @return {!google.wspl.Statement} The created Statement.
*/
google.wspl.Statement.prototype.createStatement = function(params) {
return new google.wspl.Statement(this.sql, params);
};
/**
* Constructs a Transaction object. Transaction objects
* group together a series of statements into a single atomic
* action on the database.
*
* @constructor
*/
google.wspl.Transaction = function() {
};
/**
* Takes a statement and an optional callback object and
* runs the statement on the database. The callback can be used to
* add more statements to the same transaction, or execute can be
* called repeatedly and the transactions will later execute in the
* order provided.
*
* @param {google.wspl.Statement} statement The statement to execute.
* @param {Object} opt_callback An object containing onSuccess and onFailure
* handlers.
*/
google.wspl.Transaction.prototype.execute = function(statement,
opt_callback) {
this.executeAll([statement], opt_callback);
};
/**
* Runs an array of statements in a single database transaction.
* Invokes the onSuccess callback once for each successfully executed
* statement and once for the first failed statement. The callback can be
* used to add more statements to the same transaction, or executeAll can
* be called repeatedly and each block of statements given will execute
* in the same order as the sequence of calls to executeAll.
*
* @param {Array.<google.wspl.Statement>} statements The statements to
* execute.
* @param {Object?} opt_callback An object containing onSuccess and onFailure
* handlers.
*/
google.wspl.Transaction.prototype.executeAll = function(statements,
opt_callback) {
throw Error('executeAll not implemented');
};
/**
* Constructs a Database object. Database objects are handles that allow
* access to a database (and create the corresponding database if it doesn't
* already exist). To open the database, pass the name of the needed
* database to the constructor, and then execute transactions on it using
* the execute method.
*
* @constructor
*/
google.wspl.Database = function() {
};
/**
* Creates a transaction object that can execute a series of SQL statements
* atomically.
*
* @param {Function} populate A callback to run execute calls on the
* transaction.
* @param {Object?} opt_callback An optional success/failure callback that is
* called when the entire transaction is finished executing.
*/
google.wspl.Database.prototype.createTransaction = function(populate,
opt_callback) {
throw Error('createTransaction not implemented');
};
/**
* Executes an array of statements on the database, invoking the optional
* callback after statement execution and the optional transactionCallback upon
* completion of the transaction.
*
* @param {google.wspl.Statement} statement the statement to execute
* @param {Object?} opt_callback object that defines onSuccess and onFailure
* @param {Object?} opt_transactionCallback object that defines onSuccess and
* onFailure
*/
google.wspl.Database.prototype.execute = function(statement,
opt_callback,
opt_transactionCallback) {
this.createTransaction(function(tx) {
tx.execute(statement, opt_callback);
}, opt_transactionCallback);
};
/**
* Executes an array of statements on the database, invoking the optional
* callback for each statement in the transaction. In the case of a statement
* failure, only the first failed statement will be reported and the transaction
* will be rolled back. This method invokes the optional transactionCallback
* upon completion of the transaction.
*
* @param {Array.<google.wspl.Statement>} statements the statements to execute
* @param {Object?} opt_callback object that defines onSuccess and onFailure
* @param {Object?} opt_transactionCallback object that defines onSuccess and
* onFailure
*/
google.wspl.Database.prototype.executeAll = function(statements,
opt_callback,
opt_transactionCallback) {
this.createTransaction(function(tx) {
tx.executeAll(statements, opt_callback);
}, opt_transactionCallback);
};
/**
* An immutable set of results that is returned from a single successful query
* on the database.
*
* @constructor
*/
google.wspl.ResultSet = function() {
};
/**
* Returns true if next() will advance to a valid row in the result set.
*
* @return {boolean} if next() will advance to a valid row in the result set
*/
google.wspl.ResultSet.prototype.isValidRow = function() {
throw Error('isValidRow not implemented');
};
/**
* Advances to the next row in the results.
*/
google.wspl.ResultSet.prototype.next = function() {
throw Error('next not implemented');
};
/**
* Returns the current row as an object with a property for each field returned
* by the database. The property will have the name of the column and the value
* of the cell.
*
* @return {Object} The current row
*/
google.wspl.ResultSet.prototype.getRow = function() {
throw Error('getRow not implemented');
};

View file

@ -1,51 +0,0 @@
<!DOCTYPE html>
<!--
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Database wrapper api</title>
<script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
<script type="text/javascript" src="jsmock.js"></script>
<script type="text/javascript" src="global_functions.js"></script>
<script type="text/javascript" src="dbwrapperapi.js"></script>
</head>
<body>
<script type='text/javascript'>
function subStatementTest(stm) {
assertEquals('statement not set correctly', 'bar', stm.sql);
assertEquals('first param not set correctly', 1, stm.params[0]);
assertEquals('second param not set correctly', 2, stm.params[1]);
}
function testConstructStatement() {
var stm = new google.wspl.Statement('foo');
assertEquals('statement not set correctly', 'foo', stm.sql);
var stm = new google.wspl.Statement('bar', [1,2]);
subStatementTest(stm);
}
function testConstructStatementFromTemplate() {
var temp = new google.wspl.Statement('bar');
var stm = temp.createStatement([1,2]);
subStatementTest(stm);
}
</script>
</body>
</html>

View file

@ -1,71 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview A Gears implementation of dbwrapperapi ResultSet.
*/
/**
* Creates a Gears ResultSet object.
* @see google.wspl.ResultSet#ResultSet
*
* @constructor
* @extends google.wspl.ResultSet
* @param {!Array.<Object>} resultArray An array of hash objects where the
* column names in the query are used as members of the objects.
*/
google.wspl.gears.ResultSet = function(resultArray) {
google.wspl.ResultSet.call(this);
/**
* The result set as an array of hash objects.
* @type {!Array.<Object>}
* @private
*/
this.resultArray_ = resultArray;
};
google.inherits(google.wspl.gears.ResultSet, google.wspl.ResultSet);
/**
* The current record in the result set.
* @type {number}
* @private
*/
google.wspl.gears.ResultSet.prototype.current_ = 0;
/**
* @see google.wspl.ResultSet#isValidRow
* @inheritDoc
*/
google.wspl.gears.ResultSet.prototype.isValidRow = function() {
return this.current_ < this.resultArray_.length;
};
/**
* @see google.wspl.ResultSet#next
* @inheritDoc
*/
google.wspl.gears.ResultSet.prototype.next = function() {
this.current_++;
};
/**
* @see google.wspl.ResultSet#getRow
* @inheritDoc
*/
google.wspl.gears.ResultSet.prototype.getRow = function() {
return this.resultArray_[this.current_];
};

View file

@ -1,86 +0,0 @@
<!DOCTYPE html>
<!--
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Gears resultset tests</title>
<script type="text/javascript" src="../jsunit/app/jsUnitCore.js"></script>
<script type="text/javascript" src="jsmock.js"></script>
<script type="text/javascript" src="global_functions.js"></script>
<script type="text/javascript" src="dbwrapperapi.js"></script>
<script type="text/javascript" src="gears_resultset.js"></script>
</head>
<body>
<script type='text/javascript'>
var resultArray;
function setUp() {
var obj1 = {
field1: 'a',
field2: 2,
field3: 'c'
};
var obj2 = {
field1: 'd',
field2: 4,
field3: 'f'
};
var obj3 = {
field1: 'g',
field2: 6,
field3: 'i'
};
resultArray = [obj1, obj2, obj3];
}
function testResultSetNext() {
var result = new google.wspl.gears.ResultSet(resultArray);
assertEquals('incorrect value for current', 0, result.current_);
result.next();
assertEquals('incorrect value for current', 1, result.current_);
result.next();
assertEquals('incorrect value for current', 2, result.current_);
}
function testResultSetIsValidRow() {
var result = new google.wspl.gears.ResultSet(resultArray);
assertTrue('incorrect return from isValidRow', result.isValidRow());
result.next();
assertTrue('incorrect return from isValidRow', result.isValidRow());
result.next();
assertTrue('incorrect return from isValidRow', result.isValidRow());
result.next();
assertFalse('incorrect return from isValidRow', result.isValidRow());
}
function testResultSetGetRow() {
var result = new google.wspl.gears.ResultSet(resultArray);
result.next();
var row = result.getRow();
assertEquals('first field is not valid', 'd', row.field1);
assertEquals('first field is not valid', 4, row.field2);
assertEquals('first field is not valid', 'f', row.field3);
}
</script>
</body>
</html>

View file

@ -1,196 +0,0 @@
/*
Copyright 2009 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview A Gears implementation of dbwrapperapi Transaction.
*/
/**
* Creates a Gears Transaction object.
* @see google.wspl.ResultSet#ResultSet
*
* @constructor
* @extends google.wspl.Transaction
*
* @param {number} id The unique id for this transaction
* @param {google.wspl.gears.Database} db The Gears implementation of the
* dbwrapperapi database
*/
google.wspl.gears.Transaction = function(id, db) {
google.wspl.Transaction.call(this);
/**
* The unique id for this transaction.
* @type {number}
* @private
*/
this.id_ = id;
/**
* The Gears implementation of the dbwrapperapi database.
* @type {google.wspl.gears.Database}
* @private
*/
this.db_ = db;
/**
* A map of statements, callback, and current statement.
* @type {Object}
* @private
*/
this.activeExecutes_ = {};
};
google.inherits(google.wspl.gears.Transaction, google.wspl.Transaction);
/**
* The number of active executes.
* @type {number}
* @private
*/
google.wspl.gears.Transaction.prototype.numActiveExecutes_ = 0;
/**
* The id for the next call to execute. Incremented after use.
* @type {number}
* @private
*/
google.wspl.gears.Transaction.prototype.nextCallbackId_ = 1;
/**
* Whether the transaction should be rolled back or not. This property is set
* to true when a statement fails.
* @type {boolean}
* @private
*/
google.wspl.gears.Transaction.prototype.needsRollback_ = false;
/**
* Begins a new transaction with the Gears database. Commits the transaction if
* all calls to executeAll for this transaction have finished receiving
* callbacks. Rolls the transaction back if a statement failed.
*
* @see google.wspl.Transaction#executeAll
* @inheritDoc
*/
google.wspl.gears.Transaction.prototype.executeAll = function(statements,
opt_callback) {
if (statements.length == 0) {
throw Error('Possibly silly attempt to execute empty statement list.');
}
if (this.numActiveExecutes_ == 0) {
this.db_.doBegin(this.id_);
}
this.numActiveExecutes_++;
var callbackId = this.nextCallbackId_++;
var callback = opt_callback || {
onSuccess : function() {},
onFailure : function() {}
};
this.activeExecutes_[callbackId] = {
statements: statements,
currentStatement: 0,
callback: callback
};
this.db_.doExecute(statements, callbackId, this.id_);
};
/**
* Invokes onSuccess on the specified callback.
*
* @param {google.wspl.ResultSet} result The result of a successful statement
* @param {number} callbackId The callback to invoke
*/
google.wspl.gears.Transaction.prototype.success = function(result,
callbackId) {
if (!this.needsRollback_) {
var activeExecute = this.activeExecutes_[callbackId];
activeExecute.callback.onSuccess(this, result);
}
this.endStatement_(callbackId);
};
/**
* Invokes onFailure on the specified callback.
*
* @param {Error} error The error of an unsuccessful statement
* @param {number} callbackId The callback to invoke
*/
google.wspl.gears.Transaction.prototype.failure = function(error,
callbackId) {
if (!this.needsRollback_) {
this.needsRollback_ = true;
var activeExecute = this.activeExecutes_[callbackId];
activeExecute.callback.onFailure(error);
}
this.endStatement_(callbackId);
};
/**
* Handles clean up for the end of a single execution.
*
* @param {number} callbackId The callback to clean up.
* @private
*/
google.wspl.gears.Transaction.prototype.endStatement_ = function(callbackId) {
var activeExecute = this.activeExecutes_[callbackId];
var statements = activeExecute.statements;
var currentStatement = ++activeExecute.currentStatement;
if (currentStatement == statements.length) {
this.endExecute_(callbackId);
}
};
/**
* Handles clean up for the end of a call to executeAll. Performs a commit or
* rollback if this is the last active execute to clean up.
*
* @param {number} callbackId The callback to clean up
* @private
*/
google.wspl.gears.Transaction.prototype.endExecute_ = function(callbackId) {
delete this.activeExecutes_[callbackId];
this.numActiveExecutes_--;
if (!this.isExecuting()) {
this.endTransaction_();
}
};
/**
* Instructs the worker to commit the transaction or roll it back if a failure
* occurred and a rollback is required.
*
* @private
*/
google.wspl.gears.Transaction.prototype.endTransaction_ = function() {
if (this.needsRollback_) {
this.db_.doRollback(this.id_);
} else {
this.db_.doCommit(this.id_);
}
};
/**
* @return {boolean} True if the transaction has statements executing, false
* otherwise.
*/
google.wspl.gears.Transaction.prototype.isExecuting = function() {
return this.numActiveExecutes_ > 0;
};

Some files were not shown because too many files have changed in this diff Show more