red 4.1.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,374 @@
1
+ require 'browser'
2
+
3
+ `
4
+ Array.fromCollection = function(collection){
5
+ for (var i = 0, a = [], j = collection.length; i < j; i++){
6
+ a.push($E(collection[i]))
7
+ }
8
+ return a
9
+ };
10
+ // used in several places where element/selector comparison is used
11
+ // tells whether an element matches another element or selector
12
+ Element.prototype.match = function(selector){
13
+ if (!selector) return true;
14
+ var tagid = Selectors.Utils.parseTagAndID(selector);
15
+ var tag = tagid[0], id = tagid[1];
16
+ if (!Selectors.Filters.byID(this, id) || !Selectors.Filters.byTag(this, tag)) return false;
17
+ var parsed = Selectors.Utils.parseSelector(selector);
18
+ return (parsed) ? Selectors.Utils.filter(this, parsed, {}) : true;
19
+ };
20
+
21
+ // Provides polymorphic access to getElementById to Elements as well as documents, both of which
22
+ // can be passed into Selectors.Utils.search as location for searching for subelements.
23
+ Element.prototype.getElementById = function(id, nocash){
24
+ var el = this.ownerDocument.getElementById(id);
25
+ if (!el) return null;
26
+ for (var parent = el.parentNode; parent != this; parent = parent.parentNode){
27
+ if (!parent) return null;
28
+ }
29
+ return $E(el);
30
+ },
31
+
32
+ Element.Attributes = {
33
+ Props: {'html': 'innerHTML', 'class': 'className', 'for': 'htmlFor', 'text': (#{trident?}) ? 'innerText' : 'textContent'},
34
+ Bools: ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readonly', 'multiple', 'selected', 'noresize', 'defer'],
35
+ Camels: ['value', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', 'rowSpan', 'tabIndex', 'useMap']
36
+ };
37
+
38
+ Element.prototype.getProperty = function(attribute){
39
+ var EA = Element.Attributes, key = EA.Props[attribute];
40
+ var value = (key) ? this[key] : this.getAttribute(attribute, 2);
41
+ return (EA.Bools[attribute]) ? !!value : (key) ? value : value || null;
42
+ },
43
+
44
+ String.prototype.contains = function(string, separator){
45
+ return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
46
+ };
47
+
48
+ String.prototype.trim = function(){
49
+ return this.replace(/^\s+|\s+$/g, '');
50
+ };
51
+
52
+ var Selectors = {Cache: {nth: {}, parsed: {}}};
53
+
54
+ Selectors.RegExps = {
55
+ id: (/#([\\w-]+)/),
56
+ tag: (/^(\\w+|\\*)/),
57
+ quick: (/^(\\w+|\\*)$/),
58
+ splitter: (/\\s*([+>~\\s])\\s*([a-zA-Z#.*:\\[])/g),
59
+ combined: (/\\.([\\w-]+)|\\[(\\w+)(?:([!*^$~|]?=)(["']?)([^\\4]*?)\\4)?\\]|:([\\w-]+)(?:\\(["']?(.*?)?["']?\\)|$)/g)
60
+ };
61
+
62
+ Selectors.Utils = {
63
+ // added to replace $uid
64
+ // uses internal Red.id
65
+ object_uid: function(item){
66
+ return item.__id__||(item.__id__=Red.id++)
67
+ },
68
+
69
+ chk: function(item, uniques){
70
+ if (!uniques) return true;
71
+ var uid = Selectors.Utils.object_uid(item);
72
+ if (!uniques[uid]) return uniques[uid] = true;
73
+ return false;
74
+ },
75
+
76
+ parseNthArgument: function(argument){
77
+ if (Selectors.Cache.nth[argument]) return Selectors.Cache.nth[argument];
78
+ var parsed = argument.match(/^([+-]?\\d*)?([a-z]+)?([+-]?\\d*)?$/);
79
+ if (!parsed) return false;
80
+ var inta = parseInt(parsed[1]);
81
+ var a = (inta || inta === 0) ? inta : 1;
82
+ var special = parsed[2] || false;
83
+ var b = parseInt(parsed[3]) || 0;
84
+ if (a != 0){
85
+ b--;
86
+ while (b < 1) b += a;
87
+ while (b >= a) b -= a;
88
+ } else {
89
+ a = b;
90
+ special = 'index';
91
+ }
92
+ switch (special){
93
+ case 'n': parsed = {a: a, b: b, special: 'n'}; break;
94
+ case 'odd': parsed = {a: 2, b: 0, special: 'n'}; break;
95
+ case 'even': parsed = {a: 2, b: 1, special: 'n'}; break;
96
+ case 'first': parsed = {a: 0, special: 'index'}; break;
97
+ case 'last': parsed = {special: 'last-child'}; break;
98
+ case 'only': parsed = {special: 'only-child'}; break;
99
+ default: parsed = {a: (a - 1), special: 'index'};
100
+ }
101
+
102
+ return Selectors.Cache.nth[argument] = parsed;
103
+ },
104
+
105
+ parseSelector: function(selector){
106
+ if (Selectors.Cache.parsed[selector]) return Selectors.Cache.parsed[selector];
107
+ var m, parsed = {classes: [], pseudos: [], attributes: []};
108
+ while ((m = Selectors.RegExps.combined.exec(selector))){
109
+ var cn = m[1], an = m[2], ao = m[3], av = m[5], pn = m[6], pa = m[7];
110
+ if (cn){
111
+ parsed.classes.push(cn);
112
+ } else if (pn){
113
+ var parser = Selectors.Pseudo[pn];
114
+
115
+ if (parser) parsed.pseudos.push({parser: parser, argument: pa});
116
+ else parsed.attributes.push({name: pn, operator: '=', value: pa});
117
+ } else if (an){
118
+ parsed.attributes.push({name: an, operator: ao, value: av});
119
+ }
120
+ }
121
+ if (!parsed.classes.length) delete parsed.classes;
122
+ if (!parsed.attributes.length) delete parsed.attributes;
123
+ if (!parsed.pseudos.length) delete parsed.pseudos;
124
+ if (!parsed.classes && !parsed.attributes && !parsed.pseudos) parsed = null;
125
+ return Selectors.Cache.parsed[selector] = parsed;
126
+ },
127
+
128
+ parseTagAndID: function(selector){
129
+ var tag = selector.match(Selectors.RegExps.tag);
130
+ var id = selector.match(Selectors.RegExps.id);
131
+ return [(tag) ? tag[1] : '*', (id) ? id[1] : false];
132
+ },
133
+
134
+ filter: function(item, parsed, local){
135
+ var i;
136
+ if (parsed.classes){
137
+ for (i = parsed.classes.length; i--; i){
138
+ var cn = parsed.classes[i];
139
+ if (!Selectors.Filters.byClass(item, cn)) return false;
140
+ }
141
+ }
142
+ if (parsed.attributes){
143
+ for (i = parsed.attributes.length; i--; i){
144
+ var att = parsed.attributes[i];
145
+ if (!Selectors.Filters.byAttribute(item, att.name, att.operator, att.value)) return false;
146
+ }
147
+ }
148
+ if (parsed.pseudos){
149
+ for (i = parsed.pseudos.length; i--; i){
150
+ var psd = parsed.pseudos[i];
151
+ if (!Selectors.Filters.byPseudo(item, psd.parser, psd.argument, local)) return false;
152
+ }
153
+ }
154
+ return true;
155
+ },
156
+
157
+ getByTagAndID: function(ctx, tag, id){
158
+ if (id){
159
+ var item = (ctx.getElementById) ? ctx.getElementById(id, true) : Element.getElementById(ctx, id, true);
160
+ return (item && Selectors.Filters.byTag(item, tag)) ? [item] : [];
161
+ } else {
162
+ return ctx.getElementsByTagName(tag);
163
+ }
164
+ },
165
+
166
+ search: function(self, expression, local){
167
+ var splitters = [];
168
+
169
+ var selectors = expression.trim().replace(Selectors.RegExps.splitter, function(m0, m1, m2){
170
+ splitters.push(m1);
171
+ return ':)' + m2;
172
+ }).split(':)');
173
+
174
+
175
+ var items, filtered, item;
176
+ for (var i = 0, l = selectors.length; i < l; i++){
177
+ var selector = selectors[i];
178
+
179
+ if (i == 0 && Selectors.RegExps.quick.test(selector)){
180
+ items = self.getElementsByTagName(selector);
181
+ continue;
182
+ }
183
+
184
+ var splitter = splitters[i - 1];
185
+
186
+ var tagid = Selectors.Utils.parseTagAndID(selector);
187
+ var tag = tagid[0], id = tagid[1];
188
+ if (i == 0){
189
+ items = Selectors.Utils.getByTagAndID(self, tag, id);
190
+ } else {
191
+ var uniques = {}, found = [];
192
+ for (var j = 0, k = items.length; j < k; j++) found = Selectors.Getters[splitter](found, items[j], tag, id, uniques);
193
+ items = found;
194
+ }
195
+
196
+ var parsed = Selectors.Utils.parseSelector(selector);
197
+ if (parsed){
198
+ filtered = [];
199
+ for (var m = 0, n = items.length; m < n; m++){
200
+ item = items[m];
201
+ if (Selectors.Utils.filter(item, parsed, local)) filtered.push(item);
202
+ }
203
+ items = filtered;
204
+ }
205
+
206
+ }
207
+ return items;
208
+
209
+ }
210
+
211
+ };
212
+
213
+ Selectors.Getters = {
214
+
215
+ ' ': function(found, self, tag, id, uniques){
216
+ var items = Selectors.Utils.getByTagAndID(self, tag, id);
217
+ for (var i = 0, l = items.length; i < l; i++){
218
+ var item = items[i];
219
+ if (Selectors.Utils.chk(item, uniques)) found.push(item);
220
+ }
221
+ return found;
222
+ },
223
+
224
+ '>': function(found, self, tag, id, uniques){
225
+ var children = Selectors.Utils.getByTagAndID(self, tag, id);
226
+ for (var i = 0, l = children.length; i < l; i++){
227
+ var child = children[i];
228
+ if (child.parentNode == self && Selectors.Utils.chk(child, uniques)) found.push(child);
229
+ }
230
+ return found;
231
+ },
232
+
233
+ '+': function(found, self, tag, id, uniques){
234
+ while ((self = self.nextSibling)){
235
+ if (self.nodeType == 1){
236
+ if (Selectors.Utils.chk(self, uniques) && Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
237
+ break;
238
+ }
239
+ }
240
+ return found;
241
+ },
242
+
243
+ '~': function(found, self, tag, id, uniques){
244
+
245
+ while ((self = self.nextSibling)){
246
+ if (self.nodeType == 1){
247
+ if (!Selectors.Utils.chk(self, uniques)) break;
248
+ if (Selectors.Filters.byTag(self, tag) && Selectors.Filters.byID(self, id)) found.push(self);
249
+ }
250
+ }
251
+ return found;
252
+ }
253
+
254
+ };
255
+
256
+ Selectors.Filters = {
257
+
258
+ byTag: function(self, tag){
259
+ return (tag == '*' || (self.tagName && self.tagName.toLowerCase() == tag));
260
+ },
261
+
262
+ byID: function(self, id){
263
+ return (!id || (self.id && self.id == id));
264
+ },
265
+
266
+ byClass: function(self, klass){
267
+ return (self.className && self.className.contains(klass, ' '));
268
+ },
269
+
270
+ byPseudo: function(self, parser, argument, local){
271
+ return parser.call(self, argument, local);
272
+ },
273
+
274
+ byAttribute: function(self, name, operator, value){
275
+ var result = Element.prototype.getProperty.call(self, name);
276
+ if (!result) return false;
277
+ if (!operator || value == undefined) return true;
278
+ switch (operator){
279
+ case '=': return (result == value);
280
+ case '*=': return (result.contains(value));
281
+ case '^=': return (result.substr(0, value.length) == value);
282
+ case '$=': return (result.substr(result.length - value.length) == value);
283
+ case '!=': return (result != value);
284
+ case '~=': return result.contains(value, ' ');
285
+ case '|=': return result.contains(value, '-');
286
+ }
287
+ return false;
288
+ }
289
+
290
+ };
291
+
292
+ Selectors.Pseudo = {
293
+
294
+ // w3c pseudo selectors
295
+
296
+ empty: function(){
297
+ return !(this.innerText || this.textContent || '').length;
298
+ },
299
+
300
+ not: function(selector){
301
+ return !Element.match(this, selector);
302
+ },
303
+
304
+ contains: function(text){
305
+ return (this.innerText || this.textContent || '').contains(text);
306
+ },
307
+
308
+ 'first-child': function(){
309
+ return Selectors.Pseudo.index.call(this, 0);
310
+ },
311
+
312
+ 'last-child': function(){
313
+ var element = this;
314
+ while ((element = element.nextSibling)){
315
+ if (element.nodeType == 1) return false;
316
+ }
317
+ return true;
318
+ },
319
+
320
+ 'only-child': function(){
321
+ var prev = this;
322
+ while ((prev = prev.previousSibling)){
323
+ if (prev.nodeType == 1) return false;
324
+ }
325
+ var next = this;
326
+ while ((next = next.nextSibling)){
327
+ if (next.nodeType == 1) return false;
328
+ }
329
+ return true;
330
+ },
331
+
332
+ 'nth-child': function(argument, local){
333
+ argument = (argument == undefined) ? 'n' : argument;
334
+ var parsed = Selectors.Utils.parseNthArgument(argument);
335
+ if (parsed.special != 'n') return Selectors.Pseudo[parsed.special].call(this, parsed.a, local);
336
+ var count = 0;
337
+ local.positions = local.positions || {};
338
+ var uid = Selectors.Utils.object_uid(this);
339
+ if (!local.positions[uid]){
340
+ var self = this;
341
+ while ((self = self.previousSibling)){
342
+ if (self.nodeType != 1) continue;
343
+ count ++;
344
+ var position = local.positions[Selectors.Utils.object_uid(self)];
345
+ if (position != undefined){
346
+ count = position + count;
347
+ break;
348
+ }
349
+ }
350
+ local.positions[uid] = count;
351
+ }
352
+ return (local.positions[uid] % parsed.a == parsed.b);
353
+ },
354
+
355
+ // custom pseudo selectors
356
+
357
+ index: function(index){
358
+ var element = this, count = 0;
359
+ while ((element = element.previousSibling)){
360
+ if (element.nodeType == 1 && ++count > index) return false;
361
+ }
362
+ return (count == index);
363
+ },
364
+
365
+ even: function(argument, local){
366
+ return Selectors.Pseudo['nth-child'].call(this, '2n+1', local);
367
+ },
368
+
369
+ odd: function(argument, local){
370
+ return Selectors.Pseudo['nth-child'].call(this, '2n', local);
371
+ }
372
+
373
+ };
374
+ `
@@ -0,0 +1,328 @@
1
+ require 'element'
2
+ require 'window'
3
+
4
+ # Classes mixing in <tt>Situated</tt> and its submodules gain the ability
5
+ # to provide locational and dimensional data about their visual DOM
6
+ # representation within the browser.
7
+ #
8
+ module Situated
9
+ `window.styleString=function(el,prop){if(el.currentStyle){return el.currentStyle[prop.replace(/[_-]\\D/g, function(match){return match.charAt(1).toUpperCase();})];};var computed=document.defaultView.getComputedStyle(el,null);return(computed?computed.getPropertyValue([prop.replace(/[A-Z]/g, function(match){return('-'+match.charAt(0).toLowerCase());})]):null);}`
10
+ module PositionAndSize
11
+
12
+ # call-seq:
13
+ # situated.height -> integer
14
+ # returns the height of the _situated_ in pixels as an integer.
15
+ # height is the amount of vertical space the content requires
16
+ # plus top padding, bottom padding, top border, and bottom border, if any
17
+ #
18
+ #
19
+ # For example,
20
+ # situated = Document['#situated']
21
+ # where _situated_ is an element whose content is 200px tall
22
+ # with a top padding of 20px, bottom padding of 10px
23
+ # and a top border of 1px
24
+ #
25
+ # situated.height #=> 231 (200 + 20 + 10 + 1)
26
+ #
27
+ def height
28
+ self.size[:y]
29
+ end
30
+
31
+ # call-seq:
32
+ # situated.width -> integer
33
+ # returns the width of the _situated_ in pixels as an integer
34
+ # width is the amount of horizontal space the content requires
35
+ # plus left padding, right padding, left border, and right border, if any
36
+ #
37
+ # For example,
38
+ # situated = Document['#situated']
39
+ # where _situated_ is an element whose content is 200px wide
40
+ # with a left padding of 20px, right padding of 10px
41
+ # and a left border of 1px
42
+ #
43
+ # situated.width #=> 231 (200 + 20 + 10 + 1)
44
+ #
45
+ def width
46
+ self.size[:x]
47
+ end
48
+
49
+ def scroll_top
50
+ self.scroll[:y]
51
+ end
52
+
53
+ def scroll_left
54
+ self.scroll[:x]
55
+ end
56
+
57
+ def scroll_height
58
+ self.scroll_size[:y]
59
+ end
60
+
61
+ def scroll_width
62
+ self.scroll_size[:x]
63
+ end
64
+
65
+ def top
66
+ self.position[:x]
67
+ end
68
+
69
+ def left
70
+ self.position[:y]
71
+ end
72
+ end
73
+
74
+ module Element
75
+ include PositionAndSize
76
+
77
+ def size
78
+ return self.window.size if self.is_body?
79
+ return {:x => `#{self}.__native__.offsetWidth`, :y => `#{self}.__native__.offsetHeight`}
80
+ end
81
+
82
+ # call-seq:
83
+ # situated.scroll -> hash
84
+ # returns a hash containing keys <tt>:x<tt> and <tt>:y<tt> representing the
85
+ # the distance that a _situated_ has been scrolled
86
+ # originating at the top left corner of the _situated_
87
+ #
88
+ # For example if a _situated_ has been scrolled 10px left and 5px down
89
+ # situated.scroll #=> {:x => 10, :y => 5}
90
+ def scroll
91
+ return self.window.scroll if self.is_body?
92
+ return {:x => `#{self}.__native__.scrollLeft`, :y => `#{self}.__native__.scrollTop`}
93
+ end
94
+
95
+ def scrolls
96
+ `var element = this.__native__, position = {x : 0, y : 0};
97
+ while (element && !c$Situated.c$Utilities.m$is_body_bool(element)){
98
+ position.x += element.scrollLeft;
99
+ position.y += element.scrollTop;
100
+ element = element.parentNode;
101
+ }`
102
+ return {:x => `position.x`, :y => `position.y`}
103
+ end
104
+
105
+ # call-seq:
106
+ # situated.scroll_size -> hash
107
+ # returns a hash containing keys <tt>:x<tt> and <tt>:y<tt> representing the
108
+ # the size that a _situated_ included potential scrollable area.
109
+ #
110
+ # For example if a _situated_ has been a visible width of 100px and visible height of 50px
111
+ # and 100 additional pixels of horizontal unseen content that can be scrolled to uncover
112
+ #
113
+ # situated.scroll_size #=> {:x => 200, :y => 50}
114
+ #
115
+ def scroll_size
116
+ return self.window.scroll_size if self.is_body?
117
+ return {:x => `#{self}.__native__.scrollWidth`, :y => `#{self}.__native__.scrollHeight`}
118
+ end
119
+
120
+ # call-seq:
121
+ # situated.scroll_to(x,y) -> situated
122
+ #
123
+ # Scrolls the _situated_ to the position referenced by the coordinates <tt>x</tt> and <tt>y</tt>
124
+ # which are pixel dimensions measured from the top left corner of hte _situated_
125
+ #
126
+ # Will scroll to the limit of one dimension if a cooridnate for that dimension
127
+ # is larger than the size element
128
+ #
129
+ # No scrolling in a direction will occur if scrolling in that direction would have no effect.
130
+ #
131
+ # Examples:
132
+ #
133
+ # Given a _situated_ that is 200px wide and 500px long and has
134
+ # only 150px of horizontal dimension visible at any time and
135
+ # only 200px of vertical dimension visible at any time
136
+ #
137
+ # situated.scroll_to(10,10)
138
+ # will scroll the visible area 10 pixels from the left and 10pixels from the top
139
+ #
140
+ # scroll.scroll_to(200,0) will scroll the _situated_ to the maximum left scrolling possible
141
+ # and return the _situated_ to the original height position.
142
+ #
143
+ def scroll_to(x,y)
144
+ if self.is_body?
145
+ self.window.scroll_to(x, y)
146
+ else
147
+ `this.__native__.scrollLeft = x`
148
+ `this.__native__.scrollTop = y`
149
+ end
150
+ return self
151
+ end
152
+
153
+ # call-seq:
154
+ # situated.offset_parent -> element
155
+ #
156
+ # returns the parent element that provides the visual offset from
157
+ # the top left corner of the viewport
158
+ #
159
+ # This is typically the closest statically position element or <tt>body</tt>
160
+ def offset_parent
161
+ element = self
162
+ return nil if element.is_body?
163
+
164
+ # All engines except trident have a native offsetParent property
165
+ `var e = #{element}.__native__.offsetParent`
166
+ return `$E(#{element}.__native__.offsetParent)` unless trident?
167
+
168
+ # For trident we walk the DOM until we have a static positioned element or reach the body
169
+ while ((element = `$E(#{element}.__native__.parentNode)`) && !element.is_body?) do
170
+ return element unless element.styles[:position] == 'static'
171
+ end
172
+
173
+ return nil
174
+ end
175
+
176
+ def offsets
177
+ `var native = this.__native__`
178
+ if trident?
179
+ `var bound = native.getBoundingClientRect()`
180
+ `var html = this.m$document.__native__.documentElement`
181
+ return {
182
+ :x => `bound.left + html.scrollLeft - html.clientLeft`,
183
+ :y => `bound.top + html.scrollTop - html.clientTop`
184
+ }
185
+ end
186
+
187
+ `var position = {x: 0, y: 0}`
188
+ return {:x => `position.x`, :y => `position.y`} if self.is_body?
189
+ `
190
+
191
+ element = native
192
+ while (element && !c$Situated.c$Utilities.m$is_body_bool(element)){
193
+ position.x += element.offsetLeft;
194
+ position.y += element.offsetTop;
195
+
196
+ if (#{gecko?}){
197
+ if (!c$Situated.c$Utilities.m$border_box(element)){
198
+ position.x += c$Situated.c$Utilities.m$left_border(element);
199
+ position.y += c$Situated.c$Utilities.m$top_border(element);
200
+ }
201
+ var parent = element.parentNode;
202
+ if (parent && window.styleString(parent, 'overflow') != 'visible'){
203
+ position.x += c$Situated.c$Utilities.m$left_border(parent);
204
+ position.y += c$Situated.c$Utilities.m$top_border(parent);
205
+ }
206
+ } else if (element != this && #{webkit?}){
207
+ position.x += c$Situated.c$Utilities.m$left_border(element);
208
+ position.y += c$Situated.c$Utilities.m$top_border(element);
209
+ }
210
+
211
+ element = element.offsetParent;
212
+ }
213
+
214
+ if (#{gecko?} && !c$Situated.c$Utilities.m$border_box(native)){
215
+ position.x -= c$Situated.c$Utilities.m$left_border(native);
216
+ position.y -= c$Situated.c$Utilities.m$top_border(native);
217
+ }
218
+ `
219
+ return {:x => `position.x`, :y => `position.y`}
220
+ end
221
+
222
+ def position_at(x,y)
223
+ native = `this.__native__`
224
+ h = {:left => x - Situated::Utilities.styleNumber(native, `'margin-left'`), :top => y - Situated::Utilities.styleNumber(native, `'margin-top'`), :position => 'absolute'}
225
+ self.set_styles(h)
226
+ end
227
+
228
+ def position(relative_to = nil)
229
+ # if (isBody(this)) return {x: 0, y: 0};
230
+ offset = self.offsets
231
+ scroll = self.scrolls
232
+ position = {:x => offset[:x] - scroll[:x], :y => offset[:y] - scroll[:y]}
233
+ relative_position = {:x => 0, :y => 0}
234
+ # relativePosition = (relative_to && (relative_to = $(relative_to)) ? relative_to.position : {x: 0, y: 0};
235
+ a = {:x => position[:x] - relative_position[:x], :y => position[:y] - relative_position[:y]}
236
+ end
237
+ end
238
+
239
+ module Viewport
240
+ include PositionAndSize
241
+
242
+ def size
243
+ win = self.window
244
+ return {:x => `#{win}.__native__.innerWidth`, :y => `#{win}.__native__.innerHeight`} if (presto? || webkit?)
245
+ doc = Situated::Utilities.native_compat_element(self)
246
+ return {:x => `#{doc}.__native__.clientWidth`, :y => `#{doc}.__native__.clientHeight`}
247
+ end
248
+
249
+ def scroll
250
+ win = self.window
251
+ doc = Situated::Utilities.native_compat_element(self)
252
+ return {:x => `#{win}.__native__.pageXOffset` || `#{doc}.__native__.scrollLeft`, :y => `#{win}.__native__pageYOffset` || `#{doc}.__native__.scrollTop`}
253
+ end
254
+
255
+ def scroll_size
256
+ doc = Situated::Utilities.native_compat_element(self)
257
+ min = self.size
258
+ return {:x => `Math.max(#{doc}.__native__.scrollWidth, #{min[:x]})`, :y => `Math.max(#{doc}.__native__.scrollHeight,#{ min[:y]})`}
259
+ end
260
+
261
+ # call-seq:
262
+ # viewport.position -> hash
263
+ # returns the position of the viewport, which is always {:x => 0, :y => 0}
264
+ #
265
+ def position
266
+ return {:x => 0, :y => 0}
267
+ end
268
+
269
+ # call-seq:
270
+ # viewport.position -> hash
271
+ # returns the coordinates of the viewport in pixesl as integers
272
+ # within a hash with the keys
273
+ # :top
274
+ # :left
275
+ # :bottom
276
+ # :right
277
+ # :height
278
+ # :width
279
+ #
280
+ # For example, a _viewport_ that is 500px wide and 800px tall will return
281
+ # viewport.coordinates
282
+ # {:top => 0,
283
+ # :left => 0,
284
+ # :bottom => 800,
285
+ # :right => 500,
286
+ # :height => 800,
287
+ # :width => 500 }
288
+ #
289
+ def coordinates
290
+ size = self.size
291
+ return {:top => 0, :left => 0, :bottom => size[:y], :right => size[:x], :height => size[:y], :width => size[:x]}
292
+ end
293
+ end
294
+
295
+ module Utilities
296
+ def self.is_body?(element)
297
+ `(/^(?:body|html)$/i).test(element.tagName)`
298
+ end
299
+
300
+ def self.styleNumber(native_element, style)
301
+ `parseInt(window.styleString(native_element, style)) || 0`
302
+ end
303
+
304
+ def self.border_box(element)
305
+ `window.styleString(element, '-moz-box-sizing') == 'border-box'`
306
+ end
307
+
308
+ def self.top_border(element)
309
+ `c$Situated.c$Utilities.m$styleNumber(element, 'border-top-width')`
310
+ end
311
+
312
+ def self.left_border(element)
313
+ `c$Situated.c$Utilities.m$styleNumber(element, 'border-left-width')`
314
+ end
315
+
316
+ def self.native_compat_element(element)
317
+ `var doc = #{element.document}.__native__`
318
+ `$E((!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body)`
319
+ end
320
+ end
321
+ end
322
+
323
+ class Element
324
+ include Situated::Element
325
+ end
326
+
327
+ Document.extend(Situated::Viewport)
328
+ Window.extend(Situated::Viewport)