Skip to content
Septagram edited this page Oct 2, 2015 · 2 revisions

Plugin integration is designed to be as simple as possible. Shops are to insert a few lines of HTML that they are not supposed to ever update. These lines are essentially the <script /> tag for the loader.js file - a plugin loader - and plugin configuration, such as shop ID and shoe ID.

Plugin loader and dependencies

Plugin code is loaded in two stages, and from two assets, plugin/loader.js and plugin/plugin.js. Plugin dependencies and modules are concatenated into plugin.js, except the Q library, which is in the loader.js (plugin loading benefits from using promises). Plugin has a lot of dependencies, and loading them is done unintrusively, with an explicit goal to not affect the global namespace except for the ShoeSizeMe object. Before loading libraries corresponding previous values on the window object are saved, and restored after the plugin-provided libraries have finished initialization. Therefore, plugin can use any library it requires, even if the web page already is using another version of this very library.

Cross-origin request handling and window roles

The distinction between loader.js and plugin.js is not also historical, but also is necessary because plugin has to perform a complicated procedure to circumvent the same-origin limitation in every browser we support. Plugin has to perform multiple requests to another domain (shoesize.me), that are restricted because they are considered a security risk (to shoesize.me, apparently). In most browsers, this can be solved by using CORS (cross-origin resource sharing), but IE8, which is used by a lot of our users, is an exception.

To work around this, an iframe is created on the shop page, and the communication is done through that iframe via postMessage(). The contents of plugin.js in production and on Staging are also transferred through that iframe, because plugin.js has grown to be very large, and loading it twice per page load would become a burden.

When loader.js is loaded, it determines the kind of the page it is loaded, stores it as a config option in ShoeSizeMe.config.windowRole and is using this to determine the correct loading process. plugin.js is also using this information to determine the required modules and the next steps for the plugin. It can be:

  • shop - plugin placed on the shop page. Will create an iframe element to http://shoesize.me/plugin/iframe.html and perform resource loading and API requests through it. Will create the visible part of the plugin on the page and manage the UI events.
  • iframe - plugin loaded within an iframe within a shop page. Will load plugin.js and transfer it to the loader script on the shop page. Will then receive RPC requests from the shop page and perform API requests and resource loading, relaying the results back.
  • auth - plugin placed on the OAuth2 landing page when user performs a login through Facebook, receives the auth token from the OAuth login process and stores it in the database along with a ShoeSize.Me auth token. Then closes the page, thus letting the shop page know the login flow is concluded.

Resource loading and plugin configuration

When the loader.js is being executed, it finds its own script tag and parses its contents (evaluates them, actually) to obtain the initial plugin configuration. The shopID and shoeID configuration options are required for every shop page to be working. The locale config option may be provided, and shoud be used when the shop has page with different locales. Other configuration options may be specified, but in development serverside configuration would overwrite them.

As the plugin.js is executed, it initiates loading a number of resources, most of them through an iframe:

  • Plugin HTML
  • Plugin stylesheets
  • Plugin locales
  • Plugin button HTML

HTML and stylesheets are then inserted into the page. In the meantime, plugin performs and API request to obtain the full plugin configuration (the config API call). Configuration for each shop can be edited in the admin backend. Configuration object received would overwrite the page-side configuration, except for a few options (full list available in the config module) in production. In development environment, page-side configuration takes priority, for developer's convenience.

Summary

For the usual shop page, the sequence for loading the plugin would usually be as follows:

  1. The loader.js is loaded and executed.
  2. It stores the Q library in the ShoeSizeMe object and restores the original value, in case another version is used by the shop.
  3. Initial plugin configuration is parsed. It is determined whether the plugin can continue loading and that it is on the shop page.
  4. An <iframe /> element is created for iframe.html.
  5. In an iframe, loader.js is loaded.
  6. It determines that it resides inside of an iframe, and loads plugin.js as a file.
  7. It executes plugin.js on its side and sends it over to the shop page through postMessage().
  8. On the shop page, loader receives the plugin.js contents and executes them.
  9. Plugin dependencies are initialized, then cleaned out from the global namespace.
  10. Plugin performs an API request for the full configuration anad for the user profile and begins loading the resources.
  11. If there are no obstacles so far and the resources are loaded, plugin button and plugin widget are inserted into the page.
  12. Plugin button fades in, plugin is ready.