Move all templates over to Github. Remove need for auto-loaded extensions in CLI
|
@ -94,4 +94,3 @@ require 'middleman-cli/extension'
|
||||||
require 'middleman-cli/server'
|
require 'middleman-cli/server'
|
||||||
require 'middleman-cli/build'
|
require 'middleman-cli/build'
|
||||||
require 'middleman-cli/console'
|
require 'middleman-cli/console'
|
||||||
require 'middleman-cli/git'
|
|
||||||
|
|
|
@ -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
|
|
|
@ -1,48 +1,70 @@
|
||||||
require 'middleman-templates'
|
|
||||||
|
|
||||||
# CLI Module
|
# CLI Module
|
||||||
module Middleman::Cli
|
module Middleman::Cli
|
||||||
# A thor task for creating new projects
|
# A thor task for creating new projects
|
||||||
class Init < Thor
|
class Init < Thor
|
||||||
|
include Thor::Actions
|
||||||
check_unknown_options!
|
check_unknown_options!
|
||||||
|
|
||||||
namespace :init
|
namespace :init
|
||||||
|
|
||||||
desc 'init NAME [options]', 'Create new project NAME'
|
desc 'init TARGET [options]', 'Create new project at TARGET'
|
||||||
available_templates = ::Middleman::Templates.registered.keys.join(', ')
|
|
||||||
method_option 'template',
|
method_option 'template',
|
||||||
aliases: '-T',
|
aliases: '-T',
|
||||||
default: 'default',
|
default: 'middleman/middleman-templates-default',
|
||||||
desc: "Use a project template: #{available_templates}"
|
desc: 'Use a project template'
|
||||||
method_option 'css_dir',
|
|
||||||
desc: 'The path to the css files'
|
# Do not run bundle install
|
||||||
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'
|
|
||||||
method_option 'skip-bundle',
|
method_option 'skip-bundle',
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
aliases: '-B',
|
aliases: '-B',
|
||||||
default: false,
|
default: false,
|
||||||
desc: "Don't run bundle install"
|
desc: 'Skip bundle install'
|
||||||
method_option 'skip-git',
|
|
||||||
type: :boolean,
|
|
||||||
default: false,
|
|
||||||
desc: 'Skip Git ignores and keeps'
|
|
||||||
# The init task
|
# The init task
|
||||||
# @param [String] name
|
# @param [String] name
|
||||||
def init(name='.')
|
def init(target='.')
|
||||||
key = options[:template].to_sym
|
require 'tmpdir'
|
||||||
unless ::Middleman::Templates.registered.key?(key)
|
|
||||||
raise Thor::Error, "Unknown project template '#{key}'"
|
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
|
end
|
||||||
|
|
||||||
thor_group = ::Middleman::Templates.registered[key]
|
Dir.mktmpdir do |dir|
|
||||||
thor_group.new([name], options).invoke_all
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
|
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 25 KiB |
|
@ -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 -->
|
|
|
@ -1 +0,0 @@
|
||||||
//= require_tree .
|
|
|
@ -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>
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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 site’s 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>
|
|
|
@ -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 -->
|
|
|
@ -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.
|
|
|
@ -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** — check if the issue has already been
|
|
||||||
reported.
|
|
||||||
|
|
||||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
|
||||||
latest `master` or development branch in the repository.
|
|
||||||
|
|
||||||
3. **Isolate the problem** — 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).
|
|
|
@ -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.
|
|
|
@ -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.
|
|
Before Width: | Height: | Size: 1.1 KiB |
|
@ -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>
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 766 B |
|
@ -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
|
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
title: HTML5 Boilerplate Middleman
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Add your site or application content here -->
|
|
||||||
<p>Hello world! This is HTML5 Boilerplate.</p>
|
|
|
@ -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.
|
|
|
@ -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>
|
|
|
@ -1,5 +0,0 @@
|
||||||
# www.robotstxt.org/
|
|
||||||
|
|
||||||
# Allow crawling of all content
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
|
@ -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)
|
|
|
@ -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)
|
|
|
@ -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>
|
|
|
@ -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)
|
|
|
@ -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 don’t understand all of the points described here, you probably
|
|
||||||
have a nasty security vulnerability. ~ simon willison
|
|
||||||
-->
|
|
||||||
|
|
||||||
</cross-domain-policy>
|
|
|
@ -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; }
|
|
||||||
}
|
|
|
@ -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+//-
|
|
||||||
:-:///////:- -:/://
|
|
||||||
-////:- --//:
|
|
||||||
-- -:
|
|
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 4.4 KiB |
|
@ -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>
|
|
|
@ -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));
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
# www.robotstxt.org/
|
|
||||||
# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
|
|
||||||
|
|
||||||
User-agent: *
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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.");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
<% string googleAnalyticsImageUrl = GoogleAnalyticsGetImageUrl(); %>
|
|
||||||
<img src="<%= googleAnalyticsImageUrl %>" />
|
|
|
@ -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(); %>
|
|
|
@ -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>
|
|
|
@ -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);
|
|
||||||
%>
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
%>
|
|
|
@ -1,2 +0,0 @@
|
||||||
<% String googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(request); %>
|
|
||||||
<img src="<%= googleAnalyticsImageUrl %>" />
|
|
|
@ -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>
|
|
|
@ -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();
|
|
||||||
?>
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?php
|
|
||||||
$googleAnalyticsImageUrl = googleAnalyticsGetImageUrl();
|
|
||||||
?>
|
|
||||||
<img src="<?= $googleAnalyticsImageUrl ?>" />
|
|
|
@ -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>
|
|
|
@ -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();
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
print '<img src="' . google_analytics_get_image_url() . '" />';
|
|
|
@ -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);
|
|
|
@ -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.
|
|
|
@ -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;
|
|
||||||
};
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -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);
|
|
Before Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 206 B |
|
@ -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
|
|
Before Width: | Height: | Size: 3.7 KiB |
|
@ -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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
|
|
@ -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_);
|
|
||||||
};
|
|
|
@ -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>
|
|
|
@ -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();
|
|
|
@ -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_);
|
|
||||||
};
|
|
|
@ -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>
|
|
|
@ -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_);
|
|
||||||
};
|
|
|
@ -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>
|
|
|
@ -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');
|
|
||||||
};
|
|
|
@ -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>
|
|
|
@ -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_];
|
|
||||||
};
|
|
|
@ -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>
|
|
|
@ -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;
|
|
||||||
};
|
|
|
@ -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>
|
|
|
@ -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;
|
|
||||||
};
|
|