monocle-rails 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
}
|