blob: 6d5484097a168ec7cd18f3e5ea21df65e01ef081 [file] [log] [blame]
// 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) {
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);
};
// See also c/b/r/chromeos/login/login_screen_behavior.js
cr.define('login', function() {
var result = {};
result[name] = api;
return result;
});
}
};
});