| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /** |
| * @fileoverview First run UI. |
| */ |
| |
| <include src="step.js"> |
| |
| // Transitions durations. |
| /** @const */ var DEFAULT_TRANSITION_DURATION_MS = 400; |
| /** @const */ var BG_TRANSITION_DURATION_MS = 800; |
| |
| /** |
| * Changes visibility of element with animated transition. |
| * @param {Element} element Element which visibility should be changed. |
| * @param {boolean} visible Whether element should be visible after transition. |
| * @param {number=} opt_transitionDuration Time length of transition in |
| * milliseconds. Default value is DEFAULT_TRANSITION_DURATION_MS. |
| * @param {function()=} opt_onFinished Called after transition has finished. |
| */ |
| function changeVisibility( |
| element, visible, opt_transitionDuration, opt_onFinished) { |
| var classes = element.classList; |
| // If target visibility is the same as current element visibility. |
| if (classes.contains('transparent') === !visible) { |
| if (opt_onFinished) |
| opt_onFinished(); |
| return; |
| } |
| var transitionDuration = (opt_transitionDuration === undefined) ? |
| cr.FirstRun.getDefaultTransitionDuration() : opt_transitionDuration; |
| var style = element.style; |
| var oldDurationValue = style.getPropertyValue('transition-duration'); |
| style.setProperty('transition-duration', transitionDuration + 'ms'); |
| var transition = visible ? 'show-animated' : 'hide-animated'; |
| classes.add(transition); |
| classes.toggle('transparent'); |
| element.addEventListener('webkitTransitionEnd', function f() { |
| element.removeEventListener('webkitTransitionEnd', f); |
| classes.remove(transition); |
| if (oldDurationValue) |
| style.setProperty('transition-duration', oldDurationValue); |
| else |
| style.removeProperty('transition-duration'); |
| if (opt_onFinished) |
| opt_onFinished(); |
| }); |
| ensureTransitionEndEvent(element, transitionDuration); |
| } |
| |
| cr.define('cr.FirstRun', function() { |
| return { |
| // Whether animated transitions are enabled. |
| transitionsEnabled_: false, |
| |
| // SVG element representing UI background. |
| background_: null, |
| |
| // Container for background. |
| backgroundContainer_: null, |
| |
| // Mask element describing transparent "holes" in background. |
| mask_: null, |
| |
| // Pattern used for creating rectangular holes. |
| rectangularHolePattern_: null, |
| |
| // Pattern used for creating round holes. |
| roundHolePattern_: null, |
| |
| // Dictionary keeping all available tutorial steps by their names. |
| steps_: {}, |
| |
| // Element representing step currently shown for user. |
| currentStep_: null, |
| |
| /** |
| * Initializes internal structures and preparing steps. |
| */ |
| initialize: function() { |
| disableTextSelectAndDrag(); |
| this.transitionsEnabled_ = loadTimeData.getBoolean('transitionsEnabled'); |
| this.background_ = $('background'); |
| this.backgroundContainer_ = $('background-container'); |
| this.mask_ = $('mask'); |
| this.rectangularHolePattern_ = $('rectangular-hole-pattern'); |
| this.rectangularHolePattern_.removeAttribute('id'); |
| this.roundHolePattern_ = $('round-hole-pattern'); |
| this.roundHolePattern_.removeAttribute('id'); |
| var stepElements = document.getElementsByClassName('step'); |
| for (var i = 0; i < stepElements.length; ++i) { |
| var step = stepElements[i]; |
| cr.FirstRun.DecorateStep(step); |
| this.steps_[step.getName()] = step; |
| } |
| this.setBackgroundVisible(true, function() { |
| chrome.send('initialized'); |
| }); |
| }, |
| |
| /** |
| * Hides all elements and background. |
| */ |
| finalize: function() { |
| // At first we hide holes (job 1) and current step (job 2) simultaneously, |
| // then background. |
| var jobsLeft = 2; |
| var onJobDone = function() { |
| --jobsLeft; |
| if (jobsLeft) |
| return; |
| this.setBackgroundVisible(false, function() { |
| chrome.send('finalized'); |
| }); |
| }.bind(this); |
| this.doHideCurrentStep_(function(name) { |
| if (name) |
| chrome.send('stepHidden', [name]); |
| onJobDone(); |
| }); |
| this.removeHoles(onJobDone); |
| }, |
| |
| /** |
| * Adds transparent rectangular hole to background. |
| * @param {number} x X coordinate of top-left corner of hole. |
| * @param {number} y Y coordinate of top-left corner of hole. |
| * @param {number} widht Width of hole. |
| * @param {number} height Height of hole. |
| */ |
| addRectangularHole: function(x, y, width, height) { |
| var hole = this.rectangularHolePattern_.cloneNode(); |
| hole.setAttribute('x', x); |
| hole.setAttribute('y', y); |
| hole.setAttribute('width', width); |
| hole.setAttribute('height', height); |
| this.mask_.appendChild(hole); |
| setTimeout(function() { |
| changeVisibility(hole, true); |
| }, 0); |
| }, |
| |
| /** |
| * Adds transparent round hole to background. |
| * @param {number} x X coordinate of circle center. |
| * @param {number} y Y coordinate of circle center. |
| * @param {number} radius Radius of circle. |
| */ |
| addRoundHole: function(x, y, radius) { |
| var hole = this.roundHolePattern_.cloneNode(); |
| hole.setAttribute('cx', x); |
| hole.setAttribute('cy', y); |
| hole.setAttribute('r', radius); |
| this.mask_.appendChild(hole); |
| setTimeout(function() { |
| changeVisibility(hole, true); |
| }, 0); |
| }, |
| |
| /** |
| * Removes all holes previously added by |addHole|. |
| * @param {function=} opt_onHolesRemoved Called after all holes have been |
| * hidden. |
| */ |
| removeHoles: function(opt_onHolesRemoved) { |
| var mask = this.mask_; |
| var holes = Array.prototype.slice.call( |
| mask.getElementsByClassName('hole')); |
| var holesLeft = holes.length; |
| if (!holesLeft) { |
| if (opt_onHolesRemoved) |
| opt_onHolesRemoved(); |
| return; |
| } |
| holes.forEach(function(hole) { |
| changeVisibility(hole, false, this.getDefaultTransitionDuration(), |
| function() { |
| mask.removeChild(hole); |
| --holesLeft; |
| if (!holesLeft && opt_onHolesRemoved) |
| opt_onHolesRemoved(); |
| }); |
| }.bind(this)); |
| }, |
| |
| /** |
| * Hides currently active step and notifies chrome after step has been |
| * hidden. |
| */ |
| hideCurrentStep: function() { |
| assert(this.currentStep_); |
| this.doHideCurrentStep_(function(name) { |
| chrome.send('stepHidden', [name]); |
| }); |
| }, |
| |
| /** |
| * Hides currently active step. |
| * @param {function(string)=} opt_onStepHidden Called after step has been |
| * hidden. |
| */ |
| doHideCurrentStep_: function(opt_onStepHidden) { |
| if (!this.currentStep_) { |
| if (opt_onStepHidden) |
| opt_onStepHidden(); |
| return; |
| } |
| var name = this.currentStep_.getName(); |
| this.currentStep_.hide(true, function() { |
| this.currentStep_ = null; |
| if (opt_onStepHidden) |
| opt_onStepHidden(name); |
| }.bind(this)); |
| }, |
| |
| /** |
| * Shows step with given name in given position. |
| * @param {string} name Name of step. |
| * @param {object} position Optional parameter with optional fields |top|, |
| * |right|, |bottom|, |left| used for step positioning. |
| * @param {Array} pointWithOffset Optional parameter for positioning |
| * bubble. Contains [x, y, offset], where (x, y) - point to which bubble |
| * points, offset - distance between arrow and point. |
| */ |
| showStep: function(name, position, pointWithOffset) { |
| assert(!this.currentStep_); |
| if (!this.steps_.hasOwnProperty(name)) |
| throw Error('Step "' + name + '" not found.'); |
| var step = this.steps_[name]; |
| if (position) |
| step.setPosition(position); |
| if (pointWithOffset) |
| step.setPointsTo(pointWithOffset.slice(0, 2), pointWithOffset[2]); |
| step.show(true, function(step) { |
| step.focusDefaultControl(); |
| }); |
| this.currentStep_ = step; |
| }, |
| |
| /** |
| * Sets visibility of the background. |
| * @param {boolean} visibility Whether background should be visible. |
| * @param {function()=} opt_onCompletion Called after visibility has |
| * changed. |
| */ |
| setBackgroundVisible: function(visible, opt_onCompletion) { |
| changeVisibility(this.backgroundContainer_, visible, |
| this.getBackgroundTransitionDuration(), opt_onCompletion); |
| }, |
| |
| /** |
| * Returns default duration of animated transitions, in ms. |
| */ |
| getDefaultTransitionDuration: function() { |
| return this.transitionsEnabled_ ? DEFAULT_TRANSITION_DURATION_MS : 0; |
| }, |
| |
| /** |
| * Returns duration of transitions of background shield, in ms. |
| */ |
| getBackgroundTransitionDuration: function() { |
| return this.transitionsEnabled_ ? BG_TRANSITION_DURATION_MS : 0; |
| } |
| }; |
| }); |
| |
| /** |
| * Initializes UI. |
| */ |
| window.onload = function() { |
| cr.FirstRun.initialize(); |
| }; |