1197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// Copyright 2014 The Chromium Authors. All rights reserved.
2197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch// found in the LICENSE file.
4197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
5197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch'use strict';
6197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
7e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)installClass('HTMLMarqueeElement', function(HTMLMarqueeElementPrototype) {
8197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
9197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDefaultScrollAmount = 6;
10197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDefaultScrollDelayMS = 85;
11197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kMinimumScrollDelayMS = 60;
12197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
13197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDefaultLoopLimit = -1;
14197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
15197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kBehaviorScroll = 'scroll';
16197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kBehaviorSlide = 'slide';
17197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kBehaviorAlternate = 'alternate';
18197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
19197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDirectionLeft = 'left';
20197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDirectionRight = 'right';
21197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDirectionUp = 'up';
22197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kDirectionDown = 'down';
23197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
24197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kPresentationalAttributes = [
25197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        'bgcolor',
26197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        'height',
27197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        'hspace',
28197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        'vspace',
29197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        'width',
30197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    ];
31197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
32197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var pixelLengthRegexp = /^\s*([\d.]+)\s*$/;
33197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var percentageLengthRegexp = /^\s*([\d.]+)\s*%\s*$/;
34197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
35197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    function convertHTMLLengthToCSSLength(value) {
36197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var pixelMatch = value.match(pixelLengthRegexp);
37197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (pixelMatch)
38197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return pixelMatch[1] + 'px';
39197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var percentageMatch = value.match(percentageLengthRegexp);
40197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (percentageMatch)
41197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return percentageMatch[1] + '%';
42197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        return null;
43197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
44197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
45197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    // FIXME: Consider moving these utility functions to PrivateScriptRunner.js.
46197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    var kInt32Max = Math.pow(2, 31);
47197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
48197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    function convertToLong(n) {
49197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // Using parseInt() is wrong but this aligns with the existing behavior of StringImpl::toInt().
50197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // FIXME: Implement a correct algorithm of the Web IDL value conversion.
51197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var value = parseInt(n);
52197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (!isNaN(value) && -kInt32Max <= value && value < kInt32Max)
53197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return value;
54197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        return NaN;
55197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
56197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
57197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    function reflectAttribute(prototype, attributeName, propertyName) {
58197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        Object.defineProperty(prototype, propertyName, {
59197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            get: function() {
60197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return this.getAttribute(attributeName) || '';
61197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            },
62197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            set: function(value) {
63197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                this.setAttribute(attributeName, value);
64197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            },
65197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            configurable: true,
66197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            enumerable: true,
67197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        });
68197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
69197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
70197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    function reflectBooleanAttribute(prototype, attributeName, propertyName) {
71197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        Object.defineProperty(prototype, propertyName, {
72197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            get: function() {
73197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return this.hasAttribute(attributeName);
74197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            },
75197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            set: function(value) {
76197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (value)
77197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    this.setAttribute(attributeName, '');
78197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                else
79197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    this.removeAttribute(attributeName);
80197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            },
81197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        });
82197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
83197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
84197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    function defineInlineEventHandler(prototype, eventName) {
85197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var propertyName = 'on' + eventName;
86197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // FIXME: We should use symbols here instead.
87197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var functionPropertyName = propertyName + 'Function_';
88197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var eventHandlerPropertyName = propertyName + 'EventHandler_';
89197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        Object.defineProperty(prototype, propertyName, {
90197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            get: function() {
91197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                var func = this[functionPropertyName];
92197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return func || null;
93197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            },
94197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            set: function(value) {
95197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                var oldEventHandler = this[eventHandlerPropertyName];
96197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (oldEventHandler)
97197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    this.removeEventListener(eventName, oldEventHandler);
98197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                // Notice that we wrap |value| in an anonymous function so that the
99197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                // author can't call removeEventListener themselves to unregister the
100197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                // inline event handler.
101197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                var newEventHandler = value ? function() { value.apply(this, arguments) } : null;
102197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                if (newEventHandler)
103197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                    this.addEventListener(eventName, newEventHandler);
104197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                this[functionPropertyName] = value;
105197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                this[eventHandlerPropertyName] = newEventHandler;
106197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            },
107197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        });
108197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    }
109197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
110197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'behavior', 'behavior');
111197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'bgcolor', 'bgColor');
112197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'direction', 'direction');
113197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'height', 'height');
114197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'hspace', 'hspace');
115197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'vspace', 'vspace');
116197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectAttribute(HTMLMarqueeElementPrototype, 'width', 'width');
117197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    reflectBooleanAttribute(HTMLMarqueeElementPrototype, 'truespeed', 'trueSpeed');
118197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
119197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    defineInlineEventHandler(HTMLMarqueeElementPrototype, 'start');
120197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    defineInlineEventHandler(HTMLMarqueeElementPrototype, 'finish');
121197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    defineInlineEventHandler(HTMLMarqueeElementPrototype, 'bounce');
122197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
123197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.createdCallback = function() {
124197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var shadow = this.createShadowRoot();
125e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        var style = document.createElement('style');
126197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    style.textContent = ':host { display: inline-block; width: -webkit-fill-available; overflow: hidden; text-align: initial; }' +
127197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            ':host([direction="up"]), :host([direction="down"]) { height: 200px; }';
128197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        shadow.appendChild(style);
129197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
130e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        var mover = document.createElement('div');
131197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        shadow.appendChild(mover);
132197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
133e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        mover.appendChild(document.createElement('content'));
134197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
135197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.loopCount_ = 0;
136197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.mover_ = mover;
137197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.player_ = null;
138197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.continueCallback_ = null;
139197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
140197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        for (var i = 0; i < kPresentationalAttributes.length; ++i)
141197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.initializeAttribute_(kPresentationalAttributes[i]);
142197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
143197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
144197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.attachedCallback = function() {
145197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.start();
146197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
147197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
148197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.detachedCallback = function() {
149197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.stop();
150197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
151197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
152197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.attributeChangedCallback = function(name, oldValue, newValue) {
153197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        switch (name) {
154197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'bgcolor':
155197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.backgroundColor = newValue;
156197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
157197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'height':
158197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.height = convertHTMLLengthToCSSLength(newValue);
159197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
160197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'hspace':
161197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var margin = convertHTMLLengthToCSSLength(newValue);
162197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.marginLeft = margin;
163197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.marginRight = margin;
164197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
165197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'vspace':
166197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var margin = convertHTMLLengthToCSSLength(newValue);
167197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.marginTop = margin;
168197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.marginBottom = margin;
169197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
170197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'width':
171197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.style.width = convertHTMLLengthToCSSLength(newValue);
172197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
173197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'behavior':
174197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case 'direction':
175197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.stop();
176197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.loopCount_ = 0;
177197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.start();
178197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
179197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
180197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
181197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
182197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.initializeAttribute_ = function(name) {
183197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var value = this.getAttribute(name);
184197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (value === null)
185197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return;
186197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.attributeChangedCallback(name, null, value);
187197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
188197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
189197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    Object.defineProperty(HTMLMarqueeElementPrototype, 'scrollAmount', {
190197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        get: function() {
191197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var value = this.getAttribute('scrollamount');
192197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var scrollAmount = convertToLong(value);
193197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (isNaN(scrollAmount) || scrollAmount < 0)
194197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return kDefaultScrollAmount;
195197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return scrollAmount;
196197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        },
197197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        set: function(value) {
198197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (value < 0)
1997242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                throwException(PrivateScriptDOMException.IndexSizeError, "The provided value (" + value + ") is negative.");
200197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.setAttribute('scrollamount', value);
201197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        },
202197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    });
203197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
204197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    Object.defineProperty(HTMLMarqueeElementPrototype, 'scrollDelay', {
205197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        get: function() {
206197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var value = this.getAttribute('scrolldelay');
207197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var scrollDelay = convertToLong(value);
208197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (isNaN(scrollDelay) || scrollDelay < 0)
209197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return kDefaultScrollDelayMS;
210197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return scrollDelay;
211197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        },
212197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        set: function(value) {
213197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (value < 0)
2147242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                throwException(PrivateScriptDOMException.IndexSizeError, "The provided value (" + value + ") is negative.");
215197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.setAttribute('scrolldelay', value);
216197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        },
217197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    });
218197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
219197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    Object.defineProperty(HTMLMarqueeElementPrototype, 'loop', {
220197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        get: function() {
221197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var value = this.getAttribute('loop');
222197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var loop = convertToLong(value);
223197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (isNaN(loop) || loop <= 0)
224197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return kDefaultLoopLimit;
225197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return loop;
226197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        },
227197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        set: function(value) {
228197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (value <= 0 && value != -1)
2297242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci                throwException(PrivateScriptDOMException.IndexSizeError, "The provided value (" + value + ") is neither positive nor -1.");
230197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.setAttribute('loop', value);
231197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        },
232197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    });
233197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
234197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.getGetMetrics_ = function() {
235197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.mover_.style.width = '-webkit-max-content';
236197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
237e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        var moverStyle = getComputedStyle(this.mover_);
238e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        var marqueeStyle = getComputedStyle(this);
239197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
240197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var metrics = {};
241197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        metrics.contentWidth = parseInt(moverStyle.width);
242197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        metrics.contentHeight = parseInt(moverStyle.height);
243197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        metrics.marqueeWidth = parseInt(marqueeStyle.width);
244197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        metrics.marqueeHeight = parseInt(marqueeStyle.height);
245197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
246197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.mover_.style.width = '';
247197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        return metrics;
248197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
249197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
250197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.getAnimationParameters_ = function() {
251197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var metrics = this.getGetMetrics_();
252197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
253197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var totalWidth = metrics.marqueeWidth + metrics.contentWidth;
254197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var totalHeight = metrics.marqueeHeight + metrics.contentHeight;
255197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
256197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var innerWidth = metrics.marqueeWidth - metrics.contentWidth;
257197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var innerHeight = metrics.marqueeHeight - metrics.contentHeight;
258197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
259197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var parameters = {};
260197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
261197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        switch (this.behavior) {
262197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case kBehaviorScroll:
263197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        default:
264197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            switch (this.direction) {
265197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionLeft:
266197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            default:
267197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateX(' + metrics.marqueeWidth + 'px)';
268197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateX(-100%)';
269197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = totalWidth;
270197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
271197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionRight:
272197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateX(-' + metrics.contentWidth + 'px)';
273197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateX(' + metrics.marqueeWidth + 'px)';
274197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = totalWidth;
275197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
276197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionUp:
277197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateY(' + metrics.marqueeHeight + 'px)';
278197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateY(-' + metrics.contentHeight + 'px)';
279197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = totalHeight;
280197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
281197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionDown:
282197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateY(-' + metrics.contentHeight + 'px)';
283197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateY(' + metrics.marqueeHeight + 'px)';
284197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = totalHeight;
285197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
286197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            }
287197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
288197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case kBehaviorAlternate:
289197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            switch (this.direction) {
290197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionLeft:
291197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            default:
292197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateX(' + innerWidth + 'px)';
293197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateX(0)';
294197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = innerWidth;
295197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
296197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionRight:
297197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateX(0)';
298197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateX(' + innerWidth + 'px)';
299197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = innerWidth;
300197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
301197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionUp:
302197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateY(' + innerHeight + 'px)';
303197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateY(0)';
304197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = innerHeight;
305197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
306197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionDown:
307197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateY(0)';
308197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateY(' + innerHeight + 'px)';
309197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = innerHeight;
310197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
311197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            }
312197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
313197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (this.loopCount_ % 2) {
314197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                var transform = parameters.transformBegin;
315197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = parameters.transformEnd;
316197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = transform;
317197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            }
318197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
319197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
320197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        case kBehaviorSlide:
321197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            switch (this.direction) {
322197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionLeft:
323197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            default:
324197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateX(' + metrics.marqueeWidth + 'px)';
325197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateX(0)';
326197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = metrics.marqueeWidth;
327197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
328197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionRight:
329197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateX(-' + metrics.contentWidth + 'px)';
330197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateX(' + innerWidth + 'px)';
331197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = metrics.marqueeWidth;
332197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
333197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionUp:
334197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateY(' + metrics.marqueeHeight + 'px)';
335197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateY(0)';
336197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = metrics.marqueeHeight;
337197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
338197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            case kDirectionDown:
339197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformBegin = 'translateY(-' + metrics.contentHeight + 'px)';
340197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.transformEnd = 'translateY(' + innerHeight + 'px)';
341197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                parameters.distance = metrics.marqueeHeight;
342197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                break;
343197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            }
344197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            break;
345197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
346197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
347197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        return parameters
348197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
349197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
350197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.shouldContinue_ = function() {
351197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var loop = this.loop;
352197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
353197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // By default, slide loops only once.
354197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (loop <= 0 && this.behavior === kBehaviorSlide)
355197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            loop = 1;
356197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
357197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (loop <= 0)
358197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return true;
359197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        return this.loopCount_ < loop;
360197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
361197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
362197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.continue_ = function() {
363197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (!this.shouldContinue_()) {
364197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.player_ = null;
365197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.dispatchEvent(new Event('finish', false, true));
366197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return;
367197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
368197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
369197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var parameters = this.getAnimationParameters_();
370197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
371197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        var player = this.mover_.animate([
372197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            { transform: parameters.transformBegin },
373197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            { transform: parameters.transformEnd },
374197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        ], {
375197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            duration: parameters.distance * this.scrollDelay / this.scrollAmount,
376197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            fill: 'forwards',
377197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        });
378197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
379197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.player_ = player;
380197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
381197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        player.addEventListener('finish', function() {
382197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (player != this.player_)
383197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                return;
384197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            ++this.loopCount_;
385197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.continue_();
386197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            if (this.player_ && this.behavior === kBehaviorAlternate)
387197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch                this.dispatchEvent(new Event('bounce', false, true));
388197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }.bind(this));
389197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
390197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
391197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.start = function() {
392197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (this.continueCallback_ || this.player_)
393197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return;
394e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        this.continueCallback_ = requestAnimationFrame(function() {
395197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.continueCallback_ = null;
396197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.continue_();
397197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }.bind(this));
398197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        this.dispatchEvent(new Event('start', false, true));
399197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
400197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
401197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    HTMLMarqueeElementPrototype.stop = function() {
402197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (!this.continueCallback_ && !this.player_)
403197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return;
404197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
405197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (this.continueCallback_) {
406e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            cancelAnimationFrame(this.continueCallback_);
407197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.continueCallback_ = null;
408197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            return;
409197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
410197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
411197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // FIXME: Rather than canceling the animation, we really should just
412197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // pause the animation, but the pause function is still flagged as
413197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        // experimental.
414197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        if (this.player_) {
415197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            var player = this.player_;
416197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            this.player_ = null;
417197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch            player.cancel();
418197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch        }
419197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    };
420197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
421197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    // FIXME: We have to inject this HTMLMarqueeElement as a custom element in order to make
422197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    // createdCallback, attachedCallback, detachedCallback and attributeChangedCallback workable.
423e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)    // document.registerElement('i-marquee', {
424197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    //    prototype: HTMLMarqueeElementPrototype,
425197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch    // });
426197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch});
427