// Copyright 2014 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 Base class for all login WebUI screens. */ cr.define('login', function() { /** @const */ var CALLBACK_USER_ACTED = 'userActed'; function doNothing() {}; function alwaysTruePredicate() { return true; } var querySelectorAll = HTMLDivElement.prototype.querySelectorAll; var Screen = function(sendPrefix) { this.sendPrefix_ = sendPrefix; }; Screen.prototype = { __proto__: HTMLDivElement.prototype, /** * Prefix added to sent to Chrome messages' names. */ sendPrefix_: null, /** * Called during screen initialization. */ decorate: doNothing, /** * Returns minimal size that screen prefers to have. Default implementation * returns current screen size. * @return {{width: number, height: number}} */ getPreferredSize: function() { return {width: this.offsetWidth, height: this.offsetHeight}; }, /** * Called for currently active screen when screen size changed. */ onWindowResize: doNothing, /** * @final */ initialize: function() { return this.initializeImpl_.apply(this, arguments); }, /** * @final */ send: function() { return this.sendImpl_.apply(this, arguments); }, /** * @override * @final */ querySelectorAll: function() { return this.querySelectorAllImpl_.apply(this, arguments); }, /** * @private */ initializeImpl_: function() { this.decorate(); }, /** * Sends message to Chrome, adding needed prefix to message name. All * arguments after |messageName| are packed into message parameters list. * * @param {string} messageName Name of message without a prefix. * @param {...*} varArgs parameters for message. * @private */ sendImpl_: function(messageName, varArgs) { if (arguments.length == 0) throw Error('Message name is not provided.'); var fullMessageName = this.sendPrefix_ + messageName; var payload = Array.prototype.slice.call(arguments, 1); chrome.send(fullMessageName, payload); }, /** * Calls standart |querySelectorAll| method and returns its result converted * to Array. * @private */ querySelectorAllImpl_: function(selector) { var list = querySelectorAll.call(this, selector); return Array.prototype.slice.call(list); }, /** * If |value| is the value of some property of |this| returns property's * name. Otherwise returns empty string. * @private */ getPropertyNameOf_: function(value) { for (var key in this) if (this[key] === value) return key; return ''; } }; Screen.CALLBACK_USER_ACTED = CALLBACK_USER_ACTED; return { Screen: Screen }; }); cr.define('login', function() { return { /** * Creates class and object for screen. * Methods specified in EXTERNAL_API array of prototype * will be available from C++ part. * Example: * login.createScreen('ScreenName', 'screen-id', { * foo: function() { console.log('foo'); }, * bar: function() { console.log('bar'); } * EXTERNAL_API: ['foo']; * }); * login.ScreenName.register(); * var screen = $('screen-id'); * screen.foo(); // valid * login.ScreenName.foo(); // valid * screen.bar(); // valid * login.ScreenName.bar(); // invalid * * @param {string} name Name of created class. * @param {string} id Id of div representing screen. * @param {(function()|Object)} proto Prototype of object or function that * returns prototype. */ createScreen: function(name, id, template, attributes) { if (typeof template == 'function') template = template(); var apiNames = template.EXTERNAL_API || []; for (var i = 0; i < apiNames.length; ++i) { var methodName = apiNames[i]; if (typeof template[methodName] !== 'function') throw Error('External method "' + methodName + '" for screen "' + name + '" not a function or undefined.'); } function checkPropertyAllowed(propertyName) { if (propertyName.charAt(propertyName.length - 1) === '_' && (propertyName in login.Screen.prototype)) { throw Error('Property "' + propertyName + '" of "' + id + '" ' + 'shadows private property of login.Screen prototype.'); } }; var Constructor = function() { login.Screen.call(this, 'login.' + name + '.'); }; Constructor.prototype = Object.create(login.Screen.prototype); var api = {}; Object.getOwnPropertyNames(template).forEach(function(propertyName) { if (propertyName === 'EXTERNAL_API') return; checkPropertyAllowed(propertyName); var descriptor = Object.getOwnPropertyDescriptor(template, propertyName); Object.defineProperty(Constructor.prototype, propertyName, descriptor); if (apiNames.indexOf(propertyName) >= 0) { api[propertyName] = function() { var screen = $(id); return screen[propertyName].apply(screen, arguments); }; } }); Constructor.prototype.name = function() { return id; }; api.register = function(opt_lazy_init) { var screen = $(id); screen.__proto__ = new Constructor(); if (opt_lazy_init !== undefined && opt_lazy_init) screen.deferredInitialization = function() { screen.initialize(); } else screen.initialize(); Oobe.getInstance().registerScreen(screen, attributes); }; // See also c/b/r/chromeos/login/login_screen_behavior.js cr.define('login', function() { var result = {}; result[name] = api; return result; }); } }; });