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