text:
Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
Orion/Coding conventions
This is a draft work in progress. If you have suggestions on good naming practices for JavaScript, CSS, and HTML, please raise a discussion on the Orion mailing list.
- 1 JavaScript
- 1.1 Compatibility
- 1.2 General Guidelines
- 1.3 Modules
- 1.4 Globals
- 1.5 The 'browser' option
- 1.6 The 'amd' environment
- 1.7 The 'node' environment
- 1.8 The 'mocha' environment
- 1.9 Internal data
- 1.10 Arrays
- 1.10.1 Testing if a value is an array
- 1.10.2 Looping through elements
- 1.10.3 Searching for an element with strict-equals
- 1.10.4 Applying a transformation to an array
- 1.10.5 Deciding if an element satisfies a condition
- 1.10.6 Determining if every element satisfies a condition
- 1.10.7 Removing elements that satisfy a condition
- 1.11 Objects
- 1.12 Functions
- 1.13 Errors
- 1.14 Classes
- 1.15 Creating a dictionary
- 1.16 Writing an event emitter
- 1.17 Asynchronous operations
- 1.18 XMLHttpRequest and Ajax
- 1.19 URLs
- 1.20 Services
- 1.22 JS Documentation
- 1.23 ESLint
- 2 JavaScript Formatting
- 5 Java
- 6 Copyrights
- 7 References
In general, Orion's JavaScript code is targeted toward modern JS runtimes. This means you can (and should) use ECMAScript 5 features like Object.keys, Array.prototype.forEach, and so on. ECMAScript 6 features should not be used, as they are not yet widely supported.
Some of Orion's client bundles may have more strict requirements about what features are allowed. For example, the Orion Editor supports older browsers like Internet Explorer 8, and thus has to avoid most ES5 features (even Array.prototype.indexOf
is not allowed in IE8).
- ES5 compatibility table
- ES6 compatibility table
- Chromium dashboard (upcoming features in Chrome)
- Can I use...?
General Guidelines
We highly recommend writing Orion code using the Orion IDE, as its built-in validator will catch a lot of possible coding errors and anti-patterns (like accidental globals, for example).
When in doubt about coding style, follow these general rules:
- Favor ES5 features.
- But don't use strict mode yet.
- Favor W3C APIs.
- If a well-supported spec exists, use it.
- If a decent-looking spec exists but it isn't well-supported, write a shim for it, then use it.
- Your code should be written as an AMD module using
. - Each
file shoulddefine
only a single module. - If your code depends on other modules or files, that relationship should be expressed as AMD dependencies (see example below).
- Use the UMD authoring pattern if your module might need to run in non-AMD environments (like CommonJS modules or plain <script> tags).
- See
for an example of a file written using UMD. - When authoring a UMD module, in the "no module-loader" case, your code should place its exports inside the
property of the global object.
- See
✔ Here's how to write a module that depends on orion/Deferred
and orion/xhr
define(["orion/Deferred", "orion/xhr"], function(Deferred, xhr) { // Use Deferred and xhr here. });
Your code should not export any* globals (with the exception of shims -- see URL-shim.js
for an example). Pay close attention to the problem markers in the Orion editor. Implied globals will be flagged as an error.
✘ Code that creates an implicit global variable. Don't do this.
for (i = 0; i < 10; i++) { // ERROR: implied global `i` console.log("Hello world"); }
If your code depends on globals (eg. define
, require
, jQuery
) or browser objects (eg. document
, setTimeout
), they should be explicitly listed in a /*global */
section near the top of the file. This allows them to be statically checked for misspellings by the validator. At minimum, you should have /*global define*/
or /*eslint-env amd*/
✔ Here we declare the document
and define
/*global define document*/ define([], function() { // Use document document.createElement('div'); });
The 'browser' option
✔ Alternatively, you can use ESLint's browser
environment, which predefines many globals for you (including document
, window
, setTimeout
, etc).
/*global define*/ /*eslint-env browser*/ define([], function() { // Use document document.createElement('div'); });
However, browser
should be used with caution. Here's a partial list of the globals it defines:
addEventListener, blur, clearInterval, clearTimeout, close, closed, defaultStatus, document, event, focus,
frames, getComputedStyle, history, Image, length, location, moveBy, moveTo, name, navigator, onbeforeunload,
onblur, onerror, onfocus, onload, onresize, onunload, open, opener, Option, parent, print, removeEventListener,
resizeBy, resizeTo, screen, scroll, scrollBy, scrollTo, setInterval, setTimeout, status, top, window, XMLHttpRequest
Among these are several commonly-used variable and parameter names in JavaScript code: parent, open, closed, event, name, location, etc. If you accidentally reference one of these globals, it can allow a real coding problem to slip past the linter undetected.
✘ For example, here is a coding error hidden by browser:true
/*eslint-env browser*/ function handleEvent(e) { console.log("Got an event: " + event); // Wrong! We intended to reference 'e' here, but the validator will not warn us. }
The 'amd' environment
Instead of writing out /*global define require*/
you can use the shortcut option /*eslint-env amd*/
. This is a good idea, please use it when writing new code.
The 'node' environment
When writing a Node.js module (for example, server-side code for the Orion Node server), use the /*eslint-env node*/
option. It will predefine globals like process
, __dirname
, etc.
The 'mocha' environment
When writing client-side Mocha tests, use /*eslint-env amd mocha*/
, which predefines both AMD and Mocha globals for you.
Internal data
All member variables starting with an underscore ('_') or having the @private jsdoc tag, are internal and should not be referenced by clients. Such variables are subject to change or removal at any time.
Testing if a value is an array
✔ Do this:
Looping through elements
✔ Use Array.prototype.forEach:
array.forEach(function(item) { // ... });
✘ Don't use a loop variable. It is more prone to typing errors, and hinders refactoring by polluting the containing function's scope with i
for (var i=0; i < array.length; i++) { // ... }
Having said that, a typical for
loop will perform faster than the forEach
function. Performance-critical code may need to use for
, but it's best to use forEach
until you've demonstrated that it's too slow for your needs.
✘ Don't treat an array like an object. Never do this:
for (var i in array) { // ... }
✘ When you have to write a for loop, beware the "infamous loop problem[1]".
for (var i=0; i < 10; i++) { var link = document.createElement("a"); link.onclick = function() { alert("I am link number " + i); // Wrong! Every handler says "I am link number 9" when run. }; body.appendChild(link); }
Searching for an element with strict-equals
✔ Use Array.prototype.indexOf:
var index = array.indexOf(item); if (index !== -1) { console.log("Found it!"); }
✘ Don't do a manual search:
for (var i=0; i < array.length; i++) { if (array[i] === item) { console.log("Found it!"); break; } }
Applying a transformation to an array
✔ Use Array.prototype.map.
Deciding if an element satisfies a condition
✔ Use Array.prototype.some.
Also note that some
's early-exit behavior (when the callback returns true
) can be used to replace a manual for .. break
search for an element. For example:
✘ Rather than write this search manually:
var array = [1, 3, 5, 8, 11]; var found; for (var i=0; i < array.length; i++) { var element = array[i]; if (element % 2 === 0) { found = element; break; } } console.log("Found an even number: " + found);
✔ … We can encapsulate it into a some
var array = [1, 3, 5, 8, 11]; var found; array.some(function(element) { if (element % 2 === 0) { found = element; return true; // stops the search } return false; }); console.log("Found an even number: " + found);
Determining if every element satisfies a condition
✔ Use Array.prototype.every.
Removing elements that satisfy a condition
✔ Use Array.prototype.filter.
Testing equality
In comparisons, always use the strict equality ===
and strict inequality !==
operators. This makes your code's intent clear to other readers. While the nonstrict equality ==
may seem like a nice shortcut, it performs type conversions that can be surprising.
✘ For example, a nonstrict comparison against null
doesn't behave like you might expect:
if (value == null) { console.log("value is null... or is it?"); // do stuff with value }
Since undefined == null
, the above code allows value
to be undefined
and pass the if
-test, which may cause problems later on.
Testing for objectness
✔ Do this:
if (value !== null && typeof value === "object") { // use value as an object Object.keys(value); }
✔ This is also OK:
if (value && typeof value === "object") { // use value as an object Object.keys(value); }
The reason that both a typeof
and null (or truthiness) check is required is because typeof null === "object"
. We need the additional test to prevent null
from sneaking into the Object.keys call and causing an error.
Testing for definedness
When writing a function that accepts a variable number of arguments (optional parameters), we sometimes want to check if a parameter is not undefined
✔ Compare the parameter's type against "undefined"
function variadic(param) { if (typeof param === "undefined") { console.log("Called with no arguments"); } else { console.log("Called with 1 argument"); } }
✔ Equality comparison against undefined
. This approach is also OK, with caveats (see below).
function variadic(param) { if (param === undefined) { console.log("Called with no arguments"); } else { console.log("Called with 1 argument"); } }
Keep in mind that undefined
here is actually a reference to the global window.undefined
property, not a primitive value. This means your code is vulnerable to accidental lexical closures if in-scope variable is named undefined
. (But who would do such a thing? Nobody, we hope.)
Looping through an object's properties
✔ Use Object.keys:
Object.keys(object).forEach(function(property) { console.log('property: ' + property); })
✘ Don't do it this way. While the code is technically correct, Object.keys
is clearer and more concise:
for (var property in object) { if (Object.prototype.hasOwnProperty.call(object, property)) { console.log('property: ' + property); } }
✘ Don't do this. It iterates through all properties in object
's prototype chain, which is almost never what you want:
for (var property in object) { console.log('property: ' + property); }
Attaching a particular this
-context to a function
✔ Use Function.prototype.bind:
var context = { myfield: "Hello world" }; var func = function() { return this.myfield; }; var boundFunction = func.bind(context); boundFunction(); // Hello world
is especially useful when dealing with event listeners, which need to be passed around to addEventListener methods but retain their this
function Widget() { this.clickCount = 0; document.getElementById("myButton").addEventListener("click", this.handleClick.bind(this)); // handleClick always gets the right "this" } Widget.protoype.handleClick = function() { this.clickCount++; alert("You clicked me " + this.clickCount + " times"); };
Throwing an error
✔ When throwing an error, use the Error
throw new Error("Something went wrong");
The reason for this is that the JS engine adds additional information to Error instances. For example, Errors have a stack
property that gives a backtrace of where the error occurred. This is valuable for debugging. You can also create a custom error type that inherits from Error
Do not throw strings or other objects directly! Without the extra info provided by Error
, it can be difficult for other programmers to track down the source of an error.
✘ Do not throw non-Errors:
throw "Something went wrong, but you may never see this message!";
Writing a class
The Orion approach to writing "classes" is fairly straightforward. It doesn't require any fancy libraries, just constructor functions and prototypes:
function Duck(name) { this.name = name; } Duck.prototype.greet = function() { console.log("Quack quack, my name is " + this.name); };
The mixin()
function from the orion/objects
module can make your code more compact when you have a lot of properties to add to the prototype. Using mixin
, the above example would look like:
function Duck(name) { this.name = name; } objects.mixin(Duck.prototype, { greet: function() { console.log("Quack quack, I'm a duck named " + this.name); } //, more properties here... });
To extend a class and add additional properties, we use a pattern similar to the code shown below. The subclass's constructor explicitly invokes the superclass's constructor to perform the usual Duck
initialization, and then performs some extra initialization specific to the subclass. Note how Object.create
is used to set up the desired prototype chain.
function SeaDuck(name, diveDepth) { Duck.call(this, name); // explicitly invoke super constructor this.diveDepth = diveDepth; } SeaDuck.prototype = Object.create(Duck.prototype); // extend super prototype SeaDuck.prototype.dive = function() { console.log(this.name + " dived to a depth of " + this.diveDepth); ;
In older code, you may see new
used to extend the superclass's prototype. This is an abuse of constructors, and creates a bogus instance of the superclass just to achieve the desired side effect of setting the prototype. Always use Object.create
instead. If you have to support non-ES5 browsers, write a beget utility or a shim for Object.create
✘ Don't use new
to extend a superclass's prototype object.
SeaDuck.prototype = new Duck();
Overriding methods
To override a method, we just add a method to the subclass's prototype having the same name as the superclass method:
// overrides Duck.protoype.greet SeaDuck.prototype.greet = function() { console.log("Avast, I'm a sea duck named " + this.name); };
JavaScript's prototypal inheritance also allows you to override methods on a per-instance basis, rather than per-class (although readers familiar with Java-style class models may find this surprising).
To invoke the superclass's implementation of an overridden method (the equivalent of the Java super
keyword), you need to explicitly call it using SuperConstructor.prototype.methodName.call(this);
SeaDuck.prototype.greet = function() { Duck.prototype.greet.call(this); // prints "Quack quack, I'm a duck named [whatever]" console.log("Just kidding, I'm a sea duck. Arrr"); };
Creating a dictionary
To create a dictionary (a map with string keys), we can use an object.
✔ Do this:
var map = Object.create(null);
✔ Or this:
var map = { };
Checking if a key is present
✔ Use hasOwnProperty
to determine if a given key is present in the map:
if (Object.prototype.hasOwnProperty.call(map, key)) { console.log("The key " + key + " was found!" }
✘ Don't use a truthiness check to determine key presence:
if (map[key]) { console.log("The key " + key + " was found!"); }
The truthiness check will give false negatives if the key's value happens to be falsey (like 0
or ""
✘ But even correctly handling falsey values won't save the []
operator. Don't do this either:
if (typeof map[key] !== "undefined") { console.log("The key " + key + " was found!"); }
While the code may appear correct, in many JS runtimes map[key]
will return a reference to the map's prototype object when key
equals the nonstandard property name __proto__
. This will again give a false positive. This can be checked for by testing the key against "__proto__", but it's easier to avoid the []
operator entirely and use hasOwnProperty instead. (Note: the code above is acceptable when the map was created using Object.create(null)
, which causes __proto__ to lose its magic and behave like a regular key.)
Writing an event emitter
Use the orion/EventTarget
mixin to inject event behavior into an object. After calling attach()
, your object implements the EventTarget DOM3 interface, and can dispatch events to listeners.
define(["orion/EventTarget"], function(EventTarget) { var myobject = {}; EventTarget.attach(myobject); myobject.addEventListener("coolevent", function(event) { console.log("Listener received an event"); }); myobject.dispatchEvent({ type: "coolevent" }); // Every event must have a type. Other fields optional. });
To add event behavior to a class, call attach
inside your constructor:
function Duck(name) { this.name = name; EventTarget.attach(this); } new Duck("Howard").dispatchEvent({ type: "quack" });
Asynchronous operations
Use orion/Deferred
to manage asynchronous operations.
XMLHttpRequest and Ajax
In most cases it's convenient to use orion/xhr
, which wraps the browser's native XMLHttpRequest into a promise API.
define["orion/xhr"], function(xhr) { xhr("GET", "/index.html").then( function(xhrResult) { console.log("Got response: " + xhrResult.response); }, function(xhrResult) { console.log("An error occurred: " + xhrResult.error); }); });
The returned promise resolves when it receives a response with a 2xx or 3xx status code, and rejects when the status code is 4xx or 5xx. It also rejects on network errors or timeouts. If you need finer-grained control over an Ajax request, xhr
will not be helpful: use a plain old XMLHttpRequest.
Orion provides a shim for the W3C URL API. Load the shim in your module, and then call the window.URL
constructor. You should always use the URL methods, rather than concatenate and encode strings by yourself.
✔ Do this:
define(["orion/URL-shim"], function() { var url = new URL("http://foo.com"); url.pathname = "example"; url.query.set("param1", "first value"); url.query.set("param2", "second value"); var myurl = url.href; // http://foo.com/example?param1=first%20value¶m2=second%20value });
✘ Don't do this:
var myurl = "http://foo.com/example?param1=" + encodeURIComponent("first value") + "&" + encodeURIComponent("second value");
Consider also using the orion/URITemplate
module, which implements the URI Template spec.
Resolving URLs
✔ Use the 2-argument form of the URL constructor: new URL(input, base). The URL is then created by resolving the relative URL input against base. To resolve a URL against the location of the current page (like how an <A> tag would), simply pass window.location.href as the second parameter.
define(["orion/URL-shim"], function() { new URL("aaa", "http://example.org/foo/bar/baz.html").href; // "http://example.org/foo/bar/aaa" new URL("../bbb", "http://example.org/foo/bar/baz.html").href; // "http://example.org/foo/bbb" new URL("/ccc", "http://example.org/sup").href; // "http://example.org/ccc });
The DOM location of service objects is not defined. All services must be accessed via the service registry using the service's symbolic id. While concrete service implementations may be found in the DOM, such object names are not API and are subject to change at any time. Service symbolic ids follow the Reverse DNS naming convention (like Java packages).
Server requests and responses are represented by default as JSON objects. Object member names use title case ("Name", "ChildrenLocation", etc).
JS Documentation
All JavaScript API uses JSDoc syntax (currently version 2). Orion code is written as self-contained AMD modules that don't expose global objects. So rather than tagging the actual objects with documentation, we tend to use JSDoc tags to construct virtual "classes" corresponding to a module's exported API. These virtual tags can appear almost anywhere in the source code, but it's best to keep them close to the functions that they represent.
The main doc tags used are:
- @param
- Documentation of method parameters. See TagParam in the jsdoc wiki for further details.
- @name
- For specifying class names. The class name should match the RequireJS module name.
- @lends
- For specifying what class a prototype contributes to
- @class
- Indicates that this comment represents a class definition. The text of the @class tag becomes the class description.
- @description
- A detailed description of what this class, function, or field does. The first sentence will be used as a one-line summary of the class constructor. If @description is not given explicitly, the header of the comment is used.
See also the complete JSDoc tag reference.
Documenting a class
JSDoc doesn't distinguish between class documentation and constructor documentation: they both go into a single comment block. So to document a class, we have a comment containing @name
, @class
, and @description
tags, plus all the normal method tags (@param
and so on).
- /**
- * @name orion.MyClass
- * @class Class header summary of MyClass.
- * @description One-line summary of MyClass.
- * Longer description of MyClass, which can include code examples (in <code> tags),
- * HTML and links to other docs (using @see). This also serves as documentation
- * for the constructor function.
- *
- * @param {String} param1 The widget to frob.
- * @param {Boolean} [param2=false] Whether the flux capacitor should be quuxed.
- */
- function MyClass(param1, param2) {
- // ...
- }
Because JavaScript's prototypal inheritance doesn't quite fit the class-oriented model of JSDoc, there's a few different approaches for documenting methods, depending on how your "class" is built. The general pattern for a method uses the tags:
- @name
- Gives name of this method. This can be fully-qualified (in which case
indicates static membership, and#
indicates instance membership). - @function
- Tells JSDoc we're documenting a function (otherwise it will assume a field).
- @param {Type} paramName comment
- Documents a required parameter. May appear multiple times.
- @param {Type} [optParamName=defaultValue] comment
- Documents an optional parameter, and its default value. May appear multiple times.
- @returns {Type} comment
- Documents the function's return value.
You may also come across these tags in the source. Note that both of these tags are made redundant by proper use of @name and @function:
- @memberOf
- Writing
@name orion.MyClass#foo
is equivalent to combining@name foo
and@memberOf orion.MyClass.prototype
- @methodOf
- Equivalent to combining
Per-instance method
If you're injecting methods into each instance of your class, a method doc comment looks like this:
- function ServiceTracker() {
- /**
- * Returns service references to the services that are being tracked.
- * @name orion.ServiceTracker#getServiceReferences
- * @function
- * @returns {orion.serviceregistry.ServiceReference[]} References to all services that
- * are being tracked by this ServiceTracker.
- */
- this.getServiceReferences = function() {
- // ...
- };
- }
Prototype method (mixin lends)
This is for when your class mixes in (or directly uses) an object literal for its prototype. We tag the object with @lends
, causing JSDoc to add all the literal's fields into the class prototype. Our method docs can then omit the @function
and @name
tags, because JSDoc already knows the containing class and the type of member.
- function ServiceTracker() {
- // ...
- }
- ServiceTracker.prototype = /** @lends orion.MyClass.prototype */ {
- /**
- * Called to customize a service object being added to this ServiceTracker.
- * @param {orion.serviceregistry.ServiceReference} serviceRef The reference to the service being added.
- * @returns {Object} The service object to be tracked for the given service reference.
- */
- addingService: function(serviceRef) {
- // ...
- }
- //, ... Additional prototype methods can go here.
- };
Prototype method (assignment)
When assigning methods directly to our class's prototype, JSDoc falls over somewhat, and can't infer the containment or the member type. So we need to explicitly place each method into the class using @name
, and function-ify it with @function
- function ServiceTracker() {
- }
- /**
- * Called to customize a service object being added to this ServiceTracker.
- * @name orion.ServiceTracker#addingService
- * @function
- * @param {orion.serviceregistry.ServiceReference} serviceRef The reference to the service being added.
- * @returns {Object} The service object to be tracked for the given service reference.
- */
- ServiceTracker.prototype.addingService = function(serviceRef) {
- // ...
- };
Static methods
To indicate that a method is static, make it a member of the class itself, rather than the class's prototype:
- /**
- * @name orion.Deferred.all
- * @function
- */
- function all(promises) {
- // ...
- }
JSDoc also has a @static
tag, but you probably won't need it.
One helpful feature of JSDoc is types, which can appear inside the curly braces of a @param
or @return
tag, and a few other places as well. When the JSDoc parser recognizes a reference to a type, it turns it into a clickable hyperlink in the HTML documentation, which is very nice.
A type can be a fully-qualified class name, or a global JavaScript object like Boolean, String, Function, etc. The Orion source generally uses the capitalized object wrapper names to refer to primitive types: for example we say String
rather than string
, Boolean
rather than boolean
, and so on.
JSDoc also understands array types: append []
after the name of any type to indicate that it's an array.
* @param {orion.Promise[]} promises
To indicate that a param or return type takes one of a set of types, join the types with a |
* @param {String|Element} elementOrDomId
When a type can be "anything", we represent it as {Object}
While most validation problems raised by ESLint are helpful, there are some situations where they need to be ignored. In these cases, add an /*eslint-env ... */
comment block near the top of your code that turns off the problems. A sample eslint-env block might look like:
/*eslint-env no-unused-params:0*/
Here's a list of common warnings and how to deal with them:
- '{variable}' is already defined.
- This warning is raised when you define the same variable more than once within a function. Usually this results from a misunderstanding of JavaScript's scoping rules. In JavaScript,
is scoped to the enclosing function, not the enclosing block. When two blocks define the same variable, it is redundant and may mislead readers as to the underlying language semantics. To eliminate this warning (and make your code clearer), move the variable definition outside the blocks.
✘ Don't redefine a variable inside a block:
if (condition) { var x = 'foo'; } else { var x = 'bar'; // 'x' is already defined }
✔ Define the variable outside the block instead:
var x; if (condition) { x = 'foo'; } else { x = 'bar'; }
It's OK to define a variable inside a block if the variable is not referenced anywhere else in the function.
JavaScript Formatting
- The Orion codebase isn't totally consistent in its formatting style. When changing a file, try to maintain the same formatting style that the rest of the file uses.
- Please, do not use the JavaScript code formatter provided by Eclipse's JavaScript Development Tools (JSDT).
- Feel free to fix sloppy or unclear formatting when you find it, but please keep this work in a separate commit from any functional changes.
None of the HTML in Orion is API. The existence and contents of any HTML file is subject to change at any time. If you see something in Orion's HTML files that you need in your application, make your own copy.
HTML Fragments and Templates
When writing a visual component, it's often convenient to define a fragment of HTML that serves as the component's template. There are several ways to go about this. This section discusses when you might want to use the different approaches, and what our long term direction is.
Where is the fragment defined?
A fragment is a little chunk of HTML that describes a component. There are a few choices in the mechanics of where a fragment is defined.
- A fragment could be defined in js code as a string. Consider this snippet from the dialog prototype:
- Dialog.prototype.TEMPLATE = '' +
- '<div class="dialog" role="dialog">' + //$NON-NLS-0$
- '<div class="dialogTitle layoutBlock"><span id="title" class="dialogTitleText layoutLeft"></span></div>' + //$NON-NLS-0$
- '<div class="layoutBlock"><hr/></div>' + //$NON-NLS-0$
- '<div id="dialogContent" class="dialogContent layoutBlock"></div>' + //$NON-NLS-1$ //$NON-NLS-0$
- '<div id="buttons" class="dialogButtons"></div>' + //$NON-NLS-1$ //$NON-NLS-0$
- '</div>'; //$NON-NLS-0$
An advantage of this approach when using Orion to edit fragments is keeping the fragment close to the JavaScript code that is manipulating it or making assumptions about its structure. This is, of course, a tooling issue (not having split editors, or popping up references, etc.) The disadvantage is the noise created by concatenation, $NON-NLS markings, etc. This kind of code also tempts the developer to directly concatenate NLS message strings into the content, and that is a security risk (described later).
- <footer id="footerContent" class="layoutBlock footerLayout" role="contentinfo">
- <!-- Requires some js code to bind the NLS variables -->
- <div class="footerBlock">${Orion is in Beta. Please try it out but BEWARE your data may be lost.}</div>
- <div class="footerRightBlock">
- <a href="http://wiki.eclipse.org/Orion/FAQ" target="_blank">${FAQ}</a>
- <a href="https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Orion&version=1.0" target="_blank">${Report a Bug}</a>|
- <a href="http://www.eclipse.org/legal/privacy.php" target="_blank">${Privacy Policy}</a>|
- <a href="http://www.eclipse.org/legal/termsofuse.php" target="_blank">${Terms of Use}</a>|
- <a href="http://www.eclipse.org/legal/copyright.php" target="_blank">${Copyright Agent}</a>
- </div>
- </footer>
- define(['i18n!orion/nls/messages', 'require', 'text!orion/banner/footer.html'],
- function(messages, require, FooterFragment) {
- // excerpt from common page code
- var footer = document.getElementById("footer"); //$NON-NLS-0$
- if (footer) {
- footer.innerHTML = FooterFragment;
- }
- });
A fragment can also be defined inside a template element. Orion provides a shim for the draft HTML Templates API that lets you define template elements. Templates are a promising approach for the following reasons:
- If you use the fragment many times on a page, using a template means it will be parsed only once.
- Once parsed, you can make common, dynamic modifications to the template (binding variable values, etc.) only once.
- The DOM structure behind a template is not associated with the document until a node is actually cloned. This means that if the template references network resources (such as an image URL), there will be no attempt to download the image until the template is actually cloned into a node and placed in the document.
If we used a template for the footer fragment, it would look like this instead. (Using a template for a footer that appears once is not necessarily a good idea, but this is shown to illustrate the difference in approach:)
- <template id="footerTemplate">
- <footer id="footerContent" class="layoutBlock footerLayout" role="contentinfo">
- <!-- Requires some js code to bind the NLS variables -->
- <div class="footerBlock">${Orion is in Beta. Please try it out but BEWARE your data may be lost.}</div>
- <div class="footerRightBlock">
- <a href="http://wiki.eclipse.org/Orion/FAQ" target="_blank">${FAQ}</a>
- <a href="https://bugs.eclipse.org/bugs/enter_bug.cgi?product=Orion&version=1.0" target="_blank">${Report a Bug}</a>|
- <a href="http://www.eclipse.org/legal/privacy.php" target="_blank">${Privacy Policy}</a>|
- <a href="http://www.eclipse.org/legal/termsofuse.php" target="_blank">${Terms of Use}</a>|
- <a href="http://www.eclipse.org/legal/copyright.php" target="_blank">${Copyright Agent}</a>
- </div>
- </footer>
- </template>
The JavaScript would look like this:
- define(['i18n!orion/nls/messages', 'require', 'orion/HTMLTemplates-shim' 'template!orion/banner/footer.html'], // note use of template! not text!
- function(messages, require) {
- var template = document.getElementById("footerTemplate");
- var footer = document.getElementById("footer"); //$NON-NLS-0$
- if (footer) {
- footer.appendChild(template.content.cloneNode());
- }
- });
The example above assumes that we have a requirejs plugin for parsing html files and building a template element. (Bug 395402). Until we have such a plugin, the template would have to be marked up in the page itself.
Inserting text fragments
As mentioned above, a template element is preferred for fragments repeated many times, or to delay/reduce the network traffic associated with associating a fragment with a document. However, our tooling for letting library modules bring in templates is not complete here. If you are using text fragments in library modules, or you have a "one-off" fragment, it is reasonable to insert the fragment into the DOM without using a template element. Suppose you have a text fragment you want to insert into the document. (You could have created this text fragment in the JavaScript as a string or imported as a text!myFragment module. It doesn't matter.)
To get it into the document you have several approaches:
If you have a node that is exclusive to the fragment, you can use the innerHTML of the node. The original footer example showed this.
- define(['i18n!orion/nls/messages', 'require', 'text!orion/banner/footer.html'],
- function(messages, require, FooterTemplate) {
- // excerpt from common page code
- var footer = document.getElementById("footer"); //$NON-NLS-0$
- if (footer) {
- footer.innerHTML = FooterTemplate;
- }
- });
Of course, if there were any other nodes inside the footer DOM node, they would be removed.
Another approach, useful when you are adding a fragment to a parent and don't want to remove other child nodes, is to create a contextual fragment and then add it into the DOM as you would any other node. For example, this code adds a banner fragment as the first child of the document body.
- define(['i18n!orion/nls/messages', 'require', 'text!orion/banner/banner.html'],
- function(messages, require, BannerFragment) {
- var range = document.createRange();
- var parent = document.body;
- range.selectNode(parent);
- var headerFragment = range.createContextualFragment(BannerFragment);
- if (parent.firstChild) {
- parent.insertBefore(headerFragment, parent.firstChild);
- } else {
- parent.appendChild(headerFragment);
- }
- });
Security and Data Binding
Often, you'll want to bind some data to a fragment. For example, you might want to describe some text using its i18n/NLS message key name, which comes from a plug-in. Or perhaps the text in the template depends on some JSON property on the server. Regardless of how you created your fragment, you'll need to ensure that these strings are not bound directly as text in a fragment. They should be inserted as text nodes in the DOM. This restriction is necessary so that plugins or other external sources cannot insert arbitrary HTML into the page.
Do not concatenate variable strings into a fragment and insert into the document. Instead, a data binding approach should be used to refer to the values by their key. Strings can be bound to a fragment using the processTextNodes function in 'orion/webui/littlelib'. This utility allows you to refer to the strings in your template using variables, and DOM API will be used later to bind your string to the text node in the HTML. You may have noticed in the Orion footer example, we use ${variable} syntax in the markup. The user of such a fragment is responsible for providing the correct data binding object for processing the DOM elements.
- define(['i18n!orion/nls/messages', 'require', 'orion/webui/littlelib','text!orion/banner/footer.html'],
- function(messages, require, lib, FooterTemplate) {
- // excerpt from common page code
- var footer = lib.node("footer"); //$NON-NLS-0$
- if (footer) {
- footer.innerHTML = FooterTemplate;
- // do the i18n string substitutions
- lib.processTextNodes(footer, messages);
- }
- });
This data binding approach can be used for any object, not just NLS message objects. When the DOM tree text nodes are processed, any nodes that have the ${variable} syntax will be considered to describe a property name. The text nodes containing the variables will be replaced with nodes containing matching property value found in the bound object. If no matching property is found, the original text remains.
We use the Orion editor to author .css
files. Orion provides a built-in CSSLint validator which flags potential problems in your stylesheets.
CSS file structure
For each Orion page {page}.html
, there is generally an associated {page}.css
file in the same folder. The page CSS is for that page only, and should not be reused. Any reusable styling for common visual components should go in the bundles/org.eclipse.orion.client.ui/web/css/
Any stylesheets required by your page CSS should be loaded using @import
. These @imports can be inlined by a CSS optimizer like RequireJS's. Do not @import external stylesheets (http://...), as those cannot be inlined.
Style tips
Use DOM structure selectors with caution
CSS provides a number of powerful selectors that act upon the DOM structure, like the type, descendent, and child selectors.
While these can be helpful, their overuse produces a page whose style is brittle and tightly coupled to the DOM structure, such that simple refactorings (adding a wrapper DIV around child elements, for example) completely break the page.
✘ A rule with many DOM selectors is brittle:
.mytable > div > div > label > span { font-weight: bold; }
✔ Something like this is better:
.mytable span.emphasis { font-weight: bold; }
Don't use unqualified tag selectors
The type selector matches any element with the given HTML tag name. In complex pages, these are prone to matching too much. They also hinder refactoring, as your stylesheet cannot safely be made reusable, and a reader of either the HTML or CSS has a difficult time determining just which elements you intend to match. Instead, consider a class selector.
✘ Don't use selectors that match an entire class name:
div { background-color: red; }
✔ Instead, create a class selector that targets only the elements you want:
div.myclass { background-color: red; }
The html and body elements are exceptions.
Avoid the ID selector
While necessary in the DOM, IDs should be avoided in stylesheets. IDs are the DOM equivalent of singletons, and targeting them prevents your styles from being reused on other elements. IDs should be regarded as an internal property of the HTML (or JavaScript), not an exported symbol to be styled. Instead of an ID, apply a class name to the DOM element you're trying to style, and use a class selector.
✘ Instead of this:
#content-title { text-shadow: white 0 1px 2px; }
✔ …Use a class:
.content-title { text-shadow: white 0 1px 2px; }
Java code on the server follows the standard Eclipse Naming Conventions. The project short name is 'orion', so all server bundles and packages start with org.eclipse.orion.
Provisional API packages use the x-internal or x-friends manifest attribute, and do not have internal as a segment in the package name. Use of PDE API tools is highly recommended to ensure you are using supported API.
The Orion server source code is licensed under the Eclipse Public License. The standard Eclipse copyright header should be used for these source files.
The Orion client code (HTML, CSS, JavaScript), is licensed under both the Eclipse Public License and the Eclipse Distribution License. Here is a sample copyright header to use in JavaScript source files:
/******************************************************************************* * Copyright (c) <date> <contributor name> and others. * All rights reserved. This program and the accompanying materials are made * available under the terms of the Eclipse Public License v1.0 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). * * Contributors: <contributor name> - initial API and implementation ******************************************************************************/


text:
James F. was the first to submit this loveliness, and as you can see it's from the one and only Pink Cake Box.
This next one may be more "bare bones", but I think it's rather classic:
Lynn H. actually sent this to me a looong time ago as a Wreck, but I liked it well enough to keep it stashed in the "Sweets" file ever since. It's from Craig's Cake Shop in Wisconsin.
I hear this next one was recently featured on the WE channel's Amazing Wedding Cakes:
It was made by Lauri Ditunno of Cake Alchemy, and Julie T. was the first of many to send it in.
I find this one particularly cool because I think that web was made using a new flexible icing product I saw at last year's ICES convention. You pipe it out as a runny icing, but once it dries it's like an edible rubber - you can pick it up, stretch it, etc. John and I had some fun at the product table playing with the stuff, but this is the first professional cake I've seen using it - assuming that's what it is, of course.
Sorry, sorry - geek out time over. Next!
I'm an art lover, so you know I have to feature at least one Day of the Dead cake:
Looove the colors! This was Janet S.'s wedding cake, and it was made by Kathy of Calamity Cakes.
Check out this dark beauty:
It took me a second to realize, but those tiers are actually coffin-shaped! See, I love that this is such a unique, gothically-inspired cake without being in-your-face. Also, this just might be my favorite Day of the Dead inspired topper ever:
And yes, it was custom-made: Carrie, the bride, wore a red gown and has purple and red hair. [sing-song voice] Gor-geous!!
This was submitted anonymously, but Mistress Carrie (she's a DJ for WAAF in Boston) was kind enough to e-mail me recently with more details. The cake was made by Something Sweet by Michelle, and the custom topper by Skellramics.
Here's another red, black & white gem:
Sabrina found it here, and it's made by Rosebud Bakery. Thanks to all of you in the comments who helped track it down!
And you know I always save my favorite for last, right? Here ya go:
Honestly, I think I'm in love with this cake. Who knew a skull topper with a spider on it could be that adorable? And I love the soft colors, the airbrushing, the purplish black roses - it's really all quite exquisite. What's even better is that this was made by the author of the blog Pumpkin And Spice for her advanced cakes class - she's still in culinary school! Many thanks to Meghan L. for finding it.

subset: commoncrawl


text:
Wednesday, December 9, 2009
Boxty and Latkes - Outdoor Wednesday
Parade to welcome Sinterklaas on December 6th
Latkes - Potato Pancakes
Boxty - Irish Potato Cakes
The winter solstice is nearly here and since recorded time we've tried to beat back its darkness with celebrations of light and fire. We adorn our trees and windows with lights and flickering tapers fill our homes with an artificial warmth and glow. The Jewish Festival of Lights, Hanukkah, begins next week and I realized I had posted no recipes for those who enjoy this wonderful celebration. I don't have many, but the ones I do have should be shared with others. My plan was to post recipes for latkes and tzimmes. Then, in what my Mother would call a flight of fancy, I started thinking of my paternal grandmother. Grandma Maude was Irish and more Catholic than the pope. I've mentioned in other posts that she spent her year traveling to the homes of her many children. She managed to rotate the dates so we all got to enjoy her company for one holiday or another. Well, today, in the middle of solstice preparations, memories of Lent with Grandma Maude came flowing back. Very old school, she was into what we, years ago, called the Black Fast. The particulars of that practice are best left to those of us who lived through it, but memories of Maude and days of fast and abstinence were triggered by, of all things, latkes. Grandma Maude had a very Irish recipe that she was convinced would carry the most egregious heathen through the Lenten season. Her weapon was boxty, an Irish potato cake that is far more dense than a latke. I'll tell you up front that it is delicious, but the only thing it has in common with a latke is its main ingredient, the potato. I learned to make latkes in the German and Scandinavian kitchens of my childhood. The goal was always a lacy, thin creation that when held to the air would allow the passage of light and not bend. Sounds easy, but it's not. My knuckles were regularly rapped until I perfected the technique. Maude's pancakes were thick and heavy and more likely to break a toe than emit light. The thing was, they were delicious and as good in their own way as those made by Hannie or Mrs. P. So, I decided to postpone the tzimmes and share both pancake recipes with you today. Here they are. Heritage demands the boxty come first.
Boxty - Irish Potato Cakes...from the kitchen of One Perfect Bite
1 cup unseasoned mashed potatoes
1 cup peeled and grated raw potatoes
2 cups self-rising flour
1/4 teaspoon salt
1-1/2 cups buttermilk
2 to 4 tablespoons butter
1 to 2 tablespoons vegetable oil
Sour Cream
1 ) Combine mashed and raw grated potatoes in a medium bowl. Mix well. Stir in flour and salt with a wooden spoon. Stir in buttermilk. Mixture should resemble a light bread dough.
2) Heat 2 tablespoons butter and 1 tablespoon oil in a large heavy bottomed skillet. When mixture is sizzling, drop batter by 1/4 cup measures into pan. Working in two batches, brown cakes, about 4 minutes per side, until they are a rich golden brown. Serve hot with additional melted butter and sour cream. Yield: 12 potato cakes.
Latkes - Potato Pancakes
2 cups peeled and grated russet or Idaho potatoes
1 small yellow onion, peeled and grated
1 large, egg beaten
3 tablespoons self-rising flour
Pinch of nutmeg
Salt and pepper
2 to 4 tablespoons vegetable oil for frying
Sour cream
1) Squeeze as much water as possible from grated potatoes. Toss in a large bowl with grated onion. Add egg, flour, nutmeg and salt and pepper to taste.
2) Heat oil in a large saute pan set over medium-high heat. Drop mounds of potato mixture into oil and pat to a thin circle. Cook, about 4 minutes per side, until golden brown. Serve hot with sour cream or applesauce. Yield: 12 pancakes.
This is being linked to:
Outdoor Wednesday - A Southern Daydreamer
67 comments :
- Vanessa Greenway said...
Oh! Everytime I see you blog banner, makes me hungry! I can even taste those pepers... These cakes look delicious as well! Have a great day! Vanessa
- December 9, 2009 at 1:37 AM
- Carolyn Ford said...
I am starving! And, those look fabulously delicious!
- December 9, 2009 at 2:10 AM
- Deb in Hawaii said...
Both potato cakes look delicious--I love latkes and now I want to try a boxty. Fun!
- December 9, 2009 at 4:20 AM
- culinario86 said...
Potato Pancakes are amazing...thanks for the recipe!
- December 9, 2009 at 5:25 AM
- Valerie Harrison (bellini) said...
Thanks for the reminder about boxty. I also make 'tattie scones or Irish farls which are also made with potato and very good for breakfast.Our grandma would be bursting with pride.
- December 9, 2009 at 5:56 AM
- Barbara said...
The recipes sound good and I am sure they are. I always marvel at the variety of ones you post. I am on a diet now, but I have saved several I hope to try one day.
Barbara - December 9, 2009 at 6:04 AM
- Martha said...
Growing up, potato pancakes were always made with leftover mashed potatoes -- and eaten for breakfast! They are delicious!
- December 9, 2009 at 6:21 AM
- KathyB. said...
I love the line.."here they are, heritage demands boxty comes first.."you do make them sound very good and on these cold, cold days ,filling enough to hold one over all day while working in this cold.
I very much appreciate the history and stories behind your recipes. - December 9, 2009 at 6:41 AM
- Diary From Africa said...
What lovely recipes, Mary - and as usual, your photo's are an absolute delight ! I love the garnish/topping for the potato pancakes ....
- December 9, 2009 at 6:42 AM
- Hootin Anni said...
My mom used to make potato pancakes all the time....and I loved 'em!!
That photo of Sinterklass is outstanding Mary!!!!
My Wednesday post is published. Do come by if you get a chance...it's all Gulf Waters today. Found HERE - December 9, 2009 at 6:54 AM
- NKP said...
It must be spud day!
I have always wanted to try latkes. Yours look wonderful! - December 9, 2009 at 8:17 AM
- Joyce said...
Your story of your GM being "mopre Catholic then the Pope" made me laugh out loud. I can understand that statement. Now the latkes are light and airy but I have never heard of the Irish version but had to laugh when you said they were heavier in texture as I always think Irish cooking is heavy (well at my Dad's family it was). thanks for sharing the recipes. I need homemade applesauce with my lates and some sour cream too. Yum!
Joyce - December 9, 2009 at 8:54 AM
- George Gaston said...
Mary, 'tis the season and your post about the various celebrations of light, brings it all together. How the different cultures have unique yet similar dishes. Don't think I have ever had Boxty - Irish Potato Cakes. I'll have to dry these. Thanks
- December 9, 2009 at 9:24 AM
- D said...
Potato pancakes, oh my. Mary, that was the way we used leftover mashed potatoes: an egg, little flour, salt and pepper. My grandmother used to do something similar to the Latkes with the grated potatoes (no nutmeg), and another dish (but only for special occasions because it took so much time) the grated potatoes were baked with chicken and bacon fat! So that's what is wrong with my legs. ;)
- December 9, 2009 at 9:41 AM
- Cathy said...
Those latkes are making my mouth water, Mary. Our freezing cold weather makes these stick to the ribs recipes so appealing. Looks like we've set a low temperature record today.
- December 9, 2009 at 9:44 AM
- Inspired by eRecipeCards said...
Latka's are my Christmas morning meal. Has been for years. I have never heard the see through lattice goal. Mine has more egg, and more flour, so it comes out more cakey.
WONDERFUL photos - December 9, 2009 at 9:45 AM
- SmilingSally said...
Oh, Mary, you've made me so hungry for potatoes!
- December 9, 2009 at 9:47 AM
- Shellbelle said...
I've helped grate the potatoes for latkes many times, but my friend's mother and aunts always did the rest. I do love them and should make them myself one day.
But I know I need to try your Boxty recipe in honor of my Irish heritage, it would make my grandmother proud. - December 9, 2009 at 10:22 AM
- My Little Space said...
Gosh, these look so scrumptious! Just imagine it with some nice sauce... drooling!
- December 9, 2009 at 10:49 AM
- storyteller said...
How DELICIOUS these holiday treats look! Methinks it's time for ME to stop blogging and fix something to eat ;-)
Hugs and blessings,
Sacred Ruminations - December 9, 2009 at 11:04 AM
- Unknown said...
these are making me hungry--for a midnight snack! argggh!
- December 9, 2009 at 11:21 AM
- Wandering Chopsticks said...
I've never known the Irish had potato pancakes. I don't know why when of course, they would! I like the combo of mashed and raw potatoes.
- December 9, 2009 at 12:28 PM
- The Quintessential Magpie said...
Mary, I am starving! It's past noon, and I haven't had breakfast, much less lunch, yet. Maybe I can have it vicarously through reading your blog. Do you think?
Happy Outdoor Wed!
Sheila :-) - December 9, 2009 at 12:52 PM
- theUngourmet said...
Wow the making of Latkes sounds like very serious business. They look like they are worth the effort. Delicious!
Hope you are keeping warm! - December 9, 2009 at 1:16 PM
- Neel | Learn Food Photography said...
The photograph of stacked cakes with whipped cream (?) and cilantro (?) on top of the cakes is wonderful. I think stacking is a great technique widely used in photographing food.
- December 9, 2009 at 1:17 PM
- Lenore said...
Oh, Mary, that story about your grandmother is so precious and you are a gifted story teller!
Love the latkes....I'm of German heritage also and my mother made them mouth watering!...I've made them a few times and they were good but not like my mother's.
Wanted to become a follower but don't see where on your site to do that. - December 9, 2009 at 1:36 PM
- Mary Bergfeld said...
Leonore, it is just to the right of the Latkes. I do hope you join us.
- December 9, 2009 at 1:48 PM
- Maria said...
We love making latkes! Great post!
Two Peas and Their Pod moved to WordPress, please update your readers, etc. Our address is: http://www.twopeasandtheirpod.com Come visit us and check out our new look! Thanks for following us! - December 9, 2009 at 2:20 PM
- Claudia said...
Aren't potatoes just wonderful? I am partial to latkes. You are making me want some right now.
- December 9, 2009 at 2:23 PM
- aspiritofsimplicity said...
I love that this Boxty could send a heathen into heaven! It sounds like she and my nana would get along just fine!
- December 9, 2009 at 2:24 PM
- Claudia said...
Miss Mary-- thanks so much for stopping at my blog-- I love your sweet comments as you always brighten my day.
Ummm I am on a carb free world right now... i know *sigh* its tough seeing your wonderful temptations.
Enjoy the season dear one.
Claudia ♥ ♥ - December 9, 2009 at 2:29 PM
- bj said...
I love potato pancakes and potato bread...well, I suppose I like anything made with potatoes!!:)
Thanks for sharing the recipes... - December 9, 2009 at 3:27 PM
- Beverly said...
Mary, I love latkes and sour cream, and these look delicious. They are perfect to me on cold days.
- December 9, 2009 at 3:54 PM
- Cindy (Applestone Cottage) said...
This is such a cool post! My hubby loves potato pancakes and this recipe is one I will try! Love the pic of Santa, he looks so regal! Cindy - December 9, 2009 at 5:12 PM
- The Blonde Duck said...
I've never heard of boxtys, but I want to try them!
- December 9, 2009 at 5:32 PM
- eileeninmd said...
I love the photo of Sinterklaas. Your Latkes and Boxty look delicious, hubby would love all these potatoes recipes. He has been cooking lately I will give him the recipes.
- December 9, 2009 at 5:44 PM
- LV said...
It is so hard to see all your goodies and cannot have them. I know there is nothing you cannot fix that would be out of this world.
- December 9, 2009 at 6:11 PM
- Atticmag said...
I've never heard of boxty but they look absolutely amazing and I'm definitely copying the recipe to try. Thanks for stopping by to take our walking tour. Happy holidays (as she goes off to copy and paste...) -- Jane F.
- December 9, 2009 at 6:13 PM
- D said...
Mary, hi again. Hope you don't mind, but I referenced your blog on a Guacamole post today. Have a wonderful evening.
- December 9, 2009 at 6:14 PM
- Patricia @ ButterYum said...
I could eat a platter of Latkes. They are so delicious!!!
I love that you always remind me of the forgotten classics.
ButterYum - December 9, 2009 at 6:15 PM
- sherri@lavenderfields said...
Thank you for this recipe. I have never made Latkes before. We were just talking about them the other day at work! They look so wonderful! Hope you are having a great Wednesday and that you enjoy the rest of your week! Sherri : )
- December 9, 2009 at 6:26 PM
- Thistlemoon said...
Wow, both of the potato dishes look amazing! I could go for some of each right now! Happy Solstice!
- December 9, 2009 at 6:37 PM
- Selba said...
Both of potato recipes are wonderful!
- December 9, 2009 at 7:00 PM
- Susan C said...
My grandmother often made the "Irish pancakes." I didn't know they were called boxties.
Every year I tell myself that I don't have to wait to be invited to a Hanukkah party to eat latkes, but I still haven't made them yet. - December 9, 2009 at 7:01 PM
- JLS Hall (Joysweb) said...
Both those recipes sound delicious! (As always!) Never heard of "Boxty" before, but I've tried many times to make a decent potato pancake, and failed miserably every time. Guess I needed someone to rap my knuckles for me when I was trying to learn! Thanks for another great (and informative) post.
- December 9, 2009 at 7:09 PM
- Bettey said...
Thank you for stopping by my blog and commenting. I love being visited by other Oregonians, as I think you are from your links. Your blog is yummy!!!
- December 9, 2009 at 7:43 PM
- La Table De Nana said...
Oh lala he stood out at FWS..and here w/the pancakes and latkes and story:)
Moreso... - December 9, 2009 at 8:29 PM
- Unknown said...
I copied your latkes reciop as they sound absolutely scrumptious! Thanks so much!
- December 9, 2009 at 8:38 PM
- Allie said...
I grew up eating latkes, but have never had boxty before. I'm going to have to make these soon!
- December 9, 2009 at 9:05 PM
- Mollye said...
Oh Mary what a delightful post. I am forwarding a link to your blog to my priest as I know he would love your post today. Hugs, Mollye
- December 9, 2009 at 9:55 PM
- CailinMarie said...
oh my! did they really wrap your knucles if your cakes were too heavy? dear me!
I love hearing about your grandmother who is more catholic than the pope. I miss the catholic church (I married an episcopalian) and I LOVE LOVE LOVE your photo of St. Nikolaus - I lived in germany for a bit growing up and the town's St. Nik visited my home on Dec 6th and I didn't understand a word he said to me but I remember it. And I remember my neighbor hiding under her table when Peter, with the coal, enacted by a friend of her father's, stopped in for a drink as our two houses were the last in town! - December 9, 2009 at 10:09 PM
- Pam said...
I always end up learning something new from your posts Mary. Your Grandma Maude sounds like a great lady. The latkes and boxtys look so tasty - I love anything made of potatoes.
- December 9, 2009 at 10:19 PM
The Irish latkes are new to me and they sound absolutely perfect for our family as we are getting ready for Hanukkah and planning our summer stay in Dublin while listening to my daughter's favorite Celtic Woman :)
- December 9, 2009 at 10:25 PM
- Mary Bergfeld said...
5 Star Foodie, you will love Dublin. Remember the boxty is a cake, not a pancake. With good butter or thick sour cream they will bring you to your knees.
- December 9, 2009 at 10:31 PM
- Ebie said...
Thank you for sharing the recipes. Coming from a different culture, I have experienced latke when I was working part time for a Jewish elderly couple. Since I did not know anything, I used the commercial box ones! Hehehe!
My daughter loves potatoes! I will try to make them!
Your posts always make me hungry! - December 9, 2009 at 11:33 PM
- ❀~Myrna~❀ said...
Great Santa Claus Pic ! Both recipes look & sound delicious .
Merry christmas !
~Myrna - December 10, 2009 at 6:13 AM
- Pam said...
My daughter was just asking me to make these yesterday and I needed a recipe. Thanks!
- December 10, 2009 at 1:23 PM
- Ms.Daisy said...
I so love the jewel tones of those peppers! One of my favorite summmertime dinners is fried peppers, onions and crushed fresh tomatoes. I can't take credit for this "hash" because it was my Mother's. We always had it with fresh baked Italian bread. Ummmmm!
Thanks for stopping by and saying 'hi' on OW.
~Jean - December 10, 2009 at 1:51 PM
- June said...
MMM they look wonderful! Both of these would be great party food miniatures as appetizers, wouldn't they?
- December 10, 2009 at 4:11 PM
- Mary Bergfeld said...
June, they'd be delicious but an awful lot of work to make as miniatures.
- December 10, 2009 at 4:32 PM
- Katy ~ said...
Latkes I've made, boxty I have not. I am going to make boxty. I even like the word!
- December 10, 2009 at 8:01 PM
- free online adventure games said...
More potato recipes! Latke reminds me of the Swedish rosti - something I really like served with some bacon and sour cream. But I'll try latke instead. Both these recipes feel like such simple but great breakfast fares.
- December 10, 2009 at 9:50 PM
- Lori said...
Latkes I have a lot but I have never heard of boxty.
More Catholic than the Pope. Wow. That is very seriously Catholic. God Bless her. - December 10, 2009 at 9:50 PM
- Sarah said...
Mary, what a "delicious" post! Thanks for these recipies. I'll give them a try. And thanks for sharing this event for Outdoor Wednesday. ~ Sarah
- December 11, 2009 at 11:27 AM
- Jamie said...
I love latkes but I think I'll love boxty maybe even more! They look so thick and toothsome and flavorful! Must try these this Hanukkah in place of my regular latkes. Methinks husband and sons may enjoy these better!
- December 13, 2009 at 2:11 PM
- Unknown said...
i agree with mary. they'd Be Delicious if they weren't so small
- February 4, 2010 at 2:20 AM
- Mary said...
I find myself returning to your site time after time. So I finally subscribed! I love making boxty!
- June 13, 2014 at 3:52 PM


text:
UPDATE (12:40 p.m.): Barbaro euthanized. Yes, this will be a huge story today, whether you like Barbaro or not. I will personally miss him -- don't forget: I picked him to win the Derby.
New-ish name! New-ish look! Have you read the post below about the blog's new look? (And don't forget: It's now www.danshanoff.com.)
Super Bowl XLI: The Bears arrived yesterday. The Colts arrive today. The media mayhem begins now. What I want to know: Are any of YOU going?
UPDATE: The biggest sensation of Super Bowl Week? Sarah Spain. Is she the new Jenn Sterger? Only if she parlays this into Maxim. (No, getting on da WSCR morning show in Chicago wouldn't count. Nor would an interview with Sean Salisbury on Chicago's WMVP. Has he sent his application to be her Super Bowl seat-mate via camera-phone yet? No?)
Tiger wins Buick, 7th Tour win in a row: Apparently, "parent-to-be" status is going to be pretty good for Tiger this season.
It's like Woods and Federer are playing in their own exclusive game of "Can you top this?" en route to legacies as the best ever in their respective sports.
NBA: Suns win 17th in a row. Nash has 23 pts, 15 ast. How many MVP voters are sorry they gave him the award last year (it should have gone to LeBron), because Nash is even more deserving THIS year. (But they can't possibly give him the MVP three years in a row... can they?!)
Officer Shaq: Some idiot allegedly hit Shaq's Escalade and tried to flee the scene. Little did he know that Shaq fancies himself a lawman. Shaq gave chase, caught up to the guy and beat his ass called in the real cops to close the deal.
More NFL: Cowboys hiring Norv Turner? Really, does it even matter? It's a caretaker job until new O.C. Jason Garrett is ready. (Again: If it is ultimately Garrett's job, why not just give it to him now?)
Meanwhile, isn't the NFL fining Reggie Bush $5K for taunting Brian Urlacher superfluously adding insult to injury? After all, Bush's taunt inspired the Bears to kick the crap out of Bush's Saints.
Barbaro in trouble? He had some risky surgery this weekend, and now he's had a set-back. The Barbaro Watch is on.
College Hoops: So maybe Oregon IS the best team in the Pac-10, especially after No. 3 UCLA lost to Stanford yesterday.
Women's College Hoops: I really had high hopes for
MLB Hot Stove: Are the Red Sox really going to get Todd Helton in a trade with the
Missed this weekend? Well, for starters, my kid's very first words may very well have been the tagline for a sports-apparel company's TV ad.


text:
Saturday, January 31, 2009
Of COURSE It's Right-Side Up...
Saturday, January 31, 2009
I don't think I buy it, Garret A. - but then, I guess I don't have to; you already did! [cymbal crash!]
(Why yes, sometimes I DO have an old-style Vaudeville soundtrack in my head - why do you ask?)
50 comments | Post a Comment
- Kate said...
does that say Happy Birthday Pearl, or Happy Birthday People ? either way, I don't think Pearl or the birthday people will be happy. And it looks like it was written with tar.
- January 31, 2009 at 9:22 AM
- January 31, 2009 at 9:34 AM
- Colleen said...
I've never seen balloons at that angle before, they look like multi colored sperm...ewww, I'm not eating that!
(Hope that isn't too crude for your blog, but it is what they look like!) - January 31, 2009 at 9:37 AM
- sendingtheclowns said...
Maybe (just) it's not so much that it's windy out but that these "balloons" *were* once aloft, and then crashed onto the cake when they realized what thick, gooey blobs of crap they actually ARE.
Poor Peorl. - January 31, 2009 at 9:50 AM
- Robert said...
I gotta say... Those sideways balloons kind of made me think of a technicolor sex-ed filmstrip or something. Maybe I just had one two many martinis last night...
- January 31, 2009 at 9:50 AM
- Linda Mooney said...
Is that supposed to say Happy Birthday People? Or am I missing the name?
- January 31, 2009 at 9:50 AM
Maybe they turned it sideways in order to fit the inscription on there. Wait. No. Ummmm?
- January 31, 2009 at 9:57 AM
- Carina Olsen said...
Wow! It look like a kid who is just learning cursive wrote that. You think they thought turning it sideways gave it the effect of flying? Seriously!
- January 31, 2009 at 9:58 AM
- HappySlob said...
Awww, but wouldn't you love to be named Pearl??
- January 31, 2009 at 9:59 AM
- Zitny Clan said...
Is that really supposed to say "people"? What's that all about? "Happy Birthday generic people whose names escape me right now" And the balloons look like very festive "swimmers" if ya know whudda mean ;)
- January 31, 2009 at 10:05 AM
- Valerie said...
What kind of professional WRITES like that? Jeeze.
I do have to admit, though, that I worked as a cake decorator a year after high school and while I could write well, I actually wrote:
on dozens of cakes we sold for graduation parties. Apparently folks thought I was being clever since they were for GRADuations. - January 31, 2009 at 10:05 AM
The balloons are deflating!!! That's it!
- January 31, 2009 at 10:06 AM
- Erin said...
Well, hopefully "Peorl" still had a good "binthday", despite the breeze.
- January 31, 2009 at 10:06 AM
- hisgal2 said...
Wow...you wouldn't think it would be all that hard to get the balloons on a cake be in the proper "balloon up" position. And of course the wreaker tried to fit the word 'birthday' in after starting the word 1/3 into the cake...I'm not even going to say anything about the handwriting. OY! ....and do we know what the colored crumb stuff that appears to be on the side of this cake is???...seeing that the balloons do kind of look like spermies...I'm a little concerned about what the FDA might say about this cake!
- January 31, 2009 at 10:13 AM
- Dawn Parsons Smith said...
Isn't that just typical of balloons? They never last through a whole party...
- January 31, 2009 at 10:14 AM
Happy Birthday Plorl!
- January 31, 2009 at 10:19 AM
- The Courteous Chihuahua said...
I was priding myself on not having a hangover after last night, but after viewing this cake, I'm starting to feel a little wonky.
- January 31, 2009 at 10:22 AM
- Jenn said...
technicolor swimmers... love that comment!
My great-grandaunt was named Pearl... not sure she would have liked this cake.
WV: digrater.... someone should have used digrater and added some chocolate to this cake instead of technicolor swimmers. - January 31, 2009 at 10:33 AM
- curlykew said...
What are the squirts of color on the sides of the cake?
- January 31, 2009 at 10:33 AM
- Oats said...
While in graphology when one writes in a mix of cursive and plain it's considered a trait of intellectuals, but I'm thinking that the mix of the capitols, lower-case and cursive on a cake may just put Peorl in a good enough mood for the conception party the balloons are striving for. Or maybe the writer is just a buffoon!?!?
- January 31, 2009 at 10:45 AM
- Unknown said...
I think they were in a bit of a hurry making that cake. Last minute rush maybe?
~Amy B - January 31, 2009 at 10:50 AM
- Indigo said...
Use http://www.instantrimshot.com to link your cymbal crashes!
- January 31, 2009 at 10:51 AM
- HorribleLicensePlates said...
I'm still trying to figure out what name that is supposed to say.
- January 31, 2009 at 10:59 AM
The sheen on those balloons is grossing me out.
Angie (from over at www.HalfAssedKitchen.com) - January 31, 2009 at 11:04 AM
Oh, how sad! I think I would burst into tears if that were my cake!
Poor Pearl! (But yay for our entertainment!) - January 31, 2009 at 11:23 AM
- mustdestroyalltraces said...
balloons? i'm pretty sure you mean the tadpoles. cute little guys.
i want maybe next year's cake will have frogs. you know, after a year's passed they're sure to have grown up. - January 31, 2009 at 11:28 AM
- Curdie said...
Jen, you're so funny.
- January 31, 2009 at 11:30 AM
This cake is the perfect example of why stopping by the bakery to pick up a last-minute cake from the case is a BAD idea, especially if all the decorators have gone home for the day and all you're left with is the high-school kid who sweeps up in the evenings.
- January 31, 2009 at 11:54 AM
- Kathryn Magendie said...
OMG laughing - someone who is just as bad as I am in the cake decor department *LAUGHING*
I'm over here from BB publishers, by the way...stopped at their twitter site and saw this :) - January 31, 2009 at 12:04 PM
- Kathryn H said...
I'm reading "Happy Birthday Plorl" which, I guess means it was for more than one birthday? As in "Happy Birthdays"
Does that mean a group celebration, or is the recipient supposed to save it? The cake that keeps on wishing..... - January 31, 2009 at 12:06 PM
Bahahaha! It's supposed to read "Happy Birthday Pearl, and I'm Pearl. I cannot believe my birthdau cake made it to Cake Wrecks! My friends Jim & Garret invited me to a great birthday dinner, and then apologized (and winced a little) when they brought this cake out. We laughed so hard we cried! I told them I've always felt a little sideways so it was ok. Bahahaha, I am so glad that all of you have the chance to laugh out loud too! Happy Cake Wrecking ~ Pearl
- January 31, 2009 at 12:21 PM
- jackie31337 said...
In keeping with the sex-ed interpretation a lot of commenters (myself included) see in this cake, I would just like to add that my verification word was "mater".
- January 31, 2009 at 12:28 PM
The balloons remind me of the old joke about the guys giving sod laying instructions: "Green side up! Green side up!"
Terrible Cake! LOL LOL LOL LOL - January 31, 2009 at 12:30 PM
Holy moly, that rim shot link is funny.
WV - aterobba - Pearl was aterobba'd of her dignity when she saw this was her birthday cake. - January 31, 2009 at 12:35 PM
- the ginabean said...
What does it say? Happy Binthday Peorh?
Maybe the birthday...boy?...girl?...is blind, so it doesn't matter. - January 31, 2009 at 12:46 PM
- Mella said...
Thanks you Cyberrblue
I was lacking a good synonym for the ballons and multi colored sperm just fits perfectly - January 31, 2009 at 12:54 PM
This is a truly tremendous site !!
Glad to see that you are not including any 'profanity, mean spirited remarks', they are just soooo last century, as I believe Jonathan Ross and Russell Brand are finding out..
Although, Russell B, bless him, is funny and should be the subject of a cake himself...
The one where a 'computer memory' has been put on the cake is just, well, surreal in a 'Twilight Zone' kinda way.. - January 31, 2009 at 12:56 PM
- Alison said...
To quote a Dorothy's nemesis: "I'm meeelting!" Gol-lee, what a mess!
- January 31, 2009 at 12:57 PM
- Hall Monitor said...
I would like to say it's the thought that counts, but...
- January 31, 2009 at 1:18 PM
- Scritzy said...
Pearl, I'm glad to see you had a sense of humor about your celebratory cake. I hope it tasted good.
Your poor friends ...
I think "spaced out" is a good way of describing it. - January 31, 2009 at 4:33 PM
Nice....Poor Pearl
- January 31, 2009 at 7:17 PM
- dizzy mom said...
So maybe Pearl was laying on the couch when they brought in the cake. That way she could read the cake...of couse the balloons would have to appear sideways! Job well done!
- January 31, 2009 at 9:28 PM
- WM said...
I think it is particularly appropriate that the recipient of this cake actually posted...I'm so sorry...but, really, it IS funny.
WV: vowless...za cake design vas vowless... - January 31, 2009 at 9:43 PM
- Garret said...
Oddly I never saw the sperm look a likes! I am the guilty one for accepting this cake.
I'll be linking this page in my blog.
Garret of http://jimandgarret.blogspot.com/ - February 1, 2009 at 12:13 AM
The vaudeville soundtrack reminds me of my halcyon days of youth, when I was in college.
A friend of mine played the clarinet. For a few days, he decided to take it around with him and provide vaudeville-style musical cues for himself as he went on about daily life.
It was made of awesome.
Word Verification: lasho. As in, "the baker who made the toilet cake deserves 30 lassho." - February 2, 2009 at 3:25 PM
- Boozy Tooth said...
OMG!! Garret my man, you're famous! Hooray for making it onto Cake Wrecks. Yay!
- February 2, 2009 at 7:17 PM
Ok, so I know I'm WAAAY late commenting, but I've just discovered your blog and I LOVE it. I also think it's awesome that the cake featured for my birthday is a birthday cake - how cool (and awful) is that?!
- March 2, 2009 at 11:05 PM
- Cupcakes Lady said...
Happy Binthday Plorl! Enjoy. lol xx
- October 13, 2010 at 8:09 AM
You gotta wonder; was the decorator on drugs when they made this special little cake, or do they not have good perception of things?
- January 18, 2011 at 8:54 PM
Why did you even buy that cake?! I would have sent it back.
- June 2, 2011 at 4:11 PM
Search This Blog
Wreck the Halls
NEW! Pre-Order Today!
Barnes & Noble
Borders |
Buy the Book
Buy the NYT Bestseller
What's a Wreck?
What's a Wreck?
A Cake Wreck is any cake that is unintentionally sad, silly, creepy, inappropriate - you name it. A Wreck is not necessarily a poorly-made cake; it's simply one I find funny, for any of a number of reasons. Anyone who has ever smeared frosting on a baked good has made a Wreck at one time or another, so I'm not here to vilify decorators: Cake Wrecks is just about finding the funny in unexpected, sugar-filled places.
Now, don't you have a photo you want to send me? ;)
- Fan Faves
- The Classics
The Classics
Praise for the Book
- "Will have you laughing so hard you'll forget to eat!"— Washington Post
- "a hilarious winner" — The Oregonian
- "a fantastically gut-busting book"— NPR
- "It's all here, each wreck a disaster of hilarity." — BookPage.com
- "Hysterically funny!"— Epinions.com
- "laugh-out-loud funny"— The Times
- "Yates' sharp humor makes the funny even funnier." — The Dallas Morning News
- "an amazing laugh-out-loud book"— The Book Triblog
What the fans are saying
- "I was laughing so hard, I couldn't catch my breath."
- "As funny as the blog that started it."
- "WAY better than I expected!"
- "Cake Wreckery at its best!"
- "Wrecktastic!"
- American Mensa:Top 50 Websites of 2010
- Amazon: Top 10 gift books of 2009
- The Orlando Sentinal "Orbbies": Winner Humor
- 2009 BlogLuxe Awards: Funniest Blog
- 2009 Bloggies: Best Writing of a Weblog, Best New Weblog, Best Food Blog
- The 2008 Weblog Awards: Best Food Blog
- Blogger's Choice 2008 Awards: Best Humor Blog
Where's the book?
We don't have any copies of Cake Wrecks for sale here, autographed or otherwise. We decided the shipping and handling costs would be too high to make it worth your while. So instead, buy your copies locally or online and then order personalized bookplates: it's cheaper, easier, and I think even looks a bit nicer.
Ordering Info
Payments must be made through Paypal, which accepts all major credit cards. Sorry, but that means no checks or MOs or barter-based chickens.
We ship everything first class USPS, and will do our best to have your package in the mail within 2 days of your order.
Popular This Month
Popular This Month
- It's Me or the Dog: Cupcake Trance
- Of COURSE It's Right-Side Up...
- Score!
- Are You Ready for Some Football?!
- It's My Blog and I'll Flog if I Want To...
- Well, the word "holy" DID come to mind...
- You Say "Redneck" Like it's a Bad Thing
- Every Breath You Take...
- Why So Serious?
- Sunday Sweets: Cutey-Patootey Dragon Babes
- Hey Everybody, Do You Know What Time It Is?
- The Problem With Phone Orders
- Breaking News: Head-Swelling Bakery Incident Goes ...
- Why Suzy Needs Therapy
- The Presidential Sweet
- Wedding Crashers, Cake Wrecks Style
- That Poe Bird
- Sunday Sweets: Lord of the Rings
- It's Just a LITTLE Soap Box, I Promise
- What Do You Mean, "Last Minute"?
- Big Day, Big Wrecks
- Think They're Organic? [smirk]
- Introducing a Good Sport
- Double Your Photos, Double Your "Fun"
- LIFE Wrecks
- Voting Update: This One's For John
- Sunday Sweets: Castles & Fairies
- New Tees!
- My Sweet What is What?
- How to Make Your Twins Hate You
- Hey, Bebeh
- Cake Wrecks Gets Its Own TV Show!
- A Real Stretch
- Calling All Evil Wrecks Henchpeople...
- Now THAT'S What I'm Talkin' 'Bout
- Bonus Side Tangent Contest Results
- Why Football & Bridal Showers Don't Mix
- Sunday Sweets: Fastastical Beasts
- Groom's Cake for a Cop
- Food that's Bad for You, Disguised as...Food that'...
- Year of the Tongue?
- Wasted Cakes


text:
License/price : Freeware , Size : 3.11 Mb
Foobar2000 is an advanced freeware audio player for the Windows
platform. Some of the basic features include full unicode support,
ReplayGain support and native support for several popular audio formats.
- Supported audio formats: MP3, MP4, AAC, CD Audio, WMA, Vorbis, FLAC, WavPack, WAV, AIFF, Musepack, Speex, AU, SND... and more with additional components.
- Gapless playback.
- Full unicode support.
- Easily customizable user interface layout.
- Advanced tagging capabilities.
- Support for ripping Audio CDs as well as transcoding all supported audio formats using the Converter component.
- Full ReplayGain support.
- Customizable keyboard shortcuts.
- Open component architecture allowing third-party developers to extend functionality of the player.


text:
The astonishing election upset in India has put the future of its economic-reform program in question. With the victorious Congress Party depending on Leftist parties for parliamentary support, uncertainty is running high about the future of the country's privatization, deregulation, and foreign-investment reforms.
Voters have sent a clear message about the need for broad-based economic growth that lifts all boats. But some members of the winning coalition may well misinterpret that message. India's recent experience—and that of its Asian neighbors—shows that continuing rural poverty stems not from too much economic reform but rather from too little. Since liberalization began, in 1991, annual GDP growth has been twice as high as it had been previously. As a result, poverty rates have fallen by nearly a third in both rural and urban areas. The celebrated software and outsourcing industries are only the latest evidence of the effectiveness of the reforms, which have created hundreds of thousands of high-paying jobs and generated billions in export revenues.
The challenge facing the new ruling coalition is to extend the success of the IT and outsourcing industries into the broader economy. To that end, foreign investment and global competition must be allowed to reach more sectors, including some in which the government now plays a significant role. Although India has broadly cut import duties and increased foreign-ownership limits over the past ten years, large parts of the economy remain sheltered by high tariffs and restrictions on foreign direct investment (Exhibit 1), which amounts to just 0.7 percent of India's GDP, compared with 4.2 percent in China and 3.2 percent in Brazil. Imports total less than $70 billion—a small fraction of China's $413 billion.
New research by the McKinsey Global Institute indicates that the foreign direct investment that did find its way to India has had an overwhelmingly positive impact. The introduction of foreign competition in IT, business-process outsourcing, and the automotive industry has prompted Indian companies to revamp their operations and boost productivity, and some have become formidable global competitors. Thousands of new jobs have been created in these industries. Consumers benefit from lower prices, better quality, and a wider selection of products and services, while domestic demand has soared in response to lower prices.
The task now is to build on the current momentum by replicating these successes across the economy. Earlier MGI research found that product market regulations, the lack of clear land titles, and pervasive government ownership were preventing India from achieving 10 percent annual GDP growth. MGI's latest research shows that the country must go further in lowering trade and foreign-investment barriers if it is to continue integrating itself into the global economy.
Shining stars
India's $1.5 billion outsourcing business illustrates how foreign investment and trade have benefited the country. Along with IT and software, business-process outsourcing is perhaps the most open sector. In 2002, it attracted 15 percent of total foreign direct investment and accounted for 10 percent of all exports. By 2008, it is expected to attract one-third of all foreign direct investment and to generate $60 billion a year in exports, creating nearly a million new jobs in the process.
Without early investments by multinational companies, the outsourcing industry probably would never have emerged (Exhibit 2). Pioneers such as British Airways and GE were among the first to see the opportunity to move IT and other back-office operations to India. The success of these companies demonstrated to the world that the country was a credible offshoring destination. The multinationals trained thousands of local workers, many of whom transferred their skills to Indian companies that sprang up in response. Although Indian outsourcing firms now control over half of the intensely competitive global IT and back-office outsourcing market, all of the leading ones started as joint ventures or subsidiaries of multinational companies or were founded by managers who had worked for them.
Of course, allowing foreign investment in an industry that has no large Indian companies was relatively painless. It is far harder to put local incumbents in the line of fire. Yet India has done so in its automotive industry, with impressive results. Until 1983, high tariffs and a ban on foreign investment shielded government-owned automakers—such as, Premier Automobiles Limited (PAL)—from global competition. Local incumbents produced just two car models, both based on antiquated technology, between them, and charged high prices. In 1983, Suzuki Motor was allowed to take a minority stake in a joint venture with Maruti Udyog, another government-owned enterprise, to produce passenger cars. The new competition forced the incumbents to respond. Within a few years, eight Indian car models were in production, and all of them—including those from PAL—were of better quality than the cars produced before this liberation took place.
In 1992, the government lifted many of the remaining barriers to foreign investment in the auto industry. Nine additional foreign carmakers responded, and today competition is stiff. As a result, labor productivity has increased more than threefold, in part because PAL has been forced out of business. Prices have fallen steadily by 8 to 10 percent annually in all market segments, unleashing a burst of consumer demand. Despite higher productivity and the closure of PAL, employment has held steady and workers have benefited from higher wages.
Today, India's $5 billion auto industry is expanding by 15 percent a year—one of the world's fastest automotive-industry growth rates—and produces 13 times more cars than it did in 1983. This year, Tata Motors will make history by exporting to the United Kingdom 20,000 cars to be sold under the MG Rover brand—a feat no one would even have dreamed of just a decade ago, given the quality of Indian cars at the time. In view of the greater competitive intensity in the market, India may be better positioned than China is to become a global low-cost auto-manufacturing base. None of this would have been possible had India's carmakers remained isolated from the world.
Hiding behind trade barriers
The dynamic growth and competitiveness of India's outsourcing and automotive industries stand in contrast to most of its economy, where continuing restrictions on foreign investment and trade dampen competition and help inefficient companies survive.
Another sector, food retailing, illustrates how Indian industry fares when foreign investment is banned entirely. Labor productivity, for instance, is a mere 5 percent of US levels, in part because street markets and mom-and-pop counter stores account for 98 percent of the market, modern store formats (like supermarkets and hypermarkets) for just 2 percent. But productivity averages just 20 percent of US levels even in Indian supermarkets as a result of their small scale, poor merchandising and marketing skills, and inefficient operations. In other emerging markets, including Brazil, China, and Mexico, global retailers such as Carrefour and Wal-Mart Stores have intensified competition and increased productivity. If these retailers could invest in India, improved Indian supermarkets could, we estimate, offer prices 10 percent lower than those of traditional grocery stores. Indian consumers across the social spectrum would benefit, and as many as eight million new jobs would be created.
Getting the full benefit of foreign investment calls for competition within industries, since it forces companies to improve their operations and innovate. Many forms of protection and regulation can stifle competition and thus limit the impact of foreign investment. The consumer electronics industry is a prime example. The government lifted foreign-investment restrictions in the sector in the early 1990s. From 1996 to 2001, foreign direct investment in it averaged $300 million annually—20 percent of the total for India—a large sum, although just 8 percent of the consumer electronics investment going to China and just half of the investment going to Brazil and Mexico. Still, the entry of multinational players has boosted the local industry's productivity and given Indian consumers more choice and lower prices.
Despite these gains, consumer electronics goods made in India still can't compete internationally, and the country's consumers pay unduly high prices for them. The industry's average labor productivity is only half of Chinese and 13 percent of South Korean levels. Tariffs, taxes, and regulations are the main culprits. Tariffs of 35 to 40 percent on finished goods keep out imports and allow inefficient companies to continue operating. They also force even the best manufacturers to operate with subscale plants when, as usually happens, Indian demand doesn't justify larger scale. Tariffs on inputs and indirect taxes (mostly sales and value-added taxes) add substantially to the price of final goods, further limiting demand (Exhibit 3). Meanwhile, labor laws that prevent the rationalization of plants and limit the use of contract labor increase production costs for both foreign and domestic companies. Red tape in getting export licenses and inefficiencies in India's ports make exporting finished goods prohibitively expensive (Exhibit 4).
These same problems limit foreign investment and prevent many industries—including banking, heavy industry, and textiles—from reaching their full potential. Ultimately, consumers and workers pay through higher prices and the anemic pace of job creation.
Going global
India has clearly benefited from closer integration into the global economy in industries such as automotive, business-process outsourcing, and IT. To build on that success, the government must now lower trade and foreign-investment barriers still further.
First, tariff levels should be cut to an average of 10 percent, matching those of India's neighbors in the Association of South East Asian Nations (ASEAN). Although progress has been made on tariffs, the Indian government still prohibits imports of many goods and protects inefficient companies from foreign competition. To give those companies a chance to improve their operations, the government might first lower duties on capital goods and inputs. Then, over several years, it could reduce them on finished goods.
Foreign-ownership restrictions should be lifted throughout the economy as well, except in strategic areas, notably defense. At present, foreign ownership is not only prohibited altogether in industries such as agriculture, real estate, and retailing but also limited to minority stakes in many others, such as banking, insurance, and telecommunications.
India's government should also reconsider the expensive but often ineffective incentives it offers foreign companies to attract foreign investment, for these resources would be put to better use improving the country's roads, telecom infrastructure, power supply, and logistics. What's more, MGI research found that the government often gives away substantial sums of money for investments that would have been made anyway. (To give one example, it has waived the 35 percent tax on corporate profits for foreign companies that move business-process operations to India, even though the country dominates the global industry.) Moreover, state governments often conduct unproductive bidding wars with one another and give away an assortment of tax holidays, import duty exemptions, and subsidized land and power. Yet MGI surveys show that foreign executives place relatively little value on these incentives and would rather see the government invest resources in the country's poor infrastructure.
Finally, interviews with foreign executives showed us that India's labor laws deter foreign investment in some industries. It is no coincidence that software and business-outsourcing companies are exempt from many labor regulations, such as those regarding hours and overtime. Executives tell us that without these exemptions, it would be impossible to perform back-office operations in India. To attract foreign investment in labor-intensive industries, the government should therefore consider making labor laws more flexible.
Some Indian policy makers might argue that the reforms proposed here would undermine long-held social objectives, such as creating employment. But the evidence shows that regulations on foreign investment, foreign trade, and labor have actually slowed economic growth and lowered the standard of living. A decade ago, India's per capita income was nearly the same as China's; today, China's is almost twice as high.
India's economy has made real progress, but further liberalization will be needed to sustain its growth. The country now has 40 million people looking for work, and an additional 35 million will join the labor force over the next three years. Creating jobs for all these Indians will require more dynamic and competitive industries across the economy. Opening up to foreign competition, not hiding from it, is the answer.