Bug 1181239 - Enable eslint 'use strict' function rule for Loop's content code. r/rs=dmose

This commit is contained in:
Mark Banner 2015-07-09 08:00:46 +01:00
Родитель 07eae88d8c
Коммит 38138f860a
16 изменённых файлов: 246 добавлений и 216 удалений

Просмотреть файл

@ -61,7 +61,7 @@
"object-curly-spacing": 0, // [2, "always"],
"quotes": [2, "double", "avoid-escape"],
"spaced-comment": [2, "always"],
"strict": 0, // [2, "function"],
"strict": [2, "function"],
// eslint-plugin-react rules. These are documented at
// <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
"react/jsx-quotes": [2, "double", "avoid-escape"],

Просмотреть файл

@ -10,6 +10,8 @@ loop.store = loop.store || {};
* the window data and store the window type.
*/
loop.store.ConversationAppStore = (function() {
"use strict";
/**
* Constructor
*

Просмотреть файл

@ -4,6 +4,7 @@
var loop = loop || {};
loop.conversationViews = (function(mozL10n) {
"use strict";
var CALL_STATES = loop.store.CALL_STATES;
var CALL_TYPES = loop.shared.utils.CALL_TYPES;

Просмотреть файл

@ -4,6 +4,7 @@
var loop = loop || {};
loop.conversationViews = (function(mozL10n) {
"use strict";
var CALL_STATES = loop.store.CALL_STATES;
var CALL_TYPES = loop.shared.utils.CALL_TYPES;

Просмотреть файл

@ -6,6 +6,8 @@ var loop = loop || {};
loop.store = loop.store || {};
(function() {
"use strict";
var sharedActions = loop.shared.actions;
var CALL_TYPES = loop.shared.utils.CALL_TYPES;
var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;

Просмотреть файл

@ -13,6 +13,7 @@
*/
var loop = loop || {};
loop.Dispatcher = (function() {
"use strict";
function Dispatcher() {
this._eventData = {};

Просмотреть файл

@ -4,6 +4,7 @@
var loop = loop || {};
loop.OTSdkDriver = (function() {
"use strict";
var sharedActions = loop.shared.actions;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;

Просмотреть файл

@ -113,6 +113,8 @@ loop.store.createStore = (function() {
* });
*/
loop.store.StoreMixin = (function() {
"use strict";
var _stores = {};
function StoreMixin(id) {
function _getStore() {

Просмотреть файл

@ -6,6 +6,8 @@ var loop = loop || {};
loop.shared = loop.shared || {};
loop.shared.views = loop.shared.views || {};
loop.shared.views.chat = (function(mozL10n) {
"use strict";
var sharedActions = loop.shared.actions;
var sharedMixins = loop.shared.mixins;
var sharedViews = loop.shared.views;

Просмотреть файл

@ -6,6 +6,8 @@ var loop = loop || {};
loop.shared = loop.shared || {};
loop.shared.views = loop.shared.views || {};
loop.shared.views.chat = (function(mozL10n) {
"use strict";
var sharedActions = loop.shared.actions;
var sharedMixins = loop.shared.mixins;
var sharedViews = loop.shared.views;

Просмотреть файл

@ -83,6 +83,8 @@ app.use("/test", express.static(__dirname + "/../test"));
// As we don't have hashes on the urls, the best way to serve the index files
// appears to be to be to closely filter the url and match appropriately.
function serveIndex(req, res) {
"use strict";
return res.sendfile(__dirname + "/content/index.html");
}

Просмотреть файл

@ -2,6 +2,7 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
describe("loop.store.ConversationAppStore", function () {
"use strict";
var expect = chai.expect;
var sharedActions = loop.shared.actions;

Просмотреть файл

@ -2,6 +2,8 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
describe("loop.store.StandaloneAppStore", function () {
"use strict";
var expect = chai.expect;
var sharedActions = loop.shared.actions;
var sharedUtils = loop.shared.utils;

Просмотреть файл

@ -2,30 +2,33 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
* objects and makes them returning the string id and serialized vars if any,
* for any requested string id.
* @type {Object}
*/
navigator.mozL10n = document.mozL10n = {
initialize: function(){},
(function() {
"use strict";
getDirection: function(){},
/**
* /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
* objects and makes them returning the string id and serialized vars if any,
* for any requested string id.
* @type {Object}
*/
navigator.mozL10n = document.mozL10n = {
initialize: function(){},
get: function(stringId, vars) {
getDirection: function(){},
// upcase the first letter
var readableStringId = stringId.replace(/^./, function(match) {
"use strict";
return match.toUpperCase();
}).replace(/_/g, " "); // and convert _ chars to spaces
get: function(stringId, vars) {
return "" + readableStringId + (vars ? ";" + JSON.stringify(vars) : "");
},
// upcase the first letter
var readableStringId = stringId.replace(/^./, function(match) {
return match.toUpperCase();
}).replace(/_/g, " "); // and convert _ chars to spaces
/* For timestamp formatting reasons. */
language: {
code: "en-US"
}
};
return "" + readableStringId + (vars ? ";" + JSON.stringify(vars) : "");
},
/* For timestamp formatting reasons. */
language: {
code: "en-US"
}
};
})();

Просмотреть файл

@ -114,52 +114,56 @@ var fakeContacts = [{
updated: 1406798311748
}];
/**
* Faking the mozLoop object which doesn't exist in regular web pages.
* @type {Object}
*/
navigator.mozLoop = {
ensureRegistered: function() {},
getAudioBlob: function(){},
getLoopPref: function(pref) {
switch(pref) {
// Ensure we skip FTE completely.
case "gettingStarted.seen":
case "contacts.gravatars.promo":
case "contextInConversations.enabled":
return true;
case "contacts.gravatars.show":
return false;
}
},
hasEncryptionKey: true,
setLoopPref: function(){},
releaseCallData: function() {},
copyString: function() {},
getUserAvatar: function(emailAddress) {
return "http://www.gravatar.com/avatar/" + (Math.ceil(Math.random() * 3) === 2 ?
"0a996f0fe2727ef1668bdb11897e4459" : "foo") + ".jpg?default=blank&s=40";
},
getSelectedTabMetadata: function(callback) {
callback({
previews: ["chrome://branding/content/about-logo.png"],
description: "sample webpage description",
url: "https://www.example.com"
});
},
contacts: {
getAll: function(callback) {
callback(null, [].concat(fakeContacts));
(function() {
"use strict";
/**
* Faking the mozLoop object which doesn't exist in regular web pages.
* @type {Object}
*/
navigator.mozLoop = {
ensureRegistered: function() {},
getAudioBlob: function(){},
getLoopPref: function(pref) {
switch(pref) {
// Ensure we skip FTE completely.
case "gettingStarted.seen":
case "contacts.gravatars.promo":
case "contextInConversations.enabled":
return true;
case "contacts.gravatars.show":
return false;
}
},
on: function() {}
},
rooms: {
getAll: function(version, callback) {
callback(null, [].concat(fakeRooms));
hasEncryptionKey: true,
setLoopPref: function(){},
releaseCallData: function() {},
copyString: function() {},
getUserAvatar: function(emailAddress) {
return "http://www.gravatar.com/avatar/" + (Math.ceil(Math.random() * 3) === 2 ?
"0a996f0fe2727ef1668bdb11897e4459" : "foo") + ".jpg?default=blank&s=40";
},
on: function() {}
},
fxAEnabled: true,
startAlerting: function() {},
stopAlerting: function() {}
};
getSelectedTabMetadata: function(callback) {
callback({
previews: ["chrome://branding/content/about-logo.png"],
description: "sample webpage description",
url: "https://www.example.com"
});
},
contacts: {
getAll: function(callback) {
callback(null, [].concat(fakeContacts));
},
on: function() {}
},
rooms: {
getAll: function(version, callback) {
callback(null, [].concat(fakeRooms));
},
on: function() {}
},
fxAEnabled: true,
startAlerting: function() {},
stopAlerting: function() {}
};
})();

Просмотреть файл

@ -12,159 +12,163 @@
*/
window.queuedFrames = [];
/**
* Renders this.props.children inside an <iframe>.
*
* Works by creating the iframe, waiting for that to finish, and then
* rendering the children inside that. Waits for a while in the hopes that the
* contents will have been rendered, and then fires a callback indicating that.
*
* @see onContentsRendered for the gory details about this.
*
* @type {ReactComponentFactory<P>}
*/
window.Frame = React.createClass({
propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.element,
React.PropTypes.arrayOf(React.PropTypes.element)
]).isRequired,
className: React.PropTypes.string,
/* By default, <link rel="stylesheet> nodes from the containing frame's
head will be cloned into this iframe. However, if the link also has
a "class" attribute, we only clone it if that class attribute is the
same as cssClass. This allows us to avoid injecting stylesheets that
aren't intended for this rendering of this component. */
cssClass: React.PropTypes.string,
head: React.PropTypes.node,
height: React.PropTypes.number,
onContentsRendered: React.PropTypes.func,
style: React.PropTypes.object,
width: React.PropTypes.number
},
render: function() {
return React.createElement("iframe", {
style: this.props.style,
head: this.props.head,
width: this.props.width,
height: this.props.height,
className: this.props.className
});
},
componentDidMount: function() {
this.renderFrameContents();
},
renderFrameContents: function() {
function isStyleSheet(node) {
return node.tagName.toLowerCase() === "link" &&
node.getAttribute("rel") === "stylesheet";
}
(function() {
"use strict";
var childDoc = this.getDOMNode().contentDocument;
if (childDoc && childDoc.readyState === "complete") {
// Remove this from the queue.
window.queuedFrames.splice(window.queuedFrames.indexOf(this), 1);
/**
* Renders this.props.children inside an <iframe>.
*
* Works by creating the iframe, waiting for that to finish, and then
* rendering the children inside that. Waits for a while in the hopes that the
* contents will have been rendered, and then fires a callback indicating that.
*
* @see onContentsRendered for the gory details about this.
*
* @type {ReactComponentFactory<P>}
*/
window.Frame = React.createClass({
propTypes: {
children: React.PropTypes.oneOfType([
React.PropTypes.element,
React.PropTypes.arrayOf(React.PropTypes.element)
]).isRequired,
className: React.PropTypes.string,
/* By default, <link rel="stylesheet> nodes from the containing frame's
head will be cloned into this iframe. However, if the link also has
a "class" attribute, we only clone it if that class attribute is the
same as cssClass. This allows us to avoid injecting stylesheets that
aren't intended for this rendering of this component. */
cssClass: React.PropTypes.string,
head: React.PropTypes.node,
height: React.PropTypes.number,
onContentsRendered: React.PropTypes.func,
style: React.PropTypes.object,
width: React.PropTypes.number
},
render: function() {
return React.createElement("iframe", {
style: this.props.style,
head: this.props.head,
width: this.props.width,
height: this.props.height,
className: this.props.className
});
},
componentDidMount: function() {
this.renderFrameContents();
},
renderFrameContents: function() {
function isStyleSheet(node) {
return node.tagName.toLowerCase() === "link" &&
node.getAttribute("rel") === "stylesheet";
}
var iframeHead = childDoc.querySelector("head");
var parentHeadChildren = document.querySelector("head").children;
var childDoc = this.getDOMNode().contentDocument;
if (childDoc && childDoc.readyState === "complete") {
// Remove this from the queue.
window.queuedFrames.splice(window.queuedFrames.indexOf(this), 1);
[].forEach.call(parentHeadChildren, function(parentHeadNode) {
var iframeHead = childDoc.querySelector("head");
var parentHeadChildren = document.querySelector("head").children;
// if this node is a CSS stylesheet...
if (isStyleSheet(parentHeadNode)) {
// and it has a class different from the one that this frame does,
// return immediately instead of appending it. Note that this
// explicitly does not check for cssClass existence, because
// non-existence of cssClass will be different from a style
// element that does have a class on it, and we want it to return
// in that case.
if (parentHeadNode.hasAttribute("class") &&
parentHeadNode.getAttribute("class") !== this.props.cssClass) {
return;
[].forEach.call(parentHeadChildren, function(parentHeadNode) {
// if this node is a CSS stylesheet...
if (isStyleSheet(parentHeadNode)) {
// and it has a class different from the one that this frame does,
// return immediately instead of appending it. Note that this
// explicitly does not check for cssClass existence, because
// non-existence of cssClass will be different from a style
// element that does have a class on it, and we want it to return
// in that case.
if (parentHeadNode.hasAttribute("class") &&
parentHeadNode.getAttribute("class") !== this.props.cssClass) {
return;
}
}
iframeHead.appendChild(parentHeadNode.cloneNode(true));
}.bind(this));
var contents = React.createElement("div",
undefined,
this.props.head,
this.props.children
);
React.render(contents, childDoc.body, this.fireOnContentsRendered);
// Set the RTL mode. We assume for now that rtl is the only query parameter.
//
// See also "ShowCase" in ui-showcase.jsx
if (document.location.search === "?rtl=1") {
childDoc.documentElement.setAttribute("lang", "ar");
childDoc.documentElement.setAttribute("dir", "rtl");
}
} else {
// Queue it, only if it isn't already. We do need to set the timeout
// regardless, as this function can get re-entered several times.
if (window.queuedFrames.indexOf(this) === -1) {
window.queuedFrames.push(this);
}
setTimeout(this.renderFrameContents, 0);
}
},
/**
* Fires the onContentsRendered callback passed in via this.props,
* with the first argument set to the window global used by the iframe.
* This is useful in extracting things specific to that iframe (such as
* the matchMedia function) for use by code running in that iframe. Once
* React gets a more complete "context" feature:
*
* https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
*
* we should be able to avoid reaching into the DOM like this.
*
* XXX wait a little while. After React has rendered this iframe (eg the
* virtual DOM cache gets flushed to the browser), there's still more stuff
* that needs to happen before layout completes. If onContentsRendered fires
* before that happens, the wrong sizes (eg remote stream vertical height
* of 0) are used to compute the position in the MediaSetupStream, resulting
* in everything looking wonky. One high likelihood candidate for the delay
* here involves loading/decode poster images, but even using link
* rel=prefetch on those isn't enough to workaround this problem, so there
* may be more.
*
* There doesn't appear to be a good cross-browser way to handle this
* at the moment without gross violation of encapsulation (see
* http://stackoverflow.com/questions/27241186/how-to-determine-when-document-has-loaded-after-loading-external-csshttp://stackoverflow.com/questions/27241186/how-to-determine-when-document-has-loaded-after-loading-external-css
* for discussion of a related problem.
*
* For now, just wait for multiple seconds. Yuck.
*/
fireOnContentsRendered: function() {
if (!this.props.onContentsRendered) {
return;
}
var contentWindow;
try {
contentWindow = this.getDOMNode().contentWindow;
if (!contentWindow) {
throw new Error("no content window returned");
}
iframeHead.appendChild(parentHeadNode.cloneNode(true));
}.bind(this));
var contents = React.createElement("div",
undefined,
this.props.head,
this.props.children
);
React.render(contents, childDoc.body, this.fireOnContentsRendered);
// Set the RTL mode. We assume for now that rtl is the only query parameter.
//
// See also "ShowCase" in ui-showcase.jsx
if (document.location.search === "?rtl=1") {
childDoc.documentElement.setAttribute("lang", "ar");
childDoc.documentElement.setAttribute("dir", "rtl");
}
} else {
// Queue it, only if it isn't already. We do need to set the timeout
// regardless, as this function can get re-entered several times.
if (window.queuedFrames.indexOf(this) === -1) {
window.queuedFrames.push(this);
}
setTimeout(this.renderFrameContents, 0);
}
},
/**
* Fires the onContentsRendered callback passed in via this.props,
* with the first argument set to the window global used by the iframe.
* This is useful in extracting things specific to that iframe (such as
* the matchMedia function) for use by code running in that iframe. Once
* React gets a more complete "context" feature:
*
* https://facebook.github.io/react/blog/2015/02/24/streamlining-react-elements.html#solution-make-context-parent-based-instead-of-owner-based
*
* we should be able to avoid reaching into the DOM like this.
*
* XXX wait a little while. After React has rendered this iframe (eg the
* virtual DOM cache gets flushed to the browser), there's still more stuff
* that needs to happen before layout completes. If onContentsRendered fires
* before that happens, the wrong sizes (eg remote stream vertical height
* of 0) are used to compute the position in the MediaSetupStream, resulting
* in everything looking wonky. One high likelihood candidate for the delay
* here involves loading/decode poster images, but even using link
* rel=prefetch on those isn't enough to workaround this problem, so there
* may be more.
*
* There doesn't appear to be a good cross-browser way to handle this
* at the moment without gross violation of encapsulation (see
* http://stackoverflow.com/questions/27241186/how-to-determine-when-document-has-loaded-after-loading-external-csshttp://stackoverflow.com/questions/27241186/how-to-determine-when-document-has-loaded-after-loading-external-css
* for discussion of a related problem.
*
* For now, just wait for multiple seconds. Yuck.
*/
fireOnContentsRendered: function() {
if (!this.props.onContentsRendered) {
return;
}
var contentWindow;
try {
contentWindow = this.getDOMNode().contentWindow;
if (!contentWindow) {
throw new Error("no content window returned");
} catch (ex) {
console.error("exception getting content window", ex);
}
} catch (ex) {
console.error("exception getting content window", ex);
// Using bind to construct a "partial function", where |this| is unchanged,
// but the first parameter is guaranteed to be set. Details at
// https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Example.3A_Partial_Functions
setTimeout(this.props.onContentsRendered.bind(undefined, contentWindow),
3000);
},
componentDidUpdate: function() {
this.renderFrameContents();
},
componentWillUnmount: function() {
React.unmountComponentAtNode(React.findDOMNode(this).contentDocument.body);
}
// Using bind to construct a "partial function", where |this| is unchanged,
// but the first parameter is guaranteed to be set. Details at
// https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Example.3A_Partial_Functions
setTimeout(this.props.onContentsRendered.bind(undefined, contentWindow),
3000);
},
componentDidUpdate: function() {
this.renderFrameContents();
},
componentWillUnmount: function() {
React.unmountComponentAtNode(React.findDOMNode(this).contentDocument.body);
}
});
});
})();