monocle-rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/monocle/rails.rb +8 -0
- data/lib/monocle/rails/version.rb +5 -0
- data/monocle-rails.gemspec +23 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/compat/browser.js +120 -0
- data/vendor/assets/javascripts/compat/css.js +145 -0
- data/vendor/assets/javascripts/compat/env.js +463 -0
- data/vendor/assets/javascripts/compat/gala.js +469 -0
- data/vendor/assets/javascripts/compat/stubs.js +50 -0
- data/vendor/assets/javascripts/controls/contents.js +59 -0
- data/vendor/assets/javascripts/controls/magnifier.js +51 -0
- data/vendor/assets/javascripts/controls/panel.js +136 -0
- data/vendor/assets/javascripts/controls/placesaver.js +100 -0
- data/vendor/assets/javascripts/controls/scrubber.js +140 -0
- data/vendor/assets/javascripts/controls/spinner.js +99 -0
- data/vendor/assets/javascripts/controls/stencil.js +410 -0
- data/vendor/assets/javascripts/core/billboard.js +120 -0
- data/vendor/assets/javascripts/core/book.js +467 -0
- data/vendor/assets/javascripts/core/bookdata.js +59 -0
- data/vendor/assets/javascripts/core/component.js +413 -0
- data/vendor/assets/javascripts/core/events.js +56 -0
- data/vendor/assets/javascripts/core/factory.js +194 -0
- data/vendor/assets/javascripts/core/formatting.js +317 -0
- data/vendor/assets/javascripts/core/monocle.js +16 -0
- data/vendor/assets/javascripts/core/place.js +210 -0
- data/vendor/assets/javascripts/core/reader.js +683 -0
- data/vendor/assets/javascripts/core/selection.js +158 -0
- data/vendor/assets/javascripts/core/styles.js +155 -0
- data/vendor/assets/javascripts/dimensions/columns.js +218 -0
- data/vendor/assets/javascripts/flippers/instant.js +78 -0
- data/vendor/assets/javascripts/flippers/scroller.js +128 -0
- data/vendor/assets/javascripts/flippers/slider.js +469 -0
- data/vendor/assets/javascripts/monocore.js +27 -0
- data/vendor/assets/javascripts/monoctrl.js +1 -0
- data/vendor/assets/javascripts/panels/eink.js +61 -0
- data/vendor/assets/javascripts/panels/imode.js +180 -0
- data/vendor/assets/javascripts/panels/magic.js +297 -0
- data/vendor/assets/javascripts/panels/marginal.js +50 -0
- data/vendor/assets/javascripts/panels/twopane.js +34 -0
- data/vendor/assets/stylesheets/monocore.css +194 -0
- data/vendor/assets/stylesheets/monoctrl.css +168 -0
- metadata +129 -0
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Monocle::Rails
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'monocle-rails'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install monocle-rails
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'monocle/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "monocle-rails"
|
8
|
+
spec.version = Monocle::Rails::VERSION
|
9
|
+
spec.authors = ["Klaus Göttling"]
|
10
|
+
spec.email = ["klaus.goettling@hs-augsburg.de"]
|
11
|
+
spec.description = %q{A silky, tactile browser-based ebook reader. Initial development by Joseph Pearson of Inventive Labs. Released under the MIT license. More information (including demos): http://monocle.inventivelabs.com.au}
|
12
|
+
spec.summary = %q{Monocle from Joseph Pearson, bundled by Klaus Göttling}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/vendor/.DS_Store
ADDED
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,120 @@
|
|
1
|
+
Monocle.Browser = {};
|
2
|
+
|
3
|
+
// Compare the user-agent string to a string or regex pattern.
|
4
|
+
//
|
5
|
+
Monocle.Browser.uaMatch = function (test) {
|
6
|
+
var ua = navigator.userAgent;
|
7
|
+
if (typeof test == "string") { return ua.indexOf(test) >= 0; }
|
8
|
+
return !!ua.match(test);
|
9
|
+
}
|
10
|
+
|
11
|
+
|
12
|
+
// Detect the browser engine and set boolean flags for reference.
|
13
|
+
//
|
14
|
+
Monocle.Browser.is = {
|
15
|
+
IE: !!(window.attachEvent && !Monocle.Browser.uaMatch('Opera')),
|
16
|
+
Opera: Monocle.Browser.uaMatch('Opera'),
|
17
|
+
WebKit: Monocle.Browser.uaMatch(/Apple\s?WebKit/),
|
18
|
+
Gecko: Monocle.Browser.uaMatch('Gecko') && !Monocle.Browser.uaMatch('KHTML'),
|
19
|
+
MobileSafari: Monocle.Browser.uaMatch(/OS \d_.*AppleWebKit.*Mobile/)
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
// Set the browser engine string.
|
24
|
+
//
|
25
|
+
if (Monocle.Browser.is.IE) {
|
26
|
+
Monocle.Browser.engine = "IE";
|
27
|
+
} else if (Monocle.Browser.is.Opera) {
|
28
|
+
Monocle.Browser.engine = "Opera";
|
29
|
+
} else if (Monocle.Browser.is.WebKit) {
|
30
|
+
Monocle.Browser.engine = "WebKit";
|
31
|
+
} else if (Monocle.Browser.is.Gecko) {
|
32
|
+
Monocle.Browser.engine = "Gecko";
|
33
|
+
} else {
|
34
|
+
console.warn("Unknown engine; assuming W3C compliant.");
|
35
|
+
Monocle.Browser.engine = "W3C";
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
// Detect the client platform (typically device/operating system).
|
40
|
+
//
|
41
|
+
Monocle.Browser.on = {
|
42
|
+
iPhone: Monocle.Browser.is.MobileSafari && screen.width == 320,
|
43
|
+
iPad: Monocle.Browser.is.MobileSafari && screen.width == 768,
|
44
|
+
UIWebView: (
|
45
|
+
Monocle.Browser.is.MobileSafari &&
|
46
|
+
!Monocle.Browser.uaMatch('Safari') &&
|
47
|
+
!navigator.standalone
|
48
|
+
),
|
49
|
+
BlackBerry: Monocle.Browser.uaMatch('BlackBerry'),
|
50
|
+
Android: (
|
51
|
+
Monocle.Browser.uaMatch('Android') ||
|
52
|
+
Monocle.Browser.uaMatch('Silk') ||
|
53
|
+
Monocle.Browser.uaMatch(/Linux;.*EBRD/) // Sony Readers
|
54
|
+
),
|
55
|
+
MacOSX: (
|
56
|
+
Monocle.Browser.uaMatch('Mac OS X') &&
|
57
|
+
!Monocle.Browser.is.MobileSafari
|
58
|
+
),
|
59
|
+
Kindle3: Monocle.Browser.uaMatch(/Kindle\/3/)
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
// It is only because MobileSafari is responsible for so much anguish that
|
64
|
+
// we special-case it here. Not a badge of honour.
|
65
|
+
//
|
66
|
+
if (Monocle.Browser.is.MobileSafari) {
|
67
|
+
(function () {
|
68
|
+
var ver = navigator.userAgent.match(/ OS ([\d_]+)/);
|
69
|
+
if (ver) {
|
70
|
+
Monocle.Browser.iOSVersion = ver[1].replace(/_/g, '.');
|
71
|
+
} else {
|
72
|
+
console.warn("Unknown MobileSafari user agent: "+navigator.userAgent);
|
73
|
+
}
|
74
|
+
})();
|
75
|
+
}
|
76
|
+
Monocle.Browser.iOSVersionBelow = function (strOrNum) {
|
77
|
+
return !!Monocle.Browser.iOSVersion && Monocle.Browser.iOSVersion < strOrNum;
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
// Some browser environments are too slow or too problematic for
|
82
|
+
// special animation effects.
|
83
|
+
//
|
84
|
+
// FIXME: These tests are too opinionated. Replace with more targeted tests.
|
85
|
+
//
|
86
|
+
Monocle.Browser.renders = (function () {
|
87
|
+
var ua = navigator.userAgent;
|
88
|
+
var caps = {};
|
89
|
+
caps.eInk = Monocle.Browser.on.Kindle3;
|
90
|
+
caps.slow = (
|
91
|
+
caps.eInk ||
|
92
|
+
(Monocle.Browser.on.Android && !ua.match(/Chrome/)) ||
|
93
|
+
Monocle.Browser.on.Blackberry ||
|
94
|
+
ua.match(/NintendoBrowser/)
|
95
|
+
);
|
96
|
+
return caps;
|
97
|
+
})();
|
98
|
+
|
99
|
+
|
100
|
+
// A helper class for sniffing CSS features and creating CSS rules
|
101
|
+
// appropriate to the current rendering engine.
|
102
|
+
//
|
103
|
+
Monocle.Browser.css = new Monocle.CSS();
|
104
|
+
|
105
|
+
|
106
|
+
// During Reader initialization, this method is called to create the
|
107
|
+
// "environment", which tests for the existence of various browser
|
108
|
+
// features and bugs, then invokes the callback to continue initialization.
|
109
|
+
//
|
110
|
+
// If the survey has already been conducted and the env exists, calls
|
111
|
+
// callback immediately.
|
112
|
+
//
|
113
|
+
Monocle.Browser.survey = function (callback) {
|
114
|
+
if (!Monocle.Browser.env) {
|
115
|
+
Monocle.Browser.env = new Monocle.Env();
|
116
|
+
Monocle.Browser.env.survey(callback);
|
117
|
+
} else if (typeof callback == "function") {
|
118
|
+
callback();
|
119
|
+
}
|
120
|
+
}
|
@@ -0,0 +1,145 @@
|
|
1
|
+
// A class for manipulating CSS properties in a browser-engine-aware way.
|
2
|
+
//
|
3
|
+
Monocle.CSS = function () {
|
4
|
+
|
5
|
+
var API = { constructor: Monocle.CSS }
|
6
|
+
var k = API.constants = API.constructor;
|
7
|
+
var p = API.properties = {
|
8
|
+
guineapig: document.createElement('div')
|
9
|
+
}
|
10
|
+
|
11
|
+
|
12
|
+
// Returns engine-specific properties,
|
13
|
+
//
|
14
|
+
// eg:
|
15
|
+
//
|
16
|
+
// toCSSProps('transform')
|
17
|
+
//
|
18
|
+
// ... in WebKit, this will return:
|
19
|
+
//
|
20
|
+
// ['transform', '-webkit-transform']
|
21
|
+
//
|
22
|
+
function toCSSProps(prop) {
|
23
|
+
var props = [prop];
|
24
|
+
var eng = k.engines.indexOf(Monocle.Browser.engine);
|
25
|
+
if (eng) {
|
26
|
+
var pf = k.prefixes[eng];
|
27
|
+
if (pf) {
|
28
|
+
props.push(pf+prop);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
return props;
|
32
|
+
}
|
33
|
+
|
34
|
+
|
35
|
+
// Returns an engine-specific CSS string.
|
36
|
+
//
|
37
|
+
// eg:
|
38
|
+
//
|
39
|
+
// toCSSDeclaration('column-width', '300px')
|
40
|
+
//
|
41
|
+
// ... in Mozilla, this will return:
|
42
|
+
//
|
43
|
+
// "column-width: 300px; -moz-column-width: 300px;"
|
44
|
+
//
|
45
|
+
function toCSSDeclaration(prop, val) {
|
46
|
+
var props = toCSSProps(prop);
|
47
|
+
for (var i = 0, ii = props.length; i < ii; ++i) {
|
48
|
+
props[i] += ": "+val+";";
|
49
|
+
}
|
50
|
+
return props.join("");
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
// Returns an array of DOM properties specific to this engine.
|
55
|
+
//
|
56
|
+
// eg:
|
57
|
+
//
|
58
|
+
// toDOMProps('column-width')
|
59
|
+
//
|
60
|
+
// ... in Opera, this will return:
|
61
|
+
//
|
62
|
+
// [columnWidth, OColumnWidth]
|
63
|
+
//
|
64
|
+
function toDOMProps(prop) {
|
65
|
+
var parts = prop.split('-');
|
66
|
+
for (var i = parts.length; i > 0; --i) {
|
67
|
+
parts[i] = capStr(parts[i]);
|
68
|
+
}
|
69
|
+
|
70
|
+
var props = [parts.join('')];
|
71
|
+
var eng = k.engines.indexOf(Monocle.Browser.engine);
|
72
|
+
if (eng) {
|
73
|
+
var pf = k.domprefixes[eng];
|
74
|
+
if (pf) {
|
75
|
+
parts[0] = capStr(parts[0]);
|
76
|
+
props.push(pf+parts.join(''));
|
77
|
+
}
|
78
|
+
}
|
79
|
+
return props;
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
// Is this exact property (or any in this array of properties) supported
|
84
|
+
// by this engine?
|
85
|
+
//
|
86
|
+
function supportsProperty(props) {
|
87
|
+
for (var i in props) {
|
88
|
+
if (p.guineapig.style[props[i]] !== undefined) { return true; }
|
89
|
+
}
|
90
|
+
return false;
|
91
|
+
} // Thanks modernizr!
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
// Is this property (or a prefixed variant) supported by this engine?
|
96
|
+
//
|
97
|
+
function supportsPropertyWithAnyPrefix(prop) {
|
98
|
+
return supportsProperty(toDOMProps(prop));
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
function supportsMediaQuery(query) {
|
103
|
+
var gpid = "monocle_guineapig";
|
104
|
+
p.guineapig.id = gpid;
|
105
|
+
var st = document.createElement('style');
|
106
|
+
st.textContent = query+'{#'+gpid+'{height:3px}}';
|
107
|
+
(document.head || document.getElementsByTagName('head')[0]).appendChild(st);
|
108
|
+
document.documentElement.appendChild(p.guineapig);
|
109
|
+
|
110
|
+
var result = p.guineapig.offsetHeight === 3;
|
111
|
+
|
112
|
+
st.parentNode.removeChild(st);
|
113
|
+
p.guineapig.parentNode.removeChild(p.guineapig);
|
114
|
+
|
115
|
+
return result;
|
116
|
+
} // Thanks modernizr!
|
117
|
+
|
118
|
+
|
119
|
+
function supportsMediaQueryProperty(prop) {
|
120
|
+
return supportsMediaQuery(
|
121
|
+
'@media (' + k.prefixes.join(prop+'),(') + 'monocle__)'
|
122
|
+
);
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
function capStr(wd) {
|
127
|
+
return wd ? wd.charAt(0).toUpperCase() + wd.substr(1) : "";
|
128
|
+
}
|
129
|
+
|
130
|
+
|
131
|
+
API.toCSSProps = toCSSProps;
|
132
|
+
API.toCSSDeclaration = toCSSDeclaration;
|
133
|
+
API.toDOMProps = toDOMProps;
|
134
|
+
API.supportsProperty = supportsProperty;
|
135
|
+
API.supportsPropertyWithAnyPrefix = supportsPropertyWithAnyPrefix;
|
136
|
+
API.supportsMediaQuery = supportsMediaQuery;
|
137
|
+
API.supportsMediaQueryProperty = supportsMediaQueryProperty;
|
138
|
+
|
139
|
+
return API;
|
140
|
+
}
|
141
|
+
|
142
|
+
|
143
|
+
Monocle.CSS.engines = ["W3C", "WebKit", "Gecko", "Opera", "IE", "Konqueror"];
|
144
|
+
Monocle.CSS.prefixes = ["", "-webkit-", "-moz-", "-o-", "-ms-", "-khtml-"];
|
145
|
+
Monocle.CSS.domprefixes = ["", "Webkit", "Moz", "O", "ms", "Khtml"];
|
@@ -0,0 +1,463 @@
|
|
1
|
+
// A class that tests the browser environment for required capabilities and
|
2
|
+
// known bugs (for which we have workarounds).
|
3
|
+
//
|
4
|
+
Monocle.Env = function () {
|
5
|
+
|
6
|
+
var API = { constructor: Monocle.Env }
|
7
|
+
var k = API.constants = API.constructor;
|
8
|
+
var p = API.properties = {
|
9
|
+
// Assign to a function before running survey in order to get
|
10
|
+
// results as they come in. The function should take two arguments:
|
11
|
+
// testName and value.
|
12
|
+
resultCallback: null
|
13
|
+
}
|
14
|
+
|
15
|
+
// These are private variables so they don't clutter up properties.
|
16
|
+
var css = Monocle.Browser.css;
|
17
|
+
var activeTestName = null;
|
18
|
+
var frameLoadCallback = null;
|
19
|
+
var testFrame = null;
|
20
|
+
var testFrameCntr = null;
|
21
|
+
var testFrameSize = 100;
|
22
|
+
var surveyCallback = null;
|
23
|
+
|
24
|
+
|
25
|
+
function survey(cb) {
|
26
|
+
surveyCallback = cb;
|
27
|
+
runNextTest();
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
function runNextTest() {
|
32
|
+
var test = envTests.shift();
|
33
|
+
if (!test) { return completed(); }
|
34
|
+
activeTestName = test[0];
|
35
|
+
try { test[1](); } catch (e) { result(e); }
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
// Each test should call this to say "I'm finished, run the next test."
|
40
|
+
//
|
41
|
+
function result(val) {
|
42
|
+
API[activeTestName] = val;
|
43
|
+
if (p.resultCallback) { p.resultCallback(activeTestName, val); }
|
44
|
+
runNextTest();
|
45
|
+
return val;
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
// Invoked after all tests have run.
|
50
|
+
//
|
51
|
+
function completed() {
|
52
|
+
// Remove the test frame after a slight delay (otherwise Gecko spins).
|
53
|
+
Monocle.defer(removeTestFrame);
|
54
|
+
|
55
|
+
if (typeof surveyCallback == "function") {
|
56
|
+
var fn = surveyCallback;
|
57
|
+
surveyCallback = null;
|
58
|
+
fn(API);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
// A bit of sugar for simplifying a detection pattern: does this
|
64
|
+
// function exist?
|
65
|
+
//
|
66
|
+
// Pass a string snippet of JavaScript to be evaluated.
|
67
|
+
//
|
68
|
+
function testForFunction(str) {
|
69
|
+
return function () { result(typeof eval(str) == "function"); }
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
// A bit of sugar to indicate that the detection function for this test
|
74
|
+
// hasn't been written yet...
|
75
|
+
//
|
76
|
+
// Pass the value you want assigned for the test until it is implemented.
|
77
|
+
//
|
78
|
+
function testNotYetImplemented(rslt) {
|
79
|
+
return function () { result(rslt); }
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
// Loads (or reloads) a hidden iframe so that we can test browser features.
|
84
|
+
//
|
85
|
+
// cb is the callback that is fired when the test frame's content is loaded.
|
86
|
+
//
|
87
|
+
// src is optional, in which case it defaults to 4. If provided, it can be
|
88
|
+
// a number (specifying the number of pages of default content), or a string,
|
89
|
+
// which will be loaded as a URL.
|
90
|
+
//
|
91
|
+
function loadTestFrame(cb, src) {
|
92
|
+
if (!testFrame) { testFrame = createTestFrame(); }
|
93
|
+
frameLoadCallback = cb;
|
94
|
+
|
95
|
+
src = src || 4;
|
96
|
+
|
97
|
+
if (typeof src == "number") {
|
98
|
+
var pgs = [];
|
99
|
+
for (var i = 1, ii = src; i <= ii; ++i) {
|
100
|
+
pgs.push("<div>Page "+i+"</div>");
|
101
|
+
}
|
102
|
+
var divStyle = [
|
103
|
+
"display:inline-block",
|
104
|
+
"line-height:"+testFrameSize+"px",
|
105
|
+
"width:"+testFrameSize+"px"
|
106
|
+
].join(";");
|
107
|
+
src = "javascript:'<!DOCTYPE html><html>"+
|
108
|
+
'<head><meta name="time" content="'+(new Date()).getTime()+'" />'+
|
109
|
+
'<style>div{'+divStyle+'}</style></head>'+
|
110
|
+
'<body>'+pgs.join("")+'</body>'+
|
111
|
+
"</html>'";
|
112
|
+
}
|
113
|
+
|
114
|
+
testFrame.src = src;
|
115
|
+
}
|
116
|
+
|
117
|
+
|
118
|
+
// Creates the hidden test frame and returns it.
|
119
|
+
//
|
120
|
+
function createTestFrame() {
|
121
|
+
testFrameCntr = document.createElement('div');
|
122
|
+
testFrameCntr.style.cssText = [
|
123
|
+
"width:"+testFrameSize+"px",
|
124
|
+
"height:"+testFrameSize+"px",
|
125
|
+
"overflow:hidden",
|
126
|
+
"position:absolute",
|
127
|
+
"visibility:hidden"
|
128
|
+
].join(";");
|
129
|
+
document.body.appendChild(testFrameCntr);
|
130
|
+
|
131
|
+
var fr = document.createElement('iframe');
|
132
|
+
testFrameCntr.appendChild(fr);
|
133
|
+
fr.setAttribute("scrolling", "no");
|
134
|
+
fr.style.cssText = [
|
135
|
+
"width:100%",
|
136
|
+
"height:100%",
|
137
|
+
"border:none",
|
138
|
+
"background:#900"
|
139
|
+
].join(";");
|
140
|
+
fr.addEventListener(
|
141
|
+
"load",
|
142
|
+
function () {
|
143
|
+
if (!fr.contentDocument || !fr.contentDocument.body) { return; }
|
144
|
+
var bd = fr.contentDocument.body;
|
145
|
+
bd.style.cssText = ([
|
146
|
+
"margin:0",
|
147
|
+
"padding:0",
|
148
|
+
"position:absolute",
|
149
|
+
"height:100%",
|
150
|
+
"width:100%",
|
151
|
+
"-webkit-column-width:"+testFrameSize+"px",
|
152
|
+
"-webkit-column-gap:0",
|
153
|
+
"-webkit-column-fill:auto",
|
154
|
+
"-moz-column-width:"+testFrameSize+"px",
|
155
|
+
"-moz-column-gap:0",
|
156
|
+
"-moz-column-fill:auto",
|
157
|
+
"-o-column-width:"+testFrameSize+"px",
|
158
|
+
"-o-column-gap:0",
|
159
|
+
"-o-column-fill:auto",
|
160
|
+
"column-width:"+testFrameSize+"px",
|
161
|
+
"column-gap:0",
|
162
|
+
"column-fill:auto"
|
163
|
+
].join(";"));
|
164
|
+
if (bd.scrollHeight > testFrameSize) {
|
165
|
+
bd.style.cssText += ["min-width:200%", "overflow:hidden"].join(";");
|
166
|
+
if (bd.scrollHeight <= testFrameSize) {
|
167
|
+
bd.className = "column-force";
|
168
|
+
} else {
|
169
|
+
bd.className = "column-failed "+bd.scrollHeight;
|
170
|
+
}
|
171
|
+
}
|
172
|
+
frameLoadCallback(fr);
|
173
|
+
},
|
174
|
+
false
|
175
|
+
);
|
176
|
+
return fr;
|
177
|
+
}
|
178
|
+
|
179
|
+
|
180
|
+
function removeTestFrame() {
|
181
|
+
if (testFrameCntr && testFrameCntr.parentNode) {
|
182
|
+
testFrameCntr.parentNode.removeChild(testFrameCntr);
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
|
187
|
+
function columnedWidth(fr) {
|
188
|
+
var bd = fr.contentDocument.body;
|
189
|
+
var de = fr.contentDocument.documentElement;
|
190
|
+
return Math.max(bd.scrollWidth, de.scrollWidth);
|
191
|
+
}
|
192
|
+
|
193
|
+
|
194
|
+
var envTests = [
|
195
|
+
|
196
|
+
// TEST FOR REQUIRED CAPABILITIES
|
197
|
+
|
198
|
+
["supportsW3CEvents", testForFunction("window.addEventListener")],
|
199
|
+
["supportsCustomEvents", testForFunction("document.createEvent")],
|
200
|
+
["supportsColumns", function () {
|
201
|
+
result(css.supportsPropertyWithAnyPrefix('column-width'));
|
202
|
+
}],
|
203
|
+
["supportsTransform", function () {
|
204
|
+
result(css.supportsProperty([
|
205
|
+
'transformProperty',
|
206
|
+
'WebkitTransform',
|
207
|
+
'MozTransform',
|
208
|
+
'OTransform',
|
209
|
+
'msTransform'
|
210
|
+
]));
|
211
|
+
}],
|
212
|
+
|
213
|
+
|
214
|
+
// TEST FOR OPTIONAL CAPABILITIES
|
215
|
+
|
216
|
+
// Does it do CSS transitions?
|
217
|
+
["supportsTransition", function () {
|
218
|
+
result(css.supportsPropertyWithAnyPrefix('transition'))
|
219
|
+
}],
|
220
|
+
|
221
|
+
// Can we find nodes in a document with an XPath?
|
222
|
+
//
|
223
|
+
["supportsXPath", testForFunction("document.evaluate")],
|
224
|
+
|
225
|
+
// Can we find nodes in a document natively with a CSS selector?
|
226
|
+
//
|
227
|
+
["supportsQuerySelector", testForFunction("document.querySelector")],
|
228
|
+
|
229
|
+
// Can we do 3d transforms?
|
230
|
+
//
|
231
|
+
["supportsTransform3d", function () {
|
232
|
+
result(
|
233
|
+
css.supportsMediaQueryProperty('transform-3d') &&
|
234
|
+
css.supportsProperty([
|
235
|
+
'perspectiveProperty',
|
236
|
+
'WebkitPerspective',
|
237
|
+
'MozPerspective',
|
238
|
+
'OPerspective',
|
239
|
+
'msPerspective'
|
240
|
+
]) &&
|
241
|
+
!Monocle.Browser.renders.slow // Some older browsers can't be trusted.
|
242
|
+
);
|
243
|
+
}],
|
244
|
+
|
245
|
+
|
246
|
+
// Commonly-used browser functionality
|
247
|
+
["supportsOfflineCache", function () {
|
248
|
+
result(typeof window.applicationCache != 'undefined');
|
249
|
+
}],
|
250
|
+
|
251
|
+
["supportsLocalStorage", function () {
|
252
|
+
// NB: Some duplicitous early Android browsers claim to have
|
253
|
+
// localStorage, but calls to getItem() fail.
|
254
|
+
result(
|
255
|
+
typeof window.localStorage != "undefined" &&
|
256
|
+
typeof window.localStorage.getItem == "function"
|
257
|
+
)
|
258
|
+
}],
|
259
|
+
|
260
|
+
|
261
|
+
// CHECK OUT OUR CONTEXT
|
262
|
+
|
263
|
+
// Does the device have a MobileSafari-style touch interface?
|
264
|
+
// (Here we can now simply follow the wisdom of Gala.)
|
265
|
+
//
|
266
|
+
["touch", function () { result(Gala.Pointers.ENV.noMouse); }],
|
267
|
+
|
268
|
+
|
269
|
+
// Is the Reader embedded, or in the top-level window?
|
270
|
+
//
|
271
|
+
["embedded", function () { result(top != window.self) }],
|
272
|
+
|
273
|
+
|
274
|
+
// TEST FOR CERTAIN RENDERING OR INTERACTION BUGS
|
275
|
+
|
276
|
+
// iOS (at least up to version 4.1) makes a complete hash of touch events
|
277
|
+
// when an iframe is overlapped by other elements. It's a dog's breakfast.
|
278
|
+
// See test/bugs/ios-frame-touch-bug for details.
|
279
|
+
//
|
280
|
+
["brokenIframeTouchModel", function () {
|
281
|
+
result(Monocle.Browser.iOSVersionBelow("4.2"));
|
282
|
+
}],
|
283
|
+
|
284
|
+
// Webkit-based browsers put floated elements in the wrong spot when
|
285
|
+
// columns are used -- they appear way down where they would be if there
|
286
|
+
// were no columns. Presumably the float positions are calculated before
|
287
|
+
// the columns. A bug has been lodged, and it's fixed in recent WebKits.
|
288
|
+
//
|
289
|
+
["floatsIgnoreColumns", function () {
|
290
|
+
if (!Monocle.Browser.is.WebKit) { return result(false); }
|
291
|
+
var match = navigator.userAgent.match(/AppleWebKit\/([\d\.]+)/);
|
292
|
+
if (!match) { return result(false); }
|
293
|
+
return result(match[1] < "534.30");
|
294
|
+
}],
|
295
|
+
|
296
|
+
// The latest engines all agree that if a body is translated leftwards,
|
297
|
+
// its scrollWidth is shortened. But some older WebKits (notably iOS4)
|
298
|
+
// do not subtract translateX values from scrollWidth. In this case,
|
299
|
+
// we should not add the translate back when calculating the width.
|
300
|
+
//
|
301
|
+
["widthsIgnoreTranslate", function () {
|
302
|
+
loadTestFrame(function (fr) {
|
303
|
+
var firstWidth = columnedWidth(fr);
|
304
|
+
var s = fr.contentDocument.body.style;
|
305
|
+
var props = css.toDOMProps("transform");
|
306
|
+
for (var i = 0, ii = props.length; i < ii; ++i) {
|
307
|
+
s[props[i]] = "translateX(-600px)";
|
308
|
+
}
|
309
|
+
var secondWidth = columnedWidth(fr);
|
310
|
+
for (i = 0, ii = props.length; i < ii; ++i) {
|
311
|
+
s[props[i]] = "none";
|
312
|
+
}
|
313
|
+
result(secondWidth == firstWidth);
|
314
|
+
});
|
315
|
+
}],
|
316
|
+
|
317
|
+
// On Android browsers, if the component iframe has a relative width (ie,
|
318
|
+
// 100%), the width of the entire browser will keep expanding and expanding
|
319
|
+
// to fit the width of the body of the iframe (which, with columns, is
|
320
|
+
// massive). So, 100% is treated as "of the body content" rather than "of
|
321
|
+
// the parent dimensions". In this scenario, we need to give the component
|
322
|
+
// iframe a fixed width in pixels.
|
323
|
+
//
|
324
|
+
// In iOS, the frame is clipped by overflow:hidden, so this doesn't seem to
|
325
|
+
// be a problem.
|
326
|
+
//
|
327
|
+
["relativeIframeExpands", function () {
|
328
|
+
result(navigator.userAgent.indexOf("Android 2") >= 0);
|
329
|
+
}],
|
330
|
+
|
331
|
+
// iOS3 will pause JavaScript execution if it gets a style-change + a
|
332
|
+
// scroll change on a component body. Weirdly, this seems to break GBCR
|
333
|
+
// in iOS4.
|
334
|
+
//
|
335
|
+
["scrollToApplyStyle", function () {
|
336
|
+
result(Monocle.Browser.iOSVersionBelow("4"));
|
337
|
+
}],
|
338
|
+
|
339
|
+
|
340
|
+
// TEST FOR OTHER QUIRKY BROWSER BEHAVIOUR
|
341
|
+
|
342
|
+
// Older versions of WebKit (iOS3, Kindle3) need a min-width set on the
|
343
|
+
// body of the iframe at 200%. This forces columns. But when this
|
344
|
+
// min-width is set, it's more difficult to recognise 1 page components,
|
345
|
+
// so we generally don't want to force it unless we have to.
|
346
|
+
//
|
347
|
+
["forceColumns", function () {
|
348
|
+
loadTestFrame(function (fr) {
|
349
|
+
var bd = fr.contentDocument.body;
|
350
|
+
result(bd.className ? true : false);
|
351
|
+
});
|
352
|
+
}],
|
353
|
+
|
354
|
+
// A component iframe's body is absolutely positioned. This means that
|
355
|
+
// the documentElement should have a height of 0, since it contains nothing
|
356
|
+
// other than an absolutely positioned element.
|
357
|
+
//
|
358
|
+
// But for some browsers (Gecko and Opera), the documentElement is as
|
359
|
+
// wide as the full columned content, and the body is only as wide as
|
360
|
+
// the iframe element (ie, the first column).
|
361
|
+
//
|
362
|
+
// It gets weirder. Gecko sometimes behaves like WebKit (not clipping the
|
363
|
+
// body) IF the component has been loaded via HTML/JS/Nodes, not URL. Still
|
364
|
+
// can't reproduce outside Monocle.
|
365
|
+
//
|
366
|
+
// FIXME: If we can figure out a reliable behaviour for Gecko, we should
|
367
|
+
// use it to precalculate the workaround. At the moment, this test isn't
|
368
|
+
// used, but it belongs in src/dimensions/columns.js#columnedDimensions().
|
369
|
+
//
|
370
|
+
// ["iframeBodyWidthClipped", function () {
|
371
|
+
// loadTestFrame(function (fr) {
|
372
|
+
// var doc = fr.contentDocument;
|
373
|
+
// result(
|
374
|
+
// doc.body.scrollWidth <= testFrameSize &&
|
375
|
+
// doc.documentElement.scrollWidth > testFrameSize
|
376
|
+
// );
|
377
|
+
// })
|
378
|
+
// }],
|
379
|
+
|
380
|
+
// Finding the page that a given HTML node is on is typically done by
|
381
|
+
// calculating the offset of its rectange from the body's rectangle.
|
382
|
+
//
|
383
|
+
// But if this information isn't provided by the browser, we need to use
|
384
|
+
// node.scrollIntoView and check the scrollOffset. Basically iOS3 is the
|
385
|
+
// only target platform that doesn't give us the rectangle info.
|
386
|
+
//
|
387
|
+
["findNodesByScrolling", function () {
|
388
|
+
result(typeof document.body.getBoundingClientRect !== "function");
|
389
|
+
}],
|
390
|
+
|
391
|
+
// In MobileSafari browsers, iframes are rendered at the width and height
|
392
|
+
// of their content, rather than having scrollbars. So in that case, it's
|
393
|
+
// the containing element (the "sheaf") that must be scrolled, not the
|
394
|
+
// iframe body.
|
395
|
+
//
|
396
|
+
["sheafIsScroller", function () {
|
397
|
+
loadTestFrame(function (fr) {
|
398
|
+
result(fr.parentNode.scrollWidth > testFrameSize);
|
399
|
+
});
|
400
|
+
}],
|
401
|
+
|
402
|
+
// For some reason, iOS MobileSafari sometimes loses track of a page after
|
403
|
+
// slideOut -- it thinks it has an x-translation of 0, rather than -768 or
|
404
|
+
// whatever. So the page gets "stuck" there, until it is given a non-zero
|
405
|
+
// x-translation. The workaround is to set a non-zero duration on the jumpIn,
|
406
|
+
// which seems to force WebKit to recalculate the x of the page. Weird, yeah.
|
407
|
+
//
|
408
|
+
["stickySlideOut", function () {
|
409
|
+
result(Monocle.Browser.is.MobileSafari);
|
410
|
+
}],
|
411
|
+
|
412
|
+
|
413
|
+
// Chrome and Firefox incorrectly clip text when the dimensions of
|
414
|
+
// a page are not an integer. IE10 clips text when the page dimensions
|
415
|
+
// are rounded.
|
416
|
+
//
|
417
|
+
['roundPageDimensions', function () {
|
418
|
+
result(!Monocle.Browser.is.IE);
|
419
|
+
}],
|
420
|
+
|
421
|
+
|
422
|
+
|
423
|
+
// In IE10, the <html> element of the iframe's document has scrollbars,
|
424
|
+
// unless you set its style.overflow to 'hidden'.
|
425
|
+
//
|
426
|
+
['documentElementHasScrollbars', function () {
|
427
|
+
result(Monocle.Browser.is.IE);
|
428
|
+
}],
|
429
|
+
|
430
|
+
|
431
|
+
// Older versions of iOS (<6) would render blank pages if they were
|
432
|
+
// off the screen when their layout/position was updated.
|
433
|
+
//
|
434
|
+
['offscreenRenderingClipped', function () {
|
435
|
+
result(Monocle.Browser.iOSVersionBelow('6'));
|
436
|
+
}],
|
437
|
+
|
438
|
+
|
439
|
+
// Gecko is better at loading content with document.write than with
|
440
|
+
// javascript: URLs. With the latter, it tends to put cruft in history,
|
441
|
+
// and gets confused by <base>.
|
442
|
+
['loadHTMLWithDocWrite', function () {
|
443
|
+
result(Monocle.Browser.is.Gecko || Monocle.Browser.is.Opera);
|
444
|
+
}]
|
445
|
+
];
|
446
|
+
|
447
|
+
|
448
|
+
function isCompatible() {
|
449
|
+
return (
|
450
|
+
API.supportsW3CEvents &&
|
451
|
+
API.supportsCustomEvents &&
|
452
|
+
API.supportsColumns &&
|
453
|
+
API.supportsTransform &&
|
454
|
+
!API.brokenIframeTouchModel
|
455
|
+
);
|
456
|
+
}
|
457
|
+
|
458
|
+
|
459
|
+
API.survey = survey;
|
460
|
+
API.isCompatible = isCompatible;
|
461
|
+
|
462
|
+
return API;
|
463
|
+
}
|