Move all templates over to Github. Remove need for auto-loaded extensions in CLI

remove_hooks
Thomas Reynolds 2014-07-20 14:25:47 -07:00
parent 9087da05d5
commit 525e700bfa
113 changed files with 67 additions and 11886 deletions

View File

@ -94,4 +94,3 @@ require 'middleman-cli/extension'
require 'middleman-cli/server'
require 'middleman-cli/build'
require 'middleman-cli/console'
require 'middleman-cli/git'

View File

@ -1,51 +0,0 @@
# CLI Module
module Middleman::Cli
# A thor task for creating new projects
class Git < Thor
include Thor::Actions
check_unknown_options!
namespace :git
desc 'git REPO TARGET [options]', 'Create new project from REPO at TARGET'
# Do not run bundle install
method_option 'skip-bundle',
type: :boolean,
aliases: '-B',
default: false,
desc: "Don't run bundle install"
# The git task
# @param [String] name
def git(repo, target='.')
require 'tmpdir'
path = repository_path(repo)
Dir.mktmpdir do |dir|
run("git clone #{path} #{dir}")
source_paths << dir
directory dir, target, exclude_pattern: /\.git\/|\.gitignore$/
inside(target) do
run('bundle install')
end unless ENV['TEST'] || options[:'skip-bundle']
end
end
protected
def repository_path(repo)
"git://github.com/#{repo}.git"
end
end
def self.exit_on_failure?
true
end
Base.map('g' => 'git')
end

View File

@ -1,48 +1,70 @@
require 'middleman-templates'
# CLI Module
module Middleman::Cli
# A thor task for creating new projects
class Init < Thor
include Thor::Actions
check_unknown_options!
namespace :init
desc 'init NAME [options]', 'Create new project NAME'
available_templates = ::Middleman::Templates.registered.keys.join(', ')
desc 'init TARGET [options]', 'Create new project at TARGET'
method_option 'template',
aliases: '-T',
default: 'default',
desc: "Use a project template: #{available_templates}"
method_option 'css_dir',
desc: 'The path to the css files'
method_option 'js_dir',
desc: 'The path to the javascript files'
method_option 'images_dir',
desc: 'The path to the image files'
method_option 'rack',
type: :boolean,
default: false,
desc: 'Include a config.ru file'
default: 'middleman/middleman-templates-default',
desc: 'Use a project template'
# Do not run bundle install
method_option 'skip-bundle',
type: :boolean,
aliases: '-B',
default: false,
desc: "Don't run bundle install"
method_option 'skip-git',
type: :boolean,
default: false,
desc: 'Skip Git ignores and keeps'
desc: 'Skip bundle install'
# The init task
# @param [String] name
def init(name='.')
key = options[:template].to_sym
unless ::Middleman::Templates.registered.key?(key)
raise Thor::Error, "Unknown project template '#{key}'"
def init(target='.')
require 'tmpdir'
repo = if shortname?(options[:template])
require 'open-uri'
require 'json'
api = 'http://directory.middlemanapp.com/api'
uri = ::URI.parse("#{api}/#{options[:template]}.json")
begin
data = ::JSON.parse(uri.read)
data['links']['github']
rescue ::OpenURI::HTTPError
puts "Template `#{options[:template]}` not found in Middleman Directory."
puts 'Did you mean to use a full `user/repo` path?'
exit
end
else
repository_path(options[:template])
end
thor_group = ::Middleman::Templates.registered[key]
thor_group.new([name], options).invoke_all
Dir.mktmpdir do |dir|
run("git clone #{repo} #{dir}")
source_paths << dir
directory dir, target, exclude_pattern: /\.git\/|\.gitignore$/
inside(target) do
run('bundle install')
end unless ENV['TEST'] || options[:'skip-bundle']
end
end
protected
def shortname?(repo)
repo.split('/').length != 2
end
def repository_path(repo)
"git://github.com/#{repo}.git"
end
end

View File

@ -1,108 +0,0 @@
# rubocop:disable FileName
# Setup our load paths
libdir = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
# Require Thor since that's what the whole CLI is built around
require 'thor'
require 'thor/group'
# Templates Module
module Middleman
module 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_method :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__), 'middleman-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
# Do not run bundle install
class_option :'skip-bundle', type: :boolean, default: false
# Write a Bundler Gemfile file for project
# @return [void]
def generate_bundler!
template self.class.gemfile_template, File.join(location, 'Gemfile')
return if options[:'skip-bundle']
inside(location) do
run('bundle install')
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
end
# Register all official templates
Dir.glob(File.expand_path('../middleman-templates/*.rb', __FILE__), &method(:require))
# Sometimes HOME doesn't exist, in which case there's no point to local templates
if ENV['HOME']
# Iterate over 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 a template.rb file is found require it (therefore registering the template)
# else register the folder as a Local template (which when built, just copies the folder)
if File.exist?(template_file)
require template_file
else
Middleman::Templates.register(File.basename(dir).to_sym, Middleman::Templates::Local)
end
end
end

View File

@ -1,31 +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
# 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><%= current_page.data.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,19 +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
# Output the files
# @return [void]
def build_scaffold!
template 'shared/config.tt', File.join(location, 'config.rb')
empty_directory File.join(location, 'source')
create_file File.join(location, 'source', '.gitkeep') unless options[:'skip-git']
end
end
# Register this template
Middleman::Templates.register(:empty, Middleman::Templates::Empty)

View File

@ -1,23 +0,0 @@
# HTML5 Boilerplate template
class Middleman::Templates::Html5 < Middleman::Templates::Base
# Slightly different paths
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'
# 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 'html5/source', File.join(location, 'source')
end
end
# Register this template
Middleman::Templates.register(:html5, Middleman::Templates::Html5)

View File

@ -1,679 +0,0 @@
# Apache Server Configs v2.3.0 | MIT License
# https://github.com/h5bp/server-configs-apache
# (!) Using `.htaccess` files slows down Apache, therefore, if you have access
# to the main server config file (usually called `httpd.conf`), you should add
# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html.
# ##############################################################################
# # CROSS-ORIGIN RESOURCE SHARING (CORS) #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Cross-domain AJAX requests |
# ------------------------------------------------------------------------------
# Allow cross-origin AJAX requests.
# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity
# http://enable-cors.org/
# <IfModule mod_headers.c>
# Header set Access-Control-Allow-Origin "*"
# </IfModule>
# ------------------------------------------------------------------------------
# | CORS-enabled images |
# ------------------------------------------------------------------------------
# Send the CORS header for images when browsers request it.
# https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html
# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
# ------------------------------------------------------------------------------
# | Web fonts access |
# ------------------------------------------------------------------------------
# Allow access to web fonts from all domains.
<IfModule mod_headers.c>
<FilesMatch "\.(eot|otf|tt[cf]|woff)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
# ##############################################################################
# # ERRORS #
# ##############################################################################
# ------------------------------------------------------------------------------
# | 404 error prevention for non-existing redirected folders |
# ------------------------------------------------------------------------------
# Prevent Apache from returning a 404 error as the result of a rewrite
# when the directory with the same name does not exist.
# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews
# http://www.webmasterworld.com/apache/3808792.htm
Options -MultiViews
# ------------------------------------------------------------------------------
# | Custom error messages / pages |
# ------------------------------------------------------------------------------
# Customize what Apache returns to the client in case of an error.
# http://httpd.apache.org/docs/current/mod/core.html#errordocument
ErrorDocument 404 /404.html
# ##############################################################################
# # INTERNET EXPLORER #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Better website experience |
# ------------------------------------------------------------------------------
# Force Internet Explorer to render pages in the highest available mode
# in the various cases when it may not.
# http://hsivonen.iki.fi/doctype/ie-mode.pdf
<IfModule mod_headers.c>
Header set X-UA-Compatible "IE=edge"
# `mod_headers` cannot match based on the content-type, however, this
# header should be send only for HTML pages and not for the other resources
<FilesMatch "\.(appcache|atom|crx|css|cur|eot|f4[abpv]|flv|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|tt[cf]|vcf|vtt|webapp|web[mp]|woff|xml|xpi)$">
Header unset X-UA-Compatible
</FilesMatch>
</IfModule>
# ------------------------------------------------------------------------------
# | Cookie setting from iframes |
# ------------------------------------------------------------------------------
# Allow cookies to be set from iframes in Internet Explorer.
# http://msdn.microsoft.com/en-us/library/ms537343.aspx
# http://www.w3.org/TR/2000/CR-P3P-20001215/
# <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>
# ------------------------------------------------------------------------------
# | Screen flicker |
# ------------------------------------------------------------------------------
# Stop screen flicker in Internet Explorer on CSS rollovers.
# IMPORTANT: This will only work in combination with the image related
# `ExpiresByType` directives from below.
# BrowserMatch "MSIE" brokenvary=1
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
# BrowserMatch "Opera" !brokenvary
# SetEnvIf brokenvary 1 force-no-vary
# ##############################################################################
# # MIME TYPES AND ENCODING #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Proper MIME types for all files |
# ------------------------------------------------------------------------------
<IfModule mod_mime.c>
# Audio
AddType audio/mp4 m4a f4a f4b
AddType audio/ogg oga ogg opus
# Data interchange
AddType application/json json map
AddType application/ld+json jsonld
# JavaScript
# Normalize to standard type.
# http://tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript js
# Video
AddType video/mp4 f4v f4p m4v mp4
AddType video/ogg ogv
AddType video/webm webm
AddType video/x-flv flv
# Web fonts
AddType application/font-woff woff
AddType application/vnd.ms-fontobject eot
# Browsers usually ignore the font MIME types and simply sniff the bytes
# to figure out the font type.
# http://mimesniff.spec.whatwg.org/#matching-a-font-type-pattern
# Chrome however, shows a warning if any other MIME types are used for
# the following fonts.
AddType application/x-font-ttf ttc ttf
AddType font/opentype otf
# Make SVGZ fonts work on the iPad.
# https://twitter.com/FontSquirrel/status/14855840545
AddType image/svg+xml svgz
AddEncoding gzip svgz
# Other
AddType application/octet-stream safariextz
AddType application/x-chrome-extension crx
AddType application/x-opera-extension oex
AddType application/x-web-app-manifest+json webapp
AddType application/x-xpinstall xpi
AddType application/xml atom rdf rss xml
AddType image/webp webp
AddType image/x-icon cur
AddType text/cache-manifest appcache manifest
AddType text/vtt vtt
AddType text/x-component htc
AddType text/x-vcard vcf
</IfModule>
# ------------------------------------------------------------------------------
# | UTF-8 encoding |
# ------------------------------------------------------------------------------
# Use UTF-8 encoding for anything served as `text/html` or `text/plain`.
AddDefaultCharset utf-8
# Force UTF-8 for certain file formats.
<IfModule mod_mime.c>
AddCharset utf-8 .atom .css .js .json .jsonld .rss .vtt .webapp .xml
</IfModule>
# ##############################################################################
# # URL REWRITES #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Rewrite engine |
# ------------------------------------------------------------------------------
# Turn on the rewrite engine and enable the `FollowSymLinks` option (this is
# necessary in order for the following directives to work).
# If your web host doesn't allow the `FollowSymlinks` option, you may need to
# comment it out and use `Options +SymLinksIfOwnerMatch`, but be aware of the
# performance impact.
# http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks
# Also, some cloud hosting services require `RewriteBase` to be set.
# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site
<IfModule mod_rewrite.c>
Options +FollowSymlinks
# Options +SymLinksIfOwnerMatch
RewriteEngine On
# RewriteBase /
</IfModule>
# ------------------------------------------------------------------------------
# | Suppressing / Forcing 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. This can cause
# SEO problems (duplicate content), and therefore, you should choose one
# of the alternatives and redirect the other one.
# By default `Option 1` (no `www.`) is activated.
# http://no-www.org/faq.php?q=class_b
# If you would prefer to use `Option 2`, just comment out all the lines
# from `Option 1` and uncomment the ones from `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 might not be a good idea if you use "real"
# subdomains for certain parts of your website.
# <IfModule mod_rewrite.c>
# RewriteCond %{HTTPS} !=on
# RewriteCond %{HTTP_HOST} !^www\. [NC]
# RewriteCond %{SERVER_ADDR} !=127.0.0.1
# RewriteCond %{SERVER_ADDR} !=::1
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# </IfModule>
# ##############################################################################
# # SECURITY #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Clickjacking |
# ------------------------------------------------------------------------------
# Protect website against clickjacking.
# The example below sends the `X-Frame-Options` response header with the value
# `DENY`, informing browsers not to display the web page content in any frame.
# This might not be the best setting for everyone. You should read about the
# other two possible values for `X-Frame-Options`: `SAMEORIGIN` & `ALLOW-FROM`.
# http://tools.ietf.org/html/rfc7034#section-2.1
# Keep in mind that while you could send the `X-Frame-Options` header for all
# of your sites pages, this has the potential downside that it forbids even
# non-malicious framing of your content (e.g.: when users visit your site using
# a Google Image Search results page).
# Nonetheless, you should ensure that you send the `X-Frame-Options` header for
# all pages that allow a user to make a state changing operation (e.g: pages
# that contain one-click purchase links, checkout or bank-transfer confirmation
# pages, pages that make permanent configuration changes, etc.).
# Sending the `X-Frame-Options` header can also protect your website against
# more than just clickjacking attacks: https://cure53.de/xfo-clickjacking.pdf.
# http://tools.ietf.org/html/rfc7034
# http://blogs.msdn.com/b/ieinternals/archive/2010/03/30/combating-clickjacking-with-x-frame-options.aspx
# https://www.owasp.org/index.php/Clickjacking
# <IfModule mod_headers.c>
# Header set X-Frame-Options "DENY"
# <FilesMatch "\.(appcache|atom|crx|css|cur|eot|f4[abpv]|flv|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|tt[cf]|vcf|vtt|webapp|web[mp]|woff|xml|xpi)$">
# Header unset X-Frame-Options
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | Content Security Policy (CSP) |
# ------------------------------------------------------------------------------
# Mitigate the risk of cross-site scripting and other content-injection attacks.
# This can be done by setting a `Content Security Policy` which whitelists
# trusted sources of content for your website.
# The example header below allows ONLY scripts that are loaded from the current
# site's origin (no inline scripts, no CDN, etc). This almost certainly won't
# work as-is for your site!
# For more details on how to craft a reasonable policy for your site, read:
# http://html5rocks.com/en/tutorials/security/content-security-policy (or the
# specification: http://w3.org/TR/CSP). Also, to make things easier, you can
# use an online CSP header generator such as: http://cspisawesome.com/.
# <IfModule mod_headers.c>
# Header set Content-Security-Policy "script-src 'self'; object-src 'self'"
# <FilesMatch "\.(appcache|atom|crx|css|cur|eot|f4[abpv]|flv|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|tt[cf]|vcf|vtt|webapp|web[mp]|woff|xml|xpi)$">
# Header unset Content-Security-Policy
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | File access |
# ------------------------------------------------------------------------------
# Block access to directories without a default document.
# You should leave the following uncommented, as you shouldn't allow anyone to
# surf through every directory on your server (which may includes rather private
# places such as the CMS's directories).
<IfModule mod_autoindex.c>
Options -Indexes
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Block access to hidden files and directories.
# This includes directories used by version control systems such as Git and SVN.
<IfModule mod_rewrite.c>
RewriteCond %{SCRIPT_FILENAME} -d [OR]
RewriteCond %{SCRIPT_FILENAME} -f
RewriteRule "(^|/)\." - [F]
</IfModule>
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Block access to files that can expose sensitive information.
# By default, block access to backup and source files that may be left by some
# text editors and can pose a security risk when anyone has access to them.
# http://feross.org/cmsploit/
# IMPORTANT: Update the `<FilesMatch>` regular expression from below to include
# any files that might end up on your production server and can expose sensitive
# information about your website. These files may include: configuration files,
# files that contain metadata about the project (e.g.: project dependencies),
# build scripts, etc..
<FilesMatch "(^#.*#|\.(bak|config|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$">
# Apache < 2.3
<IfModule !mod_authz_core.c>
Order allow,deny
Deny from all
Satisfy All
</IfModule>
# Apache ≥ 2.3
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
</FilesMatch>
# ------------------------------------------------------------------------------
# | Reducing MIME type security risks |
# ------------------------------------------------------------------------------
# Prevent some browsers from MIME-sniffing the response.
# This reduces exposure to drive-by download attacks and cross-origin data
# leaks, and should be left uncommented, especially if the web server is
# serving user-uploaded content or content that could potentially be treated
# as executable by the browser.
# http://www.slideshare.net/hasegawayosuke/owasp-hasegawa
# http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
# http://msdn.microsoft.com/en-us/library/ie/gg622941.aspx
# http://mimesniff.spec.whatwg.org/
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
</IfModule>
# ------------------------------------------------------------------------------
# | Reflected Cross-Site Scripting (XSS) attacks |
# ------------------------------------------------------------------------------
# (1) Try to re-enable the Cross-Site Scripting (XSS) filter built into the
# most recent web browsers.
#
# The filter is usually enabled by default, but in some cases it may be
# disabled by the user. However, in Internet Explorer for example, it can
# be re-enabled just by sending the `X-XSS-Protection` header with the
# value of `1`.
#
# (2) Prevent web browsers from rendering the web page if a potential reflected
# (a.k.a non-persistent) XSS attack is detected by the filter.
#
# By default, if the filter is enabled and browsers detect a reflected
# XSS attack, they will attempt to block the attack by making the smallest
# possible modifications to the returned web page.
#
# Unfortunately, in some browsers (e.g.: Internet Explorer), this default
# behavior may allow the XSS filter to be exploited, thereby, it's better
# to tell browsers to prevent the rendering of the page altogether, instead
# of attempting to modify it.
#
# http://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities
#
# IMPORTANT: Do not rely on the XSS filter to prevent XSS attacks! Ensure that
# you are taking all possible measures to prevent XSS attacks, the most obvious
# being: validating and sanitizing your site's inputs.
#
# http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx
# http://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx
# https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
# <IfModule mod_headers.c>
# # (1) (2)
# Header set X-XSS-Protection "1; mode=block"
# <FilesMatch "\.(appcache|atom|crx|css|cur|eot|f4[abpv]|flv|gif|htc|ico|jpe?g|js|json(ld)?|m4[av]|manifest|map|mp4|oex|og[agv]|opus|otf|pdf|png|rdf|rss|safariextz|svgz?|swf|tt[cf]|vcf|vtt|webapp|web[mp]|woff|xml|xpi)$">
# Header unset X-XSS-Protection
# </FilesMatch>
# </IfModule>
# ------------------------------------------------------------------------------
# | Secure Sockets Layer (SSL) |
# ------------------------------------------------------------------------------
# Rewrite secure requests properly in order to prevent SSL certificate warnings.
# E.g.: prevent `https://www.example.com` when your certificate 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>
# ------------------------------------------------------------------------------
# | HTTP Strict Transport Security (HSTS) |
# ------------------------------------------------------------------------------
# Force client-side SSL redirection.
# If a user types `example.com` in his browser, the above rule will redirect
# him to the secure version of the site. That still leaves a window of
# opportunity (the initial HTTP connection) for an attacker to downgrade or
# redirect the request.
# The following header ensures that browser will ONLY connect to your server
# via HTTPS, regardless of what the users type in the address bar.
# http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14#section-6.1
# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/
# IMPORTANT: Remove the `includeSubDomains` optional directive if the subdomains
# are not using HTTPS.
# <IfModule mod_headers.c>
# Header set Strict-Transport-Security "max-age=16070400; includeSubDomains"
# </IfModule>
# ------------------------------------------------------------------------------
# | Server software information |
# ------------------------------------------------------------------------------
# Avoid displaying the exact Apache version number, the description of the
# generic OS-type and the information about Apache's compiled-in modules.
# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`!
# ServerTokens Prod
# ##############################################################################
# # WEB PERFORMANCE #
# ##############################################################################
# ------------------------------------------------------------------------------
# | Compression |
# ------------------------------------------------------------------------------
<IfModule mod_deflate.c>
# Force compression for mangled headers.
# http://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
# (for Apache versions below 2.3.7, you don't need to enable `mod_filter`
# and can remove the `<IfModule mod_filter.c>` and `</IfModule>` lines
# as `AddOutputFilterByType` is still in the core directives).
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/atom+xml \
application/javascript \
application/json \
application/ld+json \
application/rss+xml \
application/vnd.ms-fontobject \
application/x-font-ttf \
application/x-web-app-manifest+json \
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>
# ------------------------------------------------------------------------------
# | Content transformations |
# ------------------------------------------------------------------------------
# Prevent mobile network providers from modifying the website's content.
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5.
# <IfModule mod_headers.c>
# Header set Cache-Control "no-transform"
# </IfModule>
# ------------------------------------------------------------------------------
# | ETags |
# ------------------------------------------------------------------------------
# Remove `ETags` as resources are sent with far-future expires headers.
# http://developer.yahoo.com/performance/rules.html#etags.
# `FileETag None` doesn't work in all cases.
<IfModule mod_headers.c>
Header unset ETag
</IfModule>
FileETag None
# ------------------------------------------------------------------------------
# | Expires headers |
# ------------------------------------------------------------------------------
# The following expires headers are set pretty far in the future. If you
# don't control versioning with filename-based cache busting, consider
# lowering the cache time for resources such as style sheets and JavaScript
# files to something like one week.
<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault "access plus 1 month"
# CSS
ExpiresByType text/css "access plus 1 year"
# Data interchange
ExpiresByType application/json "access plus 0 seconds"
ExpiresByType application/ld+json "access plus 0 seconds"
ExpiresByType application/xml "access plus 0 seconds"
ExpiresByType text/xml "access plus 0 seconds"
# Favicon (cannot be renamed!) and cursor images
ExpiresByType image/x-icon "access plus 1 week"
# HTML components (HTCs)
ExpiresByType text/x-component "access plus 1 month"
# HTML
ExpiresByType text/html "access plus 0 seconds"
# JavaScript
ExpiresByType application/javascript "access plus 1 year"
# Manifest files
ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds"
ExpiresByType text/cache-manifest "access plus 0 seconds"
# Media
ExpiresByType audio/ogg "access plus 1 month"
ExpiresByType image/gif "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType video/mp4 "access plus 1 month"
ExpiresByType video/ogg "access plus 1 month"
ExpiresByType video/webm "access plus 1 month"
# Web feeds
ExpiresByType application/atom+xml "access plus 1 hour"
ExpiresByType application/rss+xml "access plus 1 hour"
# Web fonts
ExpiresByType application/font-woff "access plus 1 month"
ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
ExpiresByType application/x-font-ttf "access plus 1 month"
ExpiresByType font/opentype "access plus 1 month"
ExpiresByType image/svg+xml "access plus 1 month"
</IfModule>
# ------------------------------------------------------------------------------
# | Filename-based cache busting |
# ------------------------------------------------------------------------------
# If you're not using a build process to manage your filename version revving,
# you might want to consider enabling the following directives to route all
# requests such as `/css/style.12345.css` to `/css/style.css`.
# To understand why this is important and a better idea than `*.css?v231`, read:
# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring
# <IfModule mod_rewrite.c>
# RewriteCond %{REQUEST_FILENAME} !-f
# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpe?g|gif)$ $1.$3 [L]
# </IfModule>
# ------------------------------------------------------------------------------
# | File concatenation |
# ------------------------------------------------------------------------------
# Allow concatenation from within specific style sheets and JavaScript files.
# e.g.:
#
# If you have the following content in a file
#
# <!--#include file="libs/jquery.js" -->
# <!--#include file="plugins/jquery.timer.js" -->
#
# Apache will replace it with the content from the specified files.
# <IfModule mod_include.c>
# <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>
# </IfModule>

View File

@ -1,59 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Page Not Found</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
line-height: 1.5;
margin: 0;
}
html {
color: #888;
font-family: sans-serif;
text-align: center;
}
body {
left: 50%;
margin: -43px 0 0 -150px;
position: absolute;
top: 50%;
width: 300px;
}
h1 {
color: #555;
font-size: 2em;
font-weight: 400;
}
p {
line-height: 1.2;
}
@media only screen and (max-width: 270px) {
body {
margin: 10px auto;
position: static;
width: 95%;
}
h1 {
font-size: 1.5em;
}
}
</style>
</head>
<body>
<h1>Page Not Found</h1>
<p>Sorry, but the page you were trying to view does not exist.</p>
</body>
</html>
<!-- IE needs 512+ bytes: http://blogs.msdn.com/b/ieinternals/archive/2010/08/19/http-error-pages-in-internet-explorer.aspx -->

View File

@ -1,197 +0,0 @@
### HEAD
* Update to Apache Server Configs 2.3.0.
* Use `<!doctype html>` instead of `<!DOCTYPE html>`
([#1522](https://github.com/h5bp/html5-boilerplate/issues/1522)).
* Update to jQuery 1.11.0.
* Add `Disallow:` to `robots.txt`
([#1487](https://github.com/h5bp/html5-boilerplate/issues/1487)).
* Remove default foreground color from form elements
([#1390](https://github.com/h5bp/html5-boilerplate/issues/1390)).
* Remove default margin from print styles
([#1477](https://github.com/h5bp/html5-boilerplate/issues/1477)).
* Update to Modernizr 2.7.1.
* Add vertical centering for `svg`
([#1453](https://github.com/h5bp/html5-boilerplate/issues/1453)).
* Redesign 404 page
([#1443](https://github.com/h5bp/html5-boilerplate/pull/1443)).
### 4.3.0 (September 10, 2013)
* Use one apple-touch-icon instead of six
([#1367](https://github.com/h5bp/html5-boilerplate/issues/1367)).
* Move font-related declarations from `body` to `html`
([#1411](https://github.com/h5bp/html5-boilerplate/issues/1411)).
* Update to Apache Server Configs 1.1.0.
* Add `initial-scale=1` to the viewport `meta`
([#1398](https://github.com/h5bp/html5-boilerplate/pull/1398)).
* Vertical centering for audio-, canvas- and video-tags
([#1326](https://github.com/h5bp/html5-boilerplate/issues/1326)).
* Remove Google Chrome Frame related code
([#1379](https://github.com/h5bp/html5-boilerplate/pull/1379),
[#1396](https://github.com/h5bp/html5-boilerplate/pull/1396)).
* Update to Google Universal Analytics
([#1347](https://github.com/h5bp/html5-boilerplate/issues/1347)).
* Update to jQuery 1.10.2.
* Update to Normalize.css 1.1.3.
### 4.2.0 (April 8, 2013)
* Remove Google Analytics protocol check
([#1319](https://github.com/h5bp/html5-boilerplate/pull/1319)).
* Update to Normalize.css 1.1.1.
* Update Apache configurations to include the latest changes in the
canonical [`.htaccess`](https://github.com/h5bp/server-configs-apache)
file.
* Use a protocol relative URL for the 404 template script.
* Update to jQuery 1.9.1.
### 4.1.0 (January 21, 2013)
* Update to Normalize.css 1.1.0.
* Update to jQuery 1.9.0.
### 4.0.3 (January 12, 2013)
* Use 32x32 favicon.ico
([#1286](https://github.com/h5bp/html5-boilerplate/pull/1286)).
* Remove named function expression in plugins.js
([#1280](https://github.com/h5bp/html5-boilerplate/pull/1280)).
* Adjust CSS image-replacement code
([#1239](https://github.com/h5bp/html5-boilerplate/issues/1239)).
* Update HiDPI example media query
([#1127](https://github.com/h5bp/html5-boilerplate/issues/1127)).
### 4.0.2 (December 9, 2012)
* Update placeholder icons.
* Update to Normalize.css 1.0.2.
* Update to jQuery 1.8.3.
### 4.0.1 (October 20, 2012)
* Further improvements to `console` method stubbing
([#1206](https://github.com/h5bp/html5-boilerplate/issues/1206),
[#1229](https://github.com/h5bp/html5-boilerplate/pull/1229)).
* Update to jQuery 1.8.2.
* Update to Modernizr 2.6.2.
* Minor additions to the documentation.
### 4.0.0 (August 28, 2012)
* Improve the Apache compression configuration
([#1012](https://github.com/h5bp/html5-boilerplate/issues/1012),
[#1173](https://github.com/h5bp/html5-boilerplate/issues/1173)).
* Add a HiDPI example media query
([#1127](https://github.com/h5bp/html5-boilerplate/issues/1127)).
* Add bundled docs
([#1154](https://github.com/h5bp/html5-boilerplate/issues/1154)).
* Add MIT license
([#1139](https://github.com/h5bp/html5-boilerplate/issues/1139)).
* Update to Normalize.css 1.0.1.
* Separate Normalize.css from the rest of the CSS
([#1160](https://github.com/h5bp/html5-boilerplate/issues/1160)).
* Improve `console.log` protection
([#1107](https://github.com/h5bp/html5-boilerplate/issues/1107)).
* Replace hot pink text selection color with a neutral color.
* Change image replacement technique
([#1149](https://github.com/h5bp/html5-boilerplate/issues/1149)).
* Code format and consistency changes
([#1112](https://github.com/h5bp/html5-boilerplate/issues/1112)).
* Rename CSS file and rename JS files and subdirectories.
* Update to jQuery 1.8
([#1161](https://github.com/h5bp/html5-boilerplate/issues/1161)).
* Update to Modernizr 2.6.1
([#1086](https://github.com/h5bp/html5-boilerplate/issues/1086)).
* Remove uncompressed jQuery
([#1153](https://github.com/h5bp/html5-boilerplate/issues/1153)).
* Remove superfluous inline comments
([#1150](https://github.com/h5bp/html5-boilerplate/issues/1150)).
### 3.0.2 (February 19, 2012)
* Update to Modernizr 2.5.3.
### 3.0.1 (February 08, 2012).
* Update to Modernizr 2.5.2 (includes html5shiv 3.3).
### 3.0.0 (February 06, 2012)
* Improvements to `.htaccess`.
* Improve 404 design.
* Simplify JS folder structure.
* Change `html` IE class names changed to target ranges rather than
specific versions of IE.
* Update CSS to include latest normalize.css changes and better
typographic defaults
([#825](https://github.com/h5bp/html5-boilerplate/issues/825)).
* Update to Modernizr 2.5 (includes yepnope 1.5 and html5shiv 3.2).
* Update to jQuery 1.7.1.
* Revert to async snippet for the Google Analytics script.
* Remove the ant build script
([#826](https://github.com/h5bp/html5-boilerplate/issues/826)).
* Remove Respond.js
([#816](https://github.com/h5bp/html5-boilerplate/issues/816)).
* Remove the `demo/` directory
([#808](https://github.com/h5bp/html5-boilerplate/issues/808)).
* Remove the `test/` directory
([#808](https://github.com/h5bp/html5-boilerplate/issues/808)).
* Remove Google Chrome Frame script for IE6 users; replace with links
to Chrome Frame and options for alternative browsers.
* Remove `initial-scale=1` from the viewport `meta`
([#824](https://github.com/h5bp/html5-boilerplate/issues/824)).
* Remove `defer` from all scripts to avoid legacy IE bugs.
* Remove explicit Site Speed tracking for Google Analytics. It's now
enabled by default.
### 2.0.0 (August 10, 2011)
* Change starting CSS to be based on normalize.css instead of reset.css
([#500](https://github.com/h5bp/html5-boilerplate/issues/500)).
* Add Respond.js media query polyfill.
* Add Google Chrome Frame script prompt for IE6 users.
* Simplify the `html` conditional comments for modern browsers and add
an `oldie` class.
* Update clearfix to use "micro clearfix".
* Add placeholder CSS MQs for mobile-first approach.
* Add `textarea { resize: vertical; }` to only allow vertical resizing.
* Add `img { max-width: 100%; }` to the print styles; prevents images
being truncated.
* Add Site Speed tracking for Google Analytics.
* Update to jQuery 1.6.2 (and use minified by default).
* Update to Modernizr 2.0 Complete, Production minified (includes
yepnope, html5shiv, and Respond.js).
* Use `Modernizr.load()` to load the Google Analytics script.
* Much faster build process.
* Add build script options for CSSLint, JSLint, JSHint tools.
* Build script now compresses all images in subfolders.
* Build script now versions files by SHA hash.
* Many `.htaccess` improvements including: disable directory browsing,
improved support for all versions of Apache, more robust and extensive
HTTP compression rules.
* Remove `handheld.css` as it has very poor device support.
* Remove touch-icon `link` elements from the HTML and include improved
touch-icon support.
* Remove the cache-busting query paramaters from files references in
the HTML.
* Remove IE6 PNGFix.
### 1.0.0 (March 21, 2011)
* Rewrite build script to make it more customizable and flexible.
* Add a humans.txt.
* Numerous `.htaccess` improvements (including inline documentation).
* Move the alternative server configurations to the H5BP server configs
repo.
* Use a protocol-relative url to reference jQuery and prevent mixed
content warnings.
* Optimize the Google Analytics snippet.
* Use Eric Meyer's recent CSS reset update and the HTML5 Doctor reset.
* More robust `sub`/`sup` CSS styles.
* Add keyboard `.focusable` helper class that extends `.visuallyhidden`.
* Print styles no longer print hash or JavaScript links.
* Add a print reset for IE's proprietary filters.
* Remove IE9-specific conditional class on the `html` element.
* Remove margins from lists within `nav` elements.
* Remove YUI profiling.

View File

@ -1,154 +0,0 @@
# Contributing to HTML5 Boilerplate
♥ [HTML5 Boilerplate](http://html5boilerplate.com) and want to get involved?
Thanks! There are plenty of ways you can help!
Please take a moment to review this document in order to make the contribution
process easy and effective for everyone involved.
Following these guidelines helps to communicate that you respect the time of
the developers managing and developing this open source project. In return,
they should reciprocate that respect in addressing your issue or assessing
patches and features.
## Using the issue tracker
The [issue tracker](https://github.com/h5bp/html5-boilerplate/issues) is
the preferred channel for [bug reports](#bugs), [features requests](#features)
and [submitting pull requests](#pull-requests), but please respect the following
restrictions:
* Please **do not** use the issue tracker for personal support requests (use
[Stack Overflow](http://stackoverflow.com/questions/tagged/html5boilerplate)
or IRC).
* Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
* Please **do not** open issues or pull requests regarding the code in
[`.htaccess`](https://github.com/h5bp/server-configs-apache),
[`jQuery`](https://github.com/jquery/jquery/),
[`Modernizr`](https://github.com/Modernizr/Modernizr) or
[`Normalize.css`](https://github.com/necolas/normalize.css) (open them in
their respective repositories).
<a name="bugs"></a>
## Bug reports
A bug is a _demonstrable problem_ that is caused by the code in the repository.
Good bug reports are extremely helpful - thank you!
Guidelines for bug reports:
1. **Use the GitHub issue search** &mdash; check if the issue has already been
reported.
2. **Check if the issue has been fixed** &mdash; try to reproduce it using the
latest `master` or development branch in the repository.
3. **Isolate the problem** &mdash; ideally create a [reduced test
case](http://css-tricks.com/6263-reduced-test-cases/) and a live example.
A good bug report shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report. What is
your environment? What steps will reproduce the issue? What browser(s) and OS
experience the problem? What would you expect to be the outcome? All these
details will help people to fix any potential bugs.
Example:
> Short and descriptive example bug report title
>
> A summary of the issue and the browser/OS environment in which it occurs. If
> suitable, include the steps required to reproduce the bug.
>
> 1. This is the first step
> 2. This is the second step
> 3. Further steps, etc.
>
> `<url>` - a link to the reduced test case
>
> Any other information you want to share that is relevant to the issue being
> reported. This might include the lines of code that you have identified as
> causing the bug, and potential solutions (and your opinions on their
> merits).
<a name="features"></a>
## Feature requests
Feature requests are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature. Please
provide as much detail and context as possible.
<a name="pull-requests"></a>
## Pull requests
Good pull requests - patches, improvements, new features - are a fantastic
help. They should remain focused in scope and avoid containing unrelated
commits.
**Please ask first** before embarking on any significant pull request (e.g.
implementing features, refactoring code, porting to a different language),
otherwise you risk spending a lot of time working on something that the
project's developers might not want to merge into the project.
Please adhere to the coding conventions used throughout a project (indentation,
accurate comments, etc.) and any other requirements (such as test coverage).
Adhering to the following this process is the best way to get your work
included in the project:
1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork,
and configure the remotes:
```bash
# Clone your fork of the repo into the current directory
git clone https://github.com/<your-username>/html5-boilerplate.git
# Navigate to the newly cloned directory
cd html5-boilerplate
# Assign the original repo to a remote called "upstream"
git remote add upstream https://github.com/h5bp/html5-boilerplate.git
```
2. If you cloned a while ago, get the latest changes from upstream:
```bash
git checkout master
git pull upstream master
```
3. Create a new topic branch (off the main project development branch) to
contain your feature, change, or fix:
```bash
git checkout -b <topic-branch-name>
```
4. Commit your changes in logical chunks. Please adhere to these [git commit
message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
or your code is unlikely be merged into the main project. Use Git's
[interactive rebase](https://help.github.com/articles/interactive-rebase)
feature to tidy up your commits before making them public.
5. Locally merge (or rebase) the upstream development branch into your topic branch:
```bash
git pull [--rebase] upstream master
```
6. Push your topic branch up to your fork:
```bash
git push origin <topic-branch-name>
```
7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/)
with a clear title and description.
**IMPORTANT**: By submitting a patch, you agree to allow the project owners to
license your work under the the terms of the [MIT License](LICENSE.md).

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,62 +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](https://github.com/h5bp/html5-boilerplate/releases) 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](doc/TOC.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](CONTRIBUTING.md). Hundreds of
developers have helped make the HTML5 Boilerplate what it is today.

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,294 +0,0 @@
/*! HTML5 Boilerplate v4.3.0 | MIT License | http://h5bp.com/ */
/*
* 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 {
color: #222;
font-size: 1em;
line-height: 1.4;
}
/*
* Remove text-shadow in selection highlight: h5bp.com/i
* These selection rule sets 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, videos, audio and canvas and the bottom of
* their containers: h5bp.com/i/440
*/
audio,
canvas,
img,
svg,
video {
vertical-align: middle;
}
/*
* Remove default fieldset styles.
*/
fieldset {
border: 0;
margin: 0;
padding: 0;
}
/*
* Allow only vertical resizing of textareas.
*/
textarea {
resize: vertical;
}
/* ==========================================================================
Browse Happy prompt
========================================================================== */
.browsehappy {
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: 150%;
}
/*
* 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.
These 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 print,
(-o-min-device-pixel-ratio: 5/4),
(-webkit-min-device-pixel-ratio: 1.25),
(min-resolution: 120dpi) {
/* 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;
}
p,
h2,
h3 {
orphans: 3;
widows: 3;
}
h2,
h3 {
page-break-after: avoid;
}
}

View File

@ -1,527 +0,0 @@
/*! normalize.css v1.1.3 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
/**
* Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/**
* 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Address `font-family` inconsistency between `textarea` and other form
* elements.
*/
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/**
* Address margins handled incorrectly in IE 6/7.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/**
* Address `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/**
* Address font sizes and margins set differently in IE 6/7.
* Address 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.67em;
margin: 2.33em 0;
}
/**
* Address styling not present in IE 7/8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/**
* Address styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address differences between Firefox and other browsers.
* Known issue: no IE 6/7 normalization.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Address styling not present in IE 6/7/8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address margins set differently in IE 6/7.
*/
p,
pre {
margin: 1em 0;
}
/**
* Correct 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;
}
/**
* Improve readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/**
* Address CSS quotes not supported in IE 6/7.
*/
q {
quotes: none;
}
/**
* Address `quotes` property not supported in Safari 4.
*/
q:before,
q:after {
content: '';
content: none;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `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
========================================================================== */
/**
* Address margins set differently in IE 6/7.
*/
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
/**
* Address paddings set differently in IE 6/7.
*/
menu,
ol,
ul {
padding: 0 0 0 40px;
}
/**
* Correct list images handled incorrectly in IE 7.
*/
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* ==========================================================================
Embedded content
========================================================================== */
/**
* 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improve image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/**
* Correct overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/**
* Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/**
* Correct 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. Correct color not being inherited in IE 6/7/8/9.
* 2. Correct text not wrapping in Firefox 3.
* 3. Correct alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/**
* 1. Correct font size not being inherited in all browsers.
* 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improve 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 */
}
/**
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
* Correct `select` style inheritance in Firefox 4+ and Opera.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
* 4. Remove 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],
html input[disabled] {
cursor: default;
}
/**
* 1. Address box sizing set to content-box in IE 8/9.
* 2. Remove excess padding in IE 8/9.
* 3. Remove 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. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Address `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;
}
/**
* Remove 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;
}
/**
* Remove inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* 1. Remove default vertical scrollbar in IE 6/7/8/9.
* 2. Improve 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: 766 B

View File

@ -1,15 +0,0 @@
# humanstxt.org/
# The humans responsible & technology colophon
# TEAM
<name> -- <role> -- <twitter>
# THANKS
<name>
# TECHNOLOGY COLOPHON
HTML5, CSS3
Normalize.css, 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,24 +0,0 @@
// Avoid `console` errors in browsers that lack a console.
(function() {
var method;
var noop = function () {};
var methods = [
'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
'timeStamp', 'trace', 'warn'
];
var length = methods.length;
var console = (window.console = window.console || {});
while (length--) {
method = methods[length];
// Only stub undefined methods.
if (!console[method]) {
console[method] = 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,42 +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">
<!-- 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, initial-scale=1">
<!-- Place favicon.ico and apple-touch-icon(s) in the root directory -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.7.1.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<%= yield %>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.11.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>
(function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
e=o.createElement(i);r=o.getElementsByTagName(i)[0];
e.src='//www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
ga('create','UA-XXXXX-X');ga('send','pageview');
</script>
</body>
</html>

View File

@ -1,5 +0,0 @@
# www.robotstxt.org/
# Allow crawling of all content
User-agent: *
Disallow:

View File

@ -1,17 +0,0 @@
# Local templates
class Middleman::Templates::Local < Middleman::Templates::Base
# Look for templates inside .middleman in the user's home directory
# @return [String]
def self.source_root
File.join(Dir.home, '.middleman')
end
# Just copy from the template path
# @return [void]
def build_scaffold!
directory options[:template].to_s, location
end
end
# Register this template
Middleman::Templates.register(:local, Middleman::Templates::Local)

View File

@ -1,23 +0,0 @@
# Mobile HTML5 Boilerplate
class Middleman::Templates::Mobile < Middleman::Templates::Base
# Slightly different paths
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'
# 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')
end
end
# Register this 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.0 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;
};

View File

@ -1,221 +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 transaction 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>
<script type="text/javascript" src="gears_transaction.js"></script>
</head>
<body>
<script type='text/javascript'>
var mockControl;
var callbackMock;
var dbMock;
function setUp() {
mockControl = new MockControl();
callbackMock = mockControl.createMock({
onSuccess: function(){},
onFailure: function(){}
});
dbMock = mockControl.createMock({
doBegin: function(){},
doCommit: function(){},
doRollback: function(){},
doExecute: function(){}
});
}
function testConstructTransaction() {
var trans = new google.wspl.gears.Transaction(10, 'foo');
assertEquals('foo', trans.db_);
assertEquals(10, trans.id_);
}
function testExecuteAll_noStatements() {
var trans = new google.wspl.gears.Transaction(27, dbMock);
try {
trans.executeAll([], callbackMock);
fail('Should never get here');
} catch(e) {
if (e.isJsUnitException) {
throw e;
}
assertEquals('did not exception fault on empty statement list',
'Error: Possibly silly attempt to execute empty statement list.',
e.toString());
}
mockControl.verify();
}
function testExecuteAll_noCallback() {
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 transactionId = 27;
var callbackId = 1;
var trans = new google.wspl.gears.Transaction(transactionId, dbMock);
dbMock.expects().doBegin(transactionId);
dbMock.expects().doExecute([stat1, stat2, stat3], callbackId, transactionId);
dbMock.expects().doCommit(transactionId);
trans.executeAll([stat1, stat2, stat3]);
trans.success('resultset1', callbackId);
trans.success('resultset2', callbackId);
trans.success('resultset3', callbackId);
}
function testExecuteAll_success() {
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 transactionId = 27;
var callbackId = 1;
var trans = new google.wspl.gears.Transaction(transactionId, dbMock);
dbMock.expects().doBegin(transactionId);
dbMock.expects().doExecute([stat1, stat2, stat3], callbackId, transactionId);
callbackMock.expect().onSuccess(trans, 'resultset1');
callbackMock.expect().onSuccess(trans, 'resultset2');
callbackMock.expect().onSuccess(trans, 'resultset3');
dbMock.expects().doCommit(transactionId);
trans.executeAll([stat1, stat2, stat3], callbackMock);
trans.success('resultset1', callbackId);
trans.success('resultset2', callbackId);
trans.success('resultset3', callbackId);
mockControl.verify();
}
function testExecuteAll_failure() {
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 stat4 = new google.wspl.Statement('stat4', [4, 5]);
var transactionId = 27;
var callbackId = 1;
var trans = new google.wspl.gears.Transaction(transactionId, dbMock);
dbMock.expects().doBegin(transactionId);
dbMock.expects().doExecute([stat1, stat2, stat3, stat4], callbackId,
transactionId);
callbackMock.expect().onSuccess(trans, 'resultset1');
callbackMock.expect().onFailure('sql error');
dbMock.expects().doRollback(transactionId);
trans.executeAll([stat1, stat2, stat3, stat4], callbackMock);
trans.success('resultset1', callbackId);
trans.failure('sql error', callbackId);
// These should do nothing.
trans.success('resultset3', callbackId);
trans.failure('sql error', callbackId);
mockControl.verify();
}
function testExecuteAll_multipleCalls() {
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 stat4 = new google.wspl.Statement('stat4', [4, 5]);
var transactionId = 27;
var callbackId_1 = 1;
var callbackId_2 = 2;
var callbackId_3 = 3;
var trans = new google.wspl.gears.Transaction(transactionId, dbMock);
dbMock.expects().doBegin(transactionId);
dbMock.expects().doExecute([stat1, stat2], callbackId_1, transactionId);
dbMock.expects().doExecute([stat3], callbackId_2, transactionId);
callbackMock.expects().onSuccess(trans, 'resultset1').andStub(function() {
trans.executeAll([stat4], callbackMock);
});
dbMock.expects().doExecute([stat4], callbackId_3, transactionId);
callbackMock.expects().onSuccess(trans, 'resultset2').andStub(function() {});
callbackMock.expects().onSuccess(trans, 'resultset3');
callbackMock.expects().onSuccess(trans, 'resultset4');
dbMock.expects().doCommit(transactionId);
trans.executeAll([stat1, stat2], callbackMock);
trans.executeAll([stat3], callbackMock);
trans.success('resultset1', callbackId_1);
trans.success('resultset2', callbackId_1);
trans.success('resultset3', callbackId_2);
trans.success('resultset4', callbackId_3);
mockControl.verify();
}
function testOnSuccess() {
var transactionId = 27;
var callbackId = 3;
var trans = new google.wspl.gears.Transaction(transactionId, dbMock);
callbackMock.expects().onSuccess(trans, 'result2');
callbackMock.expects().onSuccess(trans, 'result3');
dbMock.expects().doCommit(transactionId);
trans.numActiveExecutes_ = 1;
trans.activeExecutes_[callbackId] = {
statements: ['s1', 's2', 's3'],
currentStatement: 1,
callback: callbackMock
};
trans.success('result2', callbackId);
trans.success('result3', callbackId);
assertUndefined('activeExecute not removed',
trans.activeExecutes_[callbackId]);
mockControl.verify();
}
function testOnFailure() {
var transactionId = 27;
var callbackId = 5;
callbackMock.expects().onFailure(TypeOf.isA(Error)).andStub(function() {
assertEquals('error not returned', 'error', arguments[0].message);
});
dbMock.expects().doRollback(transactionId);
var trans = new google.wspl.gears.Transaction(transactionId, dbMock);
trans.numActiveExecutes_ = 1;
trans.activeExecutes_[callbackId] = {
statements: ['s1', 's2', 's3'],
currentStatement: 1,
callback: callbackMock
};
trans.failure(Error('error'), callbackId);
trans.success('result3', callbackId);
assertUndefined('activeExecute not removed',
trans.activeExecutes_[callbackId]);
mockControl.verify();
}
</script>
</body>
</html>

View File

@ -1,94 +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 Some simple utilities for supporting Gears usage.
*/
google.wspl.GearsUtils = google.wspl.GearsUtils || {};
/**
* Returns an array of hash objects, one per row in the result set,
* where the column names in the query are used as the members of
* the object.
*
* @param {GearsResultSet} rs the result set returned by execute.
* @return {Array.<Object>} An array containing hashes. Returns an empty
* array if there are no matching rows.
*/
google.wspl.GearsUtils.resultSetToObjectArray = function(rs) {
var rv = [];
if (rs) {
var cols = rs['fieldCount']();
var colNames = [];
for (var i = 0; i < cols; i++) {
colNames.push(rs['fieldName'](i));
}
while (rs['isValidRow']()) {
var h = {};
for (var i = 0; i < cols; i++) {
h[colNames[i]] = rs['field'](i);
}
rv.push(h);
rs['next']();
}
}
return rv;
};
/**
* Maximum file name length.
* @type {number}
* @private
*/
google.wspl.GearsUtils.MAX_FILE_NAME_LENGTH_ = 64;
/**
* Ensures that the given dbName is safe to use as a Gears database name.
* @type {!string} dbName
* @return {!string} The sanitized name.
* @private
*/
google.wspl.GearsUtils.makeSafeFileName_ = function(dbName) {
var sanitizedFileName = dbName.replace(/[^a-zA-Z0-9\.\-@_]/g, '');
if (sanitizedFileName.length <=
google.wspl.GearsUtils.MAX_FILE_NAME_LENGTH_) {
return sanitizedFileName;
} else {
return sanitizedFileName.substring(0,
google.wspl.GearsUtils.MAX_FILE_NAME_LENGTH_);
}
};
/**
* Opens a Gears Database using the provided userid and name.
* @param {string} userId The user to which the database belongs.
* @param {string} name The database's name.
* @param {GearsDatabase} db The Gears database to open.
* @param {function(string)} opt_logger A logger function for writing
* messages.
* @return {GearsDatabase} The open GearsDatabase object.
*/
google.wspl.GearsUtils.openDatabase = function(userId, name, db,
opt_logger) {
var dbId = userId + '-' + name;
var safeDbId = google.wspl.GearsUtils.makeSafeFileName_(dbId);
if (opt_logger && dbId != safeDbId) {
opt_logger('database name ' + dbId + '->' + safeDbId);
}
db.open(safeDbId);
return db;
};

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