//-------------------------------------------------------------------------------------------------
// <copyright file="IBootstrapperApplication.h" company="Outercurve Foundation">
//   Copyright (c) 2004, Outercurve Foundation.
//   This software is released under Microsoft Reciprocal License (MS-RL).
//   The license and further copyright text can be found in the file
//   LICENSE.TXT at the root directory of the distribution.
// </copyright>
//
// <summary>
// IBootstrapperApplication implemented by a bootstrapper application and used by bootstrapper engine.
// </summary>
//-------------------------------------------------------------------------------------------------

#pragma once

enum BOOTSTRAPPER_DISPLAY
{
    BOOTSTRAPPER_DISPLAY_UNKNOWN,
    BOOTSTRAPPER_DISPLAY_EMBEDDED,
    BOOTSTRAPPER_DISPLAY_NONE,
    BOOTSTRAPPER_DISPLAY_PASSIVE,
    BOOTSTRAPPER_DISPLAY_FULL,
};

enum BOOTSTRAPPER_RESTART
{
    BOOTSTRAPPER_RESTART_UNKNOWN,
    BOOTSTRAPPER_RESTART_NEVER,
    BOOTSTRAPPER_RESTART_PROMPT,
    BOOTSTRAPPER_RESTART_AUTOMATIC,
    BOOTSTRAPPER_RESTART_ALWAYS,
};

enum BOOTSTRAPPER_RESUME_TYPE
{
    BOOTSTRAPPER_RESUME_TYPE_NONE,
    BOOTSTRAPPER_RESUME_TYPE_INVALID,        // resume information is present but invalid
    BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED,    // relaunched after an unexpected interruption
    BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING, // reboot has not taken place yet
    BOOTSTRAPPER_RESUME_TYPE_REBOOT,         // relaunched after reboot
    BOOTSTRAPPER_RESUME_TYPE_SUSPEND,        // relaunched after suspend
    BOOTSTRAPPER_RESUME_TYPE_ARP,            // launched from ARP
};

enum BOOTSTRAPPER_ERROR_TYPE
{
    BOOTSTRAPPER_ERROR_TYPE_ELEVATE,            // error occurred trying to elevate.
    BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER,  // error came from windows installer.
    BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE,        // error came from an exe package.
    BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER,   // error occurred trying to authenticate with HTTP server.
    BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY,    // error occurred trying to authenticate with HTTP proxy.
    BOOTSTRAPPER_ERROR_TYPE_APPLY,              // error occurred during apply.
};

enum BOOTSTRAPPER_RELATED_OPERATION
{
    BOOTSTRAPPER_RELATED_OPERATION_NONE,
    BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE,
    BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE,
    BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE,
    BOOTSTRAPPER_RELATED_OPERATION_REMOVE,
    BOOTSTRAPPER_RELATED_OPERATION_INSTALL,
    BOOTSTRAPPER_RELATED_OPERATION_REPAIR,
};

enum BOOTSTRAPPER_CACHE_OPERATION
{
    BOOTSTRAPPER_CACHE_OPERATION_COPY,
    BOOTSTRAPPER_CACHE_OPERATION_DOWNLOAD,
    BOOTSTRAPPER_CACHE_OPERATION_EXTRACT,
};

enum BOOTSTRAPPER_APPLY_RESTART
{
    BOOTSTRAPPER_APPLY_RESTART_NONE,
    BOOTSTRAPPER_APPLY_RESTART_REQUIRED,
    BOOTSTRAPPER_APPLY_RESTART_INITIATED,
};

enum BOOTSTRAPPER_RELATION_TYPE
{
    BOOTSTRAPPER_RELATION_NONE,
    BOOTSTRAPPER_RELATION_DETECT,
    BOOTSTRAPPER_RELATION_UPGRADE,
    BOOTSTRAPPER_RELATION_ADDON,
    BOOTSTRAPPER_RELATION_PATCH,
    BOOTSTRAPPER_RELATION_DEPENDENT,
    BOOTSTRAPPER_RELATION_UPDATE,
};

struct BOOTSTRAPPER_COMMAND
{
    BOOTSTRAPPER_ACTION action;
    BOOTSTRAPPER_DISPLAY display;
    BOOTSTRAPPER_RESTART restart;

    LPWSTR wzCommandLine;
    int nCmdShow;

    BOOTSTRAPPER_RESUME_TYPE resumeType;
    HWND hwndSplashScreen;

    // If this was run from a related bundle, specifies the relation type
    BOOTSTRAPPER_RELATION_TYPE relationType;
    BOOL fPassthrough;

    LPWSTR wzLayoutDirectory;
};

DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-AB06-099D717C67FE")
{
    // OnStartup - called when the engine is ready for the bootstrapper application to start.
    //
    STDMETHOD(OnStartup)() = 0;

    // OnShutdown - called after the bootstrapper application quits the engine.
    //
    // Return:
    //  IDRESTART instructs the engine to restart. The engine will not launch again after the machine
    //            is rebooted. Ignored if reboot was already initiated by OnExecutePackageComplete().
    //
    // IDRELOAD_BOOTSTRAPPER instructs the engine to unload the bootstrapper application and restart
    //                       the engine which will load the bootstrapper application again. Typically
    //                       used to switch from a native bootstrapper application to a managed one.
    //
    //  All other return codes are ignored.
    STDMETHOD_(int, OnShutdown)() = 0;

    // OnSystemShutdown - called when the operating system is instructed to shutdown the machine.
    //
    // Return:
    //  IDCANCEL instructs the engine to block the shutdown of the machine.
    //
    //  All other return codes allow the shutdown to commence.
    STDMETHOD_(int, OnSystemShutdown)(
        __in DWORD dwEndSession,
        __in int nRecommendation
        ) = 0;

    // OnDetectBegin - called when the engine begins detection.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnDetectBegin)(
        __in BOOL fInstalled,
        __in DWORD cPackages
        ) = 0;

    // OnDetectForwardCompatibleBundle - called when the engine detects a forward compatible bundle.
    //
    // Return:
    //  IDOK instructs the engine to use the forward compatible bundle.
    //
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to not use the forward compatible bundle.
    STDMETHOD_(int, OnDetectForwardCompatibleBundle)(
        __in_z LPCWSTR wzBundleId,
        __in BOOTSTRAPPER_RELATION_TYPE relationType,
        __in_z LPCWSTR wzBundleTag,
        __in BOOL fPerMachine,
        __in DWORD64 dw64Version,
        __in int nRecommendation
        ) = 0;

    // OnDetectUpdateBegin - called when the engine begins detection for bundle update.
    //
    // Return:
    //  IDOK instructs the engine to attempt update detection.
    //
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to skip update detection.
    STDMETHOD_(int, OnDetectUpdateBegin)(
        __in_z LPCWSTR wzUpdateLocation,
        __in int nRecommendation
        ) = 0;

    // OnDetectUpdateComplete - called when the engine completes detection for bundle update.
    //
    // Remarks:
    //  wzUpdateLocation is null if no update was available.
    STDMETHOD_(void, OnDetectUpdateComplete)(
        __in HRESULT hrStatus,
        __in_z_opt LPCWSTR wzUpdateLocation
        ) = 0;

    // OnDetectRelatedBundle - called when the engine detects a related bundle.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnDetectRelatedBundle)(
        __in_z LPCWSTR wzBundleId,
        __in BOOTSTRAPPER_RELATION_TYPE relationType,
        __in_z LPCWSTR wzBundleTag,
        __in BOOL fPerMachine,
        __in DWORD64 dw64Version,
        __in BOOTSTRAPPER_RELATED_OPERATION operation
        ) = 0;

    // OnDetectPackageBegin - called when the engine begins detecting a package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnDetectPackageBegin)(
        __in_z LPCWSTR wzPackageId
        ) = 0;

    // OnDetectRelatedMsiPackage - called when the engine begins detects a related package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnDetectRelatedMsiPackage)(
        __in_z LPCWSTR wzPackageId,
        __in_z LPCWSTR wzProductCode,
        __in BOOL fPerMachine,
        __in DWORD64 dw64Version,
        __in BOOTSTRAPPER_RELATED_OPERATION operation
        ) = 0;

    // OnDetectTargetMsiPackage - called when the engine detects a target MSI package for
    //                            an MSP package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnDetectTargetMsiPackage)(
        __in_z LPCWSTR wzPackageId,
        __in_z LPCWSTR wzProductCode,
        __in BOOTSTRAPPER_PACKAGE_STATE patchState
        ) = 0;

    // OnDetectMsiFeature - called when the engine detects a feature in an MSI package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop detection.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnDetectMsiFeature)(
        __in_z LPCWSTR wzPackageId,
        __in_z LPCWSTR wzFeatureId,
        __in BOOTSTRAPPER_FEATURE_STATE state
        ) = 0;

    // OnDetectPackageComplete - called after the engine detects a package.
    //
    STDMETHOD_(void, OnDetectPackageComplete)(
        __in_z LPCWSTR wzPackageId,
        __in HRESULT hrStatus,
        __in BOOTSTRAPPER_PACKAGE_STATE state
        ) = 0;

    // OnDetectPackageComplete - called after the engine completes detection.
    //
    STDMETHOD_(void, OnDetectComplete)(
        __in HRESULT hrStatus
        ) = 0;

    // OnPlanBegin - called when the engine begins planning.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop planning.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnPlanBegin)(
        __in DWORD cPackages
        ) = 0;

    // OnPlanRelatedBundle - called when the engine begins planning a related bundle.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop planning.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnPlanRelatedBundle)(
        __in_z LPCWSTR wzBundleId,
        __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
        ) = 0;

    // OnPlanPackageBegin - called when the engine begins planning a package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop planning.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnPlanPackageBegin)(
        __in_z LPCWSTR wzPackageId,
        __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
        ) = 0;

    // OnPlanTargetMsiPackage - called when the engine plans an MSP package
    //                          to apply to an MSI package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop planning.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnPlanTargetMsiPackage)(
        __in_z LPCWSTR wzPackageId,
        __in_z LPCWSTR wzProductCode,
        __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
        ) = 0;

    // OnPlanMsiFeature - called when the engine plans a feature in an
    //                    MSI package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop planning.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnPlanMsiFeature)(
        __in_z LPCWSTR wzPackageId,
        __in_z LPCWSTR wzFeatureId,
        __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
        ) = 0;

    // OnPlanPackageComplete - called after the engine plans a package.
    //
    STDMETHOD_(void, OnPlanPackageComplete)(
        __in_z LPCWSTR wzPackageId,
        __in HRESULT hrStatus,
        __in BOOTSTRAPPER_PACKAGE_STATE state,
        __in BOOTSTRAPPER_REQUEST_STATE requested,
        __in BOOTSTRAPPER_ACTION_STATE execute,
        __in BOOTSTRAPPER_ACTION_STATE rollback
        ) = 0;

    // OnPlanComplete - called when the engine completes planning.
    //
    STDMETHOD_(void, OnPlanComplete)(
        __in HRESULT hrStatus
        ) = 0;

    // OnApplyBegin - called when the engine begins applying the plan.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnApplyBegin)() = 0;

    // OnElevate - called before the engine displays an elevation prompt.
    //             Will only happen once per execution of the engine.
    //
    // Return:
    //  IDCANCEL instructs the engine to abort elevation and stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnElevate)() = 0;

    // OnProgress - called when the engine makes progress.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnProgress)(
        __in DWORD dwProgressPercentage,
        __in DWORD dwOverallPercentage
        ) = 0;

    // OnError - called when the engine encounters an error.
    //
    // Return:
    //  uiFlags is a combination of valid ID* return values appropriate for
    //          the error.
    //
    //  IDNOACTION instructs the engine to pass the error through to default
    //             handling which usually results in the apply failing.
    STDMETHOD_(int, OnError)(
        __in BOOTSTRAPPER_ERROR_TYPE errorType,
        __in_z_opt LPCWSTR wzPackageId,
        __in DWORD dwCode,
        __in_z_opt LPCWSTR wzError,
        __in DWORD uiFlags,
        __in DWORD cData,
        __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
        __in int nRecommendation
        ) = 0;

    // OnRegisterBegin - called when the engine registers the bundle.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnRegisterBegin)() = 0;

    // OnRegisterComplete - called when the engine registration is
    //                      complete.
    //
    STDMETHOD_(void, OnRegisterComplete)(
        __in HRESULT hrStatus
        ) = 0;

    // OnCacheBegin - called when the engine begins caching.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop caching.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnCacheBegin)() = 0;

    // OnCachePackageBegin - called when the engine begins caching
    //                       a package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop caching.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnCachePackageBegin)(
        __in_z LPCWSTR wzPackageId,
        __in DWORD cCachePayloads,
        __in DWORD64 dw64PackageCacheSize
        )  = 0;

    // OnCacheAcquireBegin - called when the engine begins copying or
    //                       downloading a payload to the working folder.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop caching.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnCacheAcquireBegin)(
        __in_z_opt LPCWSTR wzPackageOrContainerId,
        __in_z_opt LPCWSTR wzPayloadId,
        __in BOOTSTRAPPER_CACHE_OPERATION operation,
        __in_z LPCWSTR wzSource
        ) = 0;

    // OnCacheAcquireProgress - called when the engine makes progresss copying
    //                          or downloading a payload to the working folder.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop caching.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnCacheAcquireProgress)(
        __in_z_opt LPCWSTR wzPackageOrContainerId,
        __in_z_opt LPCWSTR wzPayloadId,
        __in DWORD64 dw64Progress,
        __in DWORD64 dw64Total,
        __in DWORD dwOverallPercentage
        ) = 0;

    // OnResolveSource - called when a payload or container cannot be found locally.
    //
    // Parameters:
    //  wzPayloadId will be NULL when resolving a container.
    //  wzDownloadSource will be NULL if the container or payload does not provide a DownloadURL.
    //
    // Return:
    //  IDRETRY instructs the engine to try the local source again.
    //
    //  IDDOWNLOAD instructs the engine to try the download source.
    //
    //  All other return codes result in an error.
    //
    // Notes:
    //  It is expected the BA may call IBurnCore::SetLocalSource() or IBurnCore::SetDownloadSource()
    //  to update the source location before returning IDRETRY or IDDOWNLOAD.
    STDMETHOD_(int, OnResolveSource)(
        __in_z LPCWSTR wzPackageOrContainerId,
        __in_z_opt LPCWSTR wzPayloadId,
        __in_z LPCWSTR wzLocalSource,
        __in_z_opt LPCWSTR wzDownloadSource
        ) = 0;

    // OnCacheAcquireComplete - called after the engine copied or downloaded
    //                          a payload to the working folder.
    //
    // Return:
    //  IDRETRY instructs the engine to try the copy or download of the payload again.
    //
    //  All other return codes are ignored.
    STDMETHOD_(int, OnCacheAcquireComplete)(
        __in_z_opt LPCWSTR wzPackageOrContainerId,
        __in_z_opt LPCWSTR wzPayloadId,
        __in HRESULT hrStatus,
        __in int nRecommendation
        ) = 0;

    // OnCacheVerifyBegin - called when the engine begins to verify then copy
    //                      a payload or container to the package cache folder.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop caching.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnCacheVerifyBegin)(
        __in_z_opt LPCWSTR wzPackageOrContainerId,
        __in_z_opt LPCWSTR wzPayloadId
        ) = 0;

    // OnCacheVerifyComplete - called after the engine verifies and copies
    //                         a payload or container to the package cache folder.
    //
    // Return:
    //  IDRETRY instructs the engine to try the verification of the payload again.
    //          Ignored if hrStatus is success.
    //
    //  IDTRYAGAIN instructs the engine to acquire the payload again. Ignored if
    //             hrStatus is success.
    //
    //  All other return codes are ignored.
    STDMETHOD_(int, OnCacheVerifyComplete)(
        __in_z_opt LPCWSTR wzPackageOrContainerId,
        __in_z_opt LPCWSTR wzPayloadId,
        __in HRESULT hrStatus,
        __in int nRecommendation
        ) = 0;

    // OnCachePackageComplete - called after the engine attempts to copy or download all
    //                          payloads of a package into the package cache folder.
    //
    // Return:
    //  IDIGNORE instructs the engine to ignore non-vital package failures and continue with the
    //           caching. Ignored if hrStatus is a success or the package is vital.
    //
    //  IDRETRY instructs the engine to try the acquisition and verification of the package
    //          again. Ignored if hrStatus is a success.
    //
    //  All other return codes are ignored.
    STDMETHOD_(int, OnCachePackageComplete)(
        __in_z LPCWSTR wzPackageId,
        __in HRESULT hrStatus,
        __in int nRecommendation
        )  = 0;

    // OnCacheComplete - called when the engine caching is complete.
    //
    STDMETHOD_(void, OnCacheComplete)(
        __in HRESULT hrStatus
        ) = 0;

    // OnExecuteBegin - called when the engine begins executing the plan.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnExecuteBegin)(
        __in DWORD cExecutingPackages
        ) = 0;

    // OnExecuteBegin - called when the engine begins executing a package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnExecutePackageBegin)(
        __in_z LPCWSTR wzPackageId,
        __in BOOL fExecute
        ) = 0;

    // OnExecutePatchTarget - called when the engine executes one or more patches targeting
    //                        a product.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnExecutePatchTarget)(
        __in_z LPCWSTR wzPackageId,
        __in_z LPCWSTR wzTargetProductCode
        ) = 0;

    // OnExecuteProgress - called when the engine makes progress executing a package.
    //
    // Return:
    //  IDCANCEL instructs the engine to stop applying.
    //
    //  IDNOACTION instructs the engine to continue.
    STDMETHOD_(int, OnExecuteProgress)(
        __in_z LPCWSTR wzPackageId,
        __in DWORD dwProgressPercentage,
        __in DWORD dwOverallPercentage
        ) = 0;

    // OnExecuteMsiMessage - called when the engine receives an MSI package message.
    //
    // Return:
    //  uiFlags is a combination of valid ID* return values appropriate for
    //          the message.
    //
    //  IDNOACTION instructs the engine to pass the message through to default
    //             handling which usually results in the execution continuing.
    STDMETHOD_(int, OnExecuteMsiMessage)(
        __in_z LPCWSTR wzPackageId,
        __in INSTALLMESSAGE mt,
        __in UINT uiFlags,
        __in_z LPCWSTR wzMessage,
        __in DWORD cData,
        __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
        __in int nRecommendation
        ) = 0;

    // OnExecuteFilesInUse - called when the engine encounters files in use while
    //                       executing a package.
    //
    // Return:
    //  IDOK instructs the engine to let the Restart Manager attempt to close the
    //       applications to avoid a restart.
    //
    //  IDCANCEL instructs the engine to abort the execution and start rollback.
    //
    //  IDIGNORE instructs the engine to ignore the running applications. A restart will be
    //           required.
    //
    //  IDRETRY instructs the engine to check if the applications are still running again.
    //
    //  IDNOACTION is equivalent to ignoring the running applications. A restart will be
    //             required.
    STDMETHOD_(int, OnExecuteFilesInUse)(
        __in_z LPCWSTR wzPackageId,
        __in DWORD cFiles,
        __in_ecount_z(cFiles) LPCWSTR* rgwzFiles
        ) = 0;

    // OnExecutePackageComplete - called when a package execution is complete.
    //
    // Parameters:
    //  restart will indicate whether this package requires a reboot or initiated the reboot already.
    //
    // Return:
    //  IDIGNORE instructs the engine to ignore non-vital package failures and continue with the
    //           install. Ignored if hrStatus is a success or the package is vital.
    //
    //  IDRETRY instructs the engine to try the execution of the package again. Ignored if hrStatus
    //          is a success.
    //
    //  IDRESTART instructs the engine to stop processing the chain and restart. The engine will
    //            launch again after the machine is restarted.
    //
    //  IDSUSPEND instructs the engine to stop processing the chain and suspend the current state.
    //
    //  All other return codes are ignored.
    STDMETHOD_(int, OnExecutePackageComplete)(
        __in_z LPCWSTR wzPackageId,
        __in HRESULT hrStatus,
        __in BOOTSTRAPPER_APPLY_RESTART restart,
        __in int nRecommendation
        ) = 0;

    // OnExecuteComplete - called when the engine execution is complete.
    //
    STDMETHOD_(void, OnExecuteComplete)(
        __in HRESULT hrStatus
        ) = 0;

    // OnUnregisterBegin - called when the engine unregisters the bundle.
    //
    STDMETHOD_(void, OnUnregisterBegin)() = 0;

    // OnUnregisterComplete - called when the engine unregistration is complete.
    //
    STDMETHOD_(void, OnUnregisterComplete)(
        __in HRESULT hrStatus
        ) = 0;

    // OnApplyComplete - called after the plan has been applied.
    //
    // Parameters:
    //  restart will indicate whether any package required a reboot or initiated the reboot already.
    //
    // Return:
    //  IDRESTART instructs the engine to restart. The engine will not launch again after the machine
    //            is rebooted. Ignored if reboot was already initiated by OnExecutePackageComplete().
    //
    //  All other return codes are ignored.
    STDMETHOD_(int, OnApplyComplete)(
        __in HRESULT hrStatus,
        __in BOOTSTRAPPER_APPLY_RESTART restart
        ) = 0;
};

extern "C" typedef HRESULT (WINAPI *PFN_BOOTSTRAPPER_APPLICATION_CREATE)(
    __in IBootstrapperEngine* pEngine,
    __in const BOOTSTRAPPER_COMMAND* pCommand,
    __out IBootstrapperApplication** ppApplication
    );

extern "C" typedef void (WINAPI *PFN_BOOTSTRAPPER_APPLICATION_DESTROY)();


// 2.4.0 (more or less, the package.json change isn't committed) plus our PR


Package.registerBuildPlugin({
  name: "anotherCompiler",
  sources: ['plugin.js']
});

Package.onUse(function (api) {
  api.use('isobuild:compiler-plugin@1.0.0');
});


node('prop', seq(propertyName, token(':'), assignmentExpression)));

keep keep keep keep add keep keep keep keep

// Allow trailing comma in object literal, per ES5.  Trailing comma

// must follow a `name:value`, that is, `{,}` is invalid.

// We can't just use a normal comma list(), because it will seize

// on the comma as a sign that the list continues.  Instead,

// we specify a list of either ',' or nameColonValue, using positive

// and negative lookAheads to constrain the sequence.  The grammar

// is ordered so that error messages will always say

// "Expected propertyName" or "Expected ," as appropriate, not

// "Expected ," when the look-ahead is negative or "Expected }".

(function() {

var parserTestOptions = { includeComments: true };

var allNodeNames = [
  ";",
  "array",
  "assignment",
  "binary",
  "blockStmnt",
  "boolean",
  "bracket",
  "breakStmnt",
  "call",
  "case",
  "catch",
  "comma",
  "comment",
  "continueStmnt",
  "debuggerStmnt",
  "default",
  "doStmnt",
  "dot",
  "emptyStmnt",
  "expressionStmnt",
  "finally",
  "forInSpec",
  "forSpec",
  "forStmnt",
  "forVarInSpec",
  "forVarSpec",
  "functionDecl",
  "functionExpr",
  "idPropName",
  "identifier",
  "ifStmnt",
  "labelStmnt",
  "new",
  "newcall",
  "nil",
  "null",
  "numPropName",
  "number",
  "object",
  "parens",
  "postfix",
  "program",
  "prop",
  "regex",
  "returnStmnt",
  "strPropName",
  "string",
  "switchStmnt",
  "ternary",
  "this",
  "throwStmnt",
  "tryStmnt",
  "unary",
  "varDecl",
  "varStmnt",
  "whileStmnt",
  "withStmnt"
];

var allNodeNamesSet = {};
_.each(allNodeNames, function (n) { allNodeNamesSet[n] = true; });

var makeTester = function (test) {
  return {
    // Parse code and make sure it matches expectedTreeString.
    goodParse: function (code, expectedTreeString, regexTokenHints) {
      var expectedTree = ParseNode.unstringify(expectedTreeString);

      // first use lexer to collect all tokens
      var lexer = new JSLexer(code);
      var allTokensInOrder = [];
      while (! lexer.next().isEOF()) {
        var lex = lexer.lastLexeme;
        if (lex.isError())
          test.fail("Lexer error at " + lex.startPos());
        if (lex.isToken())
          allTokensInOrder.push(lex);
        if (regexTokenHints && regexTokenHints[allTokensInOrder.length])
          lexer.divisionPermitted = false;
      }

      var parser = new JSParser(code, parserTestOptions);
      var actualTree = parser.getSyntaxTree();

      var nextTokenIndex = 0;
      var check = function (tree) {
        if (tree instanceof ParseNode) {
          // This is a NODE (non-terminal).
          var nodeName = tree.name;
          if (! (nodeName && typeof nodeName === "string" &&
                 allNodeNamesSet[nodeName] === true))
            test.fail("Not a node name: " + nodeName);
          _.each(tree.children, check);
        } else if (typeof tree === 'object' &&
                   typeof tree.text === 'function') {
          // This is a TOKEN (terminal).
          // Make sure we are visiting every token once, in order.
          // Make an exception for any comment lexemes present,
          // because we couldn't know whether to include them in
          // allTokensInOrder.
          if (tree.type() !== "COMMENT") {
            if (nextTokenIndex >= allTokensInOrder.length)
              test.fail("Too many tokens: " + (nextTokenIndex + 1));
            var referenceToken = allTokensInOrder[nextTokenIndex++];
            if (tree.text() !== referenceToken.text())
              test.fail(tree.text() + " !== " + referenceToken.text());
            if (tree.startPos() !== referenceToken.startPos())
              test.fail(tree.startPos() + " !== " + referenceToken.startPos());
            if (code.substring(tree.startPos(), tree.endPos()) !== tree.text())
              test.fail("Didn't see " + tree.text() + " at " + tree.startPos() +
                        " in " + code);
          }
        } else {
          test.fail("Unknown tree part: " + tree);
        }
      };

      check(actualTree);
      if (nextTokenIndex !== allTokensInOrder.length)
        test.fail("Too few tokens: " + nextTokenIndex);

      test.equal(parser.pos, code.length);

      test.equal(ParseNode.stringify(actualTree),
                 ParseNode.stringify(expectedTree), code);
    },
    // Takes code with part of it surrounding with backticks.
    // Removes the two backtick characters, tries to parse the code,
    // and then asserts that there was a tokenization-level error,
    // with the part that was between the backticks called out as
    // the bad token.
    //
    // For example, the test "123`@`" will try to parse "123@" and
    // assert that a tokenization error occurred at '@'.
    badToken: function (code) {
      var constructMessage = function (pos, text) {
        return "Bad token at position " + pos + ", text `" + text + "`";
      };
      var pos = code.indexOf('`');
      var text = code.match(/`(.*?)`/)[1];
      code = code.replace(/`/g, '');

      var parsed = false;
      var error = null;
      try {
        var tree = new JSParser(code, parserTestOptions).getSyntaxTree();
        parsed = true;
      } catch (e) {
        error = e;
      }
      test.isFalse(parsed);
      test.isTrue(error);
      test.equal(error.message, constructMessage(pos, text));
    },
    // Takes code with a backtick-quoted string embedded in it.
    // Removes the backticks and their contents, tries to parse the code,
    // and then asserts that there was a parse error at the location
    // where the backtick-quoted string was embedded.  The embedded
    // string must match whatever the error message says was "expected".
    //
    // For example, the test "{`statement`" will try to parse the code
    // "{" and then assert that an error occured at the end of the string
    // saying "Expected statement".  The test "1 `semicolon`2" will try
    // to parse "1 2" and assert that the error "Expected semicolon"
    // appeared after the space and before the 2.
    //
    // A second backtick-quoted string is used as the "found" token
    // in the error message.
    badParse: function (code) {
      var constructMessage = function (whatExpected, pos, found, after) {
        return "Expected " + whatExpected + (after ? " after " + after : "") +
          " at position " + pos + ", found " + found;
      };
      var pos = code.indexOf('`');

      var backticked = code.match(/`.*?`/g);
      var whatExpected = backticked[0] && backticked[0].slice(1,-1);
      var found = backticked[1] && backticked[1].slice(1, -1);
      code = code.replace(/`.*?`/g, '');

      var parsed = false;
      var error = null;
      var parser = new JSParser(code, parserTestOptions);
      try {
        var tree = parser.getSyntaxTree();
        parsed = true;
      } catch (e) {
        error = e;
      }
      test.isFalse(parsed);
      test.isTrue(error);
      if (! parsed && error) {
        var after = parser.oldToken;
        found = (found || parser.newToken);
        test.equal(error.message,
                   constructMessage(whatExpected, pos, found, after));
      }
    }
  };
};

Tinytest.add("jsparse - basics", function (test) {
  var tester = makeTester(test);
  tester.goodParse('1', "program(expressionStmnt(number(1) ;()))");
  tester.goodParse('1 + 1', "program(expressionStmnt(binary(number(1) + number(1)) ;()))");
  tester.goodParse('1*2+3*4', "program(expressionStmnt(binary(binary(number(1) * number(2)) + " +
                "binary(number(3) * number(4))) ;()))");
  tester.goodParse('1 + 1;', "program(expressionStmnt(binary(number(1) + number(1)) ;))");
  tester.goodParse('1 + 1;;', "program(expressionStmnt(binary(number(1) + number(1)) ;) emptyStmnt(;))");
  tester.goodParse('', "program()");
  tester.goodParse('\n', "program()");
  tester.goodParse(';;;\n\n;\n', "program(emptyStmnt(;) emptyStmnt(;) emptyStmnt(;) emptyStmnt(;))");
  tester.goodParse('foo', "program(expressionStmnt(identifier(foo) ;()))");
  tester.goodParse('foo();', "program(expressionStmnt(call(identifier(foo) `(` `)`) ;))");
  tester.goodParse('var x = 3', "program(varStmnt(var varDecl(x = number(3)) ;()))");
  tester.goodParse('++x;', "program(expressionStmnt(unary(++ identifier(x)) ;))");
  tester.goodParse('x++;', "program(expressionStmnt(postfix(identifier(x) ++) ;))");
  tester.goodParse(
    'throw new Error',
    "program(throwStmnt(throw new(new identifier(Error)) ;()))");
  tester.goodParse(
    'var x = function () { return 123; };',
    'program(varStmnt(var varDecl(x = functionExpr(function nil() `(` `)` ' +
      '{ returnStmnt(return number(123) ;) })) ;))');

  tester.badParse("var x = `expression`");
  tester.badParse("1 `semicolon`1");
  tester.badParse("1+1`semicolon`:");
});

Tinytest.add("jsparse - tokenization errors", function (test) {
  var tester = makeTester(test);
  tester.badToken("123`@`");
  tester.badToken("thisIsATestOf = `'unterminated `\n strings'");
});

Tinytest.add("jsparse - syntax forms", function (test) {
  var tester = makeTester(test);
  var trials = [
    // STATEMENTS
    ['1',
     'program(expressionStmnt(number(1) ;()))'],
    ['1;;;;2',
     'program(expressionStmnt(number(1) ;) emptyStmnt(;) emptyStmnt(;) emptyStmnt(;) ' +
     'expressionStmnt(number(2) ;()))'],
    ['{}',
     'program(blockStmnt({ }))'],
    ['{null}',
     'program(blockStmnt({ expressionStmnt(null(null) ;()) }))'],
    ['{\nfoo()\nbar();\n}',
     'program(blockStmnt({ expressionStmnt(call(identifier(foo) `(` `)`) ;()) ' +
     'expressionStmnt(call(identifier(bar) `(` `)`) ;) }))'],
    ['{{{}}}',
     'program(blockStmnt({ blockStmnt({ blockStmnt({ }) }) }))'],
    ['var x = y, z,\n  a = b = c;',
     'program(varStmnt(var varDecl(x = identifier(y)) , varDecl(z) , varDecl(a = ' +
     'assignment(identifier(b) = identifier(c))) ;))'],
    ['if (x === y);',
     'program(ifStmnt(if `(` binary(identifier(x) === identifier(y)) `)` emptyStmnt(;)))'],
    ['if (z) return',
     'program(ifStmnt(if `(` identifier(z) `)` returnStmnt(return nil() ;())))'],
    ['if (a) b; else c',
     'program(ifStmnt(if `(` identifier(a) `)` expressionStmnt(identifier(b) ;) else ' +
     'expressionStmnt(identifier(c) ;())))'],
    ['if (n === 1) { foo(); } else if (n === 2) { bar(); } else { baz(); }',
     'program(ifStmnt(if `(` binary(identifier(n) === number(1)) `)` blockStmnt(' +
     '{ expressionStmnt(call(identifier(foo) `(` `)`) ;) }) else ifStmnt(' +
     'if `(` binary(identifier(n) === number(2)) `)` blockStmnt(' +
     '{ expressionStmnt(call(identifier(bar) `(` `)`) ;) }) else blockStmnt(' +
     '{ expressionStmnt(call(identifier(baz) `(` `)`) ;) }))))'],
    ['while (false);',
     'program(whileStmnt(while `(` boolean(false) `)` emptyStmnt(;)))'],
    ['while (/foo/.test(bar.baz)) {\n  bar = bar.baz;\n}',
     'program(whileStmnt(while `(` call(dot(regex(/foo/) . test) `(` ' +
     'dot(identifier(bar) . baz) `)`) `)` blockStmnt({ expressionStmnt(' +
     'assignment(identifier(bar) = dot(identifier(bar) . baz)) ;) })))'],
    ['while (false) while (false);',
     'program(whileStmnt(while `(` boolean(false) `)` ' +
     'whileStmnt(while `(` boolean (false) `)` emptyStmnt(;))))'],
    ['do a; while (b);',
     'program(doStmnt(do expressionStmnt(identifier(a) ;) while `(` identifier(b) `)` ;))'],
    ['do { x-- } while (x);',
     'program(doStmnt(do blockStmnt({ expressionStmnt(postfix(identifier(x) --) ;()) }) ' +
     'while `(` identifier(x) `)` ;))'],
    ['do a\n while (b)\n x++',
     'program(doStmnt(do expressionStmnt(identifier(a) ;()) while `(` identifier(b) `)` ;()) ' +
     'expressionStmnt(postfix(identifier(x) ++) ;()))'],
    ["for(;;);",
     "program(forStmnt(for `(` forSpec(nil() ; nil() ; nil()) `)` emptyStmnt(;)))"],
    ["for(x in y);",
     "program(forStmnt(for `(` forInSpec(identifier(x) in identifier(y)) `)` emptyStmnt(;)))"],
    ["for(var x in y);",
     "program(forStmnt(for `(` forVarInSpec(var varDecl(x) in identifier(y)) `)` emptyStmnt(;)))"],
    ["for(var x;;);",
     "program(forStmnt(for `(` forVarSpec(var varDecl(x) ; nil() ; nil()) `)` emptyStmnt(;)))"],
    ["for(var i=0;i<N;i++) {}",
     "program(forStmnt(for `(` forVarSpec(var varDecl(i = number(0)) ; " +
     "binary(identifier(i) < identifier(N)) ; postfix(identifier(i) ++)) `)` blockStmnt({ })))"],
    ["for (var x=3 in y);",
     "program(forStmnt(for `(` forVarInSpec(var varDecl(x = number(3)) in identifier(y)) `)` " +
     "emptyStmnt(;)))"],
    ["for (x.foo in y);",
     "program(forStmnt(for `(` forInSpec(dot(identifier(x) . foo) in identifier(y)) `)` emptyStmnt(;)))"],
    ["return",
     "program(returnStmnt(return nil() ;()))"],
    ["return;",
     "program(returnStmnt(return nil() ;))"],
    ["return null",
     "program(returnStmnt(return null(null) ;()))"],
    ["return null;",
     "program(returnStmnt(return null(null) ;))"],
    ["return\n1+1",
     "program(returnStmnt(return nil() ;()) expressionStmnt(binary(number(1) + number(1)) ;()))"],
    ["return 1\n  +1",
     "program(returnStmnt(return binary(number(1) + number(1)) ;()))"],
    ["continue",
     "program(continueStmnt(continue nil() ;()))"],
    ["continue foo",
     "program(continueStmnt(continue foo ;()))"],
    ["continue foo;",
     "program(continueStmnt(continue foo ;))"],
    ["continue\n  foo;",
     "program(continueStmnt(continue nil() ;()) expressionStmnt(identifier(foo) ;))"],
    ["break",
     "program(breakStmnt(break nil() ;()))"],
    ["break foo",
     "program(breakStmnt(break foo ;()))"],
    ["break foo;",
     "program(breakStmnt(break foo ;))"],
    ["break\n  foo;",
     "program(breakStmnt(break nil() ;()) expressionStmnt(identifier(foo) ;))"],
    ["throw e;",
     "program(throwStmnt(throw identifier(e) ;))"],
    ["throw e",
     "program(throwStmnt(throw identifier(e) ;()))"],
    ["throw new Error;",
     "program(throwStmnt(throw new(new identifier(Error)) ;))"],
    ["with(x);",
     "program(withStmnt(with `(` identifier(x) `)` emptyStmnt(;)))"],
    ["with(a=b) {}",
     "program(withStmnt(with `(` assignment(identifier(a) = identifier(b)) `)` blockStmnt({ })))"],
    ["switch(x) {}",
     "program(switchStmnt(switch `(` identifier(x) `)` { }))"],
    ["switch(x) {case 1:case 2:case 3:default:case 4:}",
     "program(switchStmnt(switch `(` identifier(x) `)` { " +
     "case(case number(1) :) case(case number(2) :) case(case number(3) :) " +
     "default(default :) case(case number(4) :) }))"],
    ["switch(x) {\ncase 1:\n  return\ncase 2:\ncase 3:\n  throw e}",
     "program(switchStmnt(switch `(` identifier(x) `)` { " +
     "case(case number(1) : returnStmnt(return nil() ;())) " +
     "case(case number(2) :) case(case number(3) : " +
     "throwStmnt(throw identifier(e) ;())) }))"],
    ["switch(x) {default:;}",
     "program(switchStmnt(switch `(` identifier(x) `)` { default(default : emptyStmnt(;)) }))"],
    ["try {} catch (e) {} finally {}",
     "program(tryStmnt(try blockStmnt({ }) catch(catch `(` e `)` blockStmnt({ })) " +
     "finally(finally blockStmnt({ }))))"],
    ["try {} finally {}",
     "program(tryStmnt(try blockStmnt({ }) nil() finally(finally blockStmnt({ }))))"],
    ["try {} catch (e) {}",
     "program(tryStmnt(try blockStmnt({ }) catch(catch `(` e `)` blockStmnt({ })) nil()))"],
    ["a:;",
     "program(labelStmnt(a : emptyStmnt(;)))"],
    ["{x:1}",
     "program(blockStmnt({ labelStmnt(x : expressionStmnt(number(1) ;())) }))"],
    ["{x:y:z:1}",
     "program(blockStmnt({ labelStmnt(x : labelStmnt(y : " +
     "labelStmnt(z : expressionStmnt(number(1) ;())))) }))"],
    [";;foo:\nfor(;;);",
     "program(emptyStmnt(;) emptyStmnt(;) labelStmnt(foo : " +
     "forStmnt(for `(` forSpec(nil() ; nil() ; nil()) `)` emptyStmnt(;))))"],
    ["debugger",
     "program(debuggerStmnt(debugger ;()))"],
    ["debugger;",
     "program(debuggerStmnt(debugger ;))"],
    ["function foo() {}",
     "program(functionDecl(function foo `(` `)` { }))"],
    ["function foo() {function bar() {}}",
     "program(functionDecl(function foo `(` `)` { functionDecl(function bar `(` `)` { }) }))"],
    [";;function f() {};;",
     "program(emptyStmnt(;) emptyStmnt(;) functionDecl(function f `(` `)` { }) " +
     "emptyStmnt(;) emptyStmnt(;))"],
    ["function foo(a,b,c) {}",
     "program(functionDecl(function foo `(` a , b , c `)` { }))"],

    // EXPRESSIONS
    ["null + this - 3 + true",
     "program(expressionStmnt(binary(binary(binary(null(null) + this(this)) - " +
     "number(3)) + boolean(true)) ;()))"],
    ["a / /b/mgi / c",
     "program(expressionStmnt(binary(binary(identifier(a) / " +
     "regex(/b/mgi)) / identifier(c)) ;()))"],
    ["'a' + \"\" + \"b\" + '\\''",
     "program(expressionStmnt(binary(binary(binary(string('a') + string(\"\")) + " +
     "string(\"b\")) + string('\\'')) ;()))"],
    ["_ + x0123 + $",
     "program(expressionStmnt(binary(binary(identifier(_) + " +
     "identifier(x0123)) + identifier($)) ;()))"],
    ["if ((x = 1)) return ((1+2))*((1<<2));",
     "program(ifStmnt(if `(` parens(`(` assignment(identifier(x) = number(1)) `)`) " +
     "`)` returnStmnt(return binary(parens(`(` parens(`(` binary(number(1) + " +
     "number(2)) `)`) `)`) * parens(`(` parens(`(` binary(number(1) << number(2)) " +
     "`)`) `)`)) ;)))"],
    ["[];",
     "program(expressionStmnt(array([ ]) ;))"],
    ["[,,,];",
     "program(expressionStmnt(array([ , , , ]) ;))"],
    ["[(1,2),,3];",
     "program(expressionStmnt(array([ parens(`(` comma(number(1) , " +
     "number(2)) `)`) , , number(3) ]) ;))"],
    ["({});",
     "program(expressionStmnt(parens(`(` object({ }) `)`) ;))"],
    ["({1:1});",
     "program(expressionStmnt(parens(`(` object({ prop(numPropName(1) : number(1)) }) `)`) ;))"],
    ["({x:true});",
     "program(expressionStmnt(parens(`(` object({ prop(idPropName(x) : boolean(true)) }) `)`) ;))"],
    ["({'a':b, c:'d', 1:null});",
     "program(expressionStmnt(parens(`(` object({ prop(strPropName('a') : " +
     "identifier(b)) , prop(idPropName(c) : string('d')) , prop(numPropName(1) " +
     ": null(null)) }) `)`) ;))"],
    ["(function () {});",
     "program(expressionStmnt(parens(`(` functionExpr(function nil() `(` `)` { }) `)`) ;))"],
    ["(function foo() {});",
     "program(expressionStmnt(parens(`(` functionExpr(function foo `(` `)` { }) `)`) ;))"],
    ["x = function () {}.y;",
     "program(expressionStmnt(assignment(identifier(x) = dot(functionExpr(" +
     "function nil() `(` `)` { }) . y)) ;))"],
    ["(function (a) {})",
     "program(expressionStmnt(parens(`(` functionExpr(function nil() " +
     "`(` a `)` { }) `)`) ;()))"],
    ["(function (a,b,c) {})",
     "program(expressionStmnt(parens(`(` functionExpr(function nil() `(` " +
     "a , b , c `)` { }) `)`) ;()))"],
    ["foo.bar.baz;",
     "program(expressionStmnt(dot(dot(identifier(foo) . bar) . baz) ;))"],
    ["foo[bar,bar][baz].qux[1+1];",
     "program(expressionStmnt(bracket(dot(bracket(bracket(identifier(foo) " +
     "[ comma(identifier(bar) , identifier(bar)) ]) [ identifier(baz) ]) . qux) " +
     "[ binary(number(1) + number(1)) ]) ;))"],
    ["new new a.b.c[d]",
     "program(expressionStmnt(new(new new(new bracket(dot(dot(identifier(a) " +
     ". b) . c) [ identifier(d) ]))) ;()))"],
    ["new new a.b.c[d]()",
     "program(expressionStmnt(new(new newcall(new " +
     "bracket(dot(dot(identifier(a) . b) . c) [ identifier(d) ]) `(` `)`)) ;()))"],
    ["new new a.b.c[d]()()",
     "program(expressionStmnt(newcall(new newcall(new " +
     "bracket(dot(dot(identifier(a) . b) . c) [ identifier(d) ]) `(` `)`) `(` `)`) ;()))"],
    ["new foo(x).bar(y)",
     "program(expressionStmnt(call(dot(newcall(new identifier(foo) `(` " +
     "identifier(x) `)`) . bar) `(` identifier(y) `)`) ;()))"],
    ["new new foo().bar",
     "program(expressionStmnt(new(new dot(newcall(new identifier(foo) `(` `)`) . bar)) ;()))"],
    ["delete void typeof - + ~ ! -- ++ x;",
     "program(expressionStmnt(unary(delete unary(void unary(typeof unary(- unary(+ " +
     "unary(~ unary(! unary(-- unary(++ identifier(x)))))))))) ;))"],
    ["x++ + ++y",
     "program(expressionStmnt(binary(postfix(identifier(x) ++) + " +
     "unary(++ identifier(y))) ;()))"],
    ["1*2+3*4",
     "program(expressionStmnt(binary(binary(number(1) * number(2)) " +
     "+ binary(number(3) * number(4))) ;()))"],
    ["a*b/c%d+e-f<<g>>h>>>i<j>k<=l>=m instanceof n in o==p!=q===r!==s&t^u|v&&w||x",
     "program(expressionStmnt(binary(binary(binary(binary(binary(binary(binary(" +
     "binary(binary(binary(binary(binary(binary(binary(binary(binary(binary(binary(" +
     "binary(binary(binary(binary(binary(identifier(a) * identifier(b)) / " +
     "identifier(c)) % identifier(d)) + identifier(e)) - identifier(f)) << identifier(g)) " +
     ">> identifier(h)) >>> identifier(i)) < identifier(j)) > identifier(k)) <= " +
     "identifier(l)) >= identifier(m)) instanceof identifier(n)) in identifier(o)) == " +
     "identifier(p)) != identifier(q)) === identifier(r)) !== identifier(s)) & " +
     "identifier(t)) ^ identifier(u)) | identifier(v)) && identifier(w)) || " +
     "identifier(x)) ;()))"],
    ["a||b&&c|d^e&f!==g===h!=i==j in k instanceof l>=m<=n<o<p>>>q>>r<<s-t+u%v/w*x",
     "program(expressionStmnt(binary(identifier(a) || binary(identifier(b) && " +
     "binary(identifier(c) | binary(identifier(d) ^ binary(identifier(e) & " +
     "binary(binary(binary(binary(identifier(f) !== identifier(g)) === identifier(h)) " +
     "!= identifier(i)) == binary(binary(binary(binary(binary(binary(identifier(j) in " +
     "identifier(k)) instanceof identifier(l)) >= identifier(m)) <= identifier(n)) < " +
     "identifier(o)) < binary(binary(binary(identifier(p) >>> identifier(q)) >> " +
     "identifier(r)) << binary(binary(identifier(s) - identifier(t)) + " +
     "binary(binary(binary(identifier(u) % identifier(v)) / identifier(w)) * " +
     "identifier(x))))))))))) ;()))"],
    ["a?b:c",
     "program(expressionStmnt(ternary(identifier(a) ? identifier(b) : " +
     "identifier(c)) ;()))"],
    ["1==2?3=4:5=6",
     "program(expressionStmnt(ternary(binary(number(1) == number(2)) ? " +
     "assignment(number(3) = number(4)) : assignment(number(5) = number(6))) ;()))"],
    ["a=b,c=d",
     "program(expressionStmnt(comma(assignment(identifier(a) = identifier(b)) , " +
     "assignment(identifier(c) = identifier(d))) ;()))"],
    ["a=b=c=d",
     "program(expressionStmnt(assignment(identifier(a) = assignment(identifier(b) " +
     "= assignment(identifier(c) = identifier(d)))) ;()))"],
    ["x[0]=x[1]=true",
     "program(expressionStmnt(assignment(bracket(identifier(x) [ number(0) ]) = " +
     "assignment(bracket(identifier(x) [ number(1) ]) = boolean(true))) ;()))"],
    ["a*=b/=c%=d+=e-=f<<=g>>=h>>>=i&=j^=k|=l",
     "program(expressionStmnt(assignment(identifier(a) *= assignment(identifier(b) " +
     "/= assignment(identifier(c) %= assignment(identifier(d) += " +
     "assignment(identifier(e) -= assignment(identifier(f) <<= " +
     "assignment(identifier(g) >>= assignment(identifier(h) >>>= " +
     "assignment(identifier(i) &= assignment(identifier(j) ^= " +
     "assignment(identifier(k) |= identifier(l)))))))))))) ;()))"],
    ["1;\n\n\n\n/* foo */\n// bar\n", // trailing whitespace and comments
     "program(expressionStmnt(number(1) ;) comment(`/* foo */`) comment(`// bar`))"],
    // includeComments option; comments in AST
    ["//foo",
     "program(comment(//foo))"],
    ["//foo\n",
     "program(comment(//foo))"],
    ["/*foo*/",
     "program(comment(/*foo*/))"],
    ["/*foo*/\n",
     "program(comment(/*foo*/))"],
    [";\n//foo",
     "program(emptyStmnt(;) comment(//foo))"],
    [";\n/*foo*/",
     "program(emptyStmnt(;) comment(/*foo*/))"],
    [";\n//foo\n;",
     "program(emptyStmnt(;) comment(//foo) emptyStmnt(;))"],
    [";\n/*foo*/\n;",
     "program(emptyStmnt(;) comment(/*foo*/) emptyStmnt(;))"],
    [";\n//foo\n//bar\n;",
     "program(emptyStmnt(;) comment(//foo) comment(//bar) emptyStmnt(;))"],
    [";\n/*foo*/ /*bar*/\n;",
     "program(emptyStmnt(;) comment(/*foo*/) comment(/*bar*/) emptyStmnt(;))"],
    [";//foo\n//bar\n;",
     "program(emptyStmnt(;) comment(//bar) emptyStmnt(;))"],
    [";/*foo*/\n/*bar*/\n;",
     "program(emptyStmnt(;) comment(/*bar*/) emptyStmnt(;))"],
    [";/*foo*//*bar*///baz\n;",
     "program(emptyStmnt(;) emptyStmnt(;))"],
    [";/*foo*//*bar*///baz",
     "program(emptyStmnt(;))"],
    ["/*foo*//*bar*///baz",
     "program(comment(/*foo*/) comment(/*bar*/) comment(//baz))"],
    ["//foo\n//bar\nfunction aaa() {}\nfunction bbb() {}",
     "program(comment(//foo) comment(//bar) functionDecl(function aaa `(` `)` { }) " +
     "functionDecl(function bbb `(` `)` { }))"],
    // comments don't interfere with parse
    ["if (true)\n//comment\nfoo();",
     "program(ifStmnt(if `(` boolean(true) `)` " +
     "expressionStmnt(call(identifier(foo) `(` `)`) ;)))"],
    // bare keywords allowed in property access and object literal
    ["foo.return();",
     "program(expressionStmnt(call(dot(identifier(foo) . return) `(` `)`) ;))"],
    ["foo.true();",
     "program(expressionStmnt(call(dot(identifier(foo) . true) `(` `)`) ;))"],
    ["foo.null();",
     "program(expressionStmnt(call(dot(identifier(foo) . null) `(` `)`) ;))"],
    ["({true:3})",
     "program(expressionStmnt(parens(`(` object({ prop(idPropName(true) : number(3)) }) `)`) ;()))"],
    ["({null:3})",
     "program(expressionStmnt(parens(`(` object({ prop(idPropName(null) : number(3)) }) `)`) ;()))"],
    ["({if:3})",
     "program(expressionStmnt(parens(`(` object({ prop(idPropName(if) : number(3)) }) `)`) ;()))"],
    // ES5 line continuations in string literals
    ["var x = 'a\\\nb\\\nc';",
     "program(varStmnt(var varDecl(x = string(`'a\\\nb\\\nc'`)) ;))"]
  ];
  _.each(trials, function (tr) {
    tester.goodParse(tr[0], tr[1]);
  });
});

Tinytest.add("jsparse - bad parses", function (test) {
  var tester = makeTester(test);
  var trials = [
    '{`statement`',
    'if (`expression`)',
    'if `(`else',
    'var`varDecl`;',
    'while (`expression`);',
    'while`(`;',
    'do a `semicolon`while b;',
    'do a\n while `(`b;',
    '1 `semicolon`2',
    'for (`forSpec`);',
    'for (1\n`semicolon`2\n3);',
    'continue `semicolon`1+1;',
    'break `semicolon`1+1;',
    'throw`expression`',
    'throw`expression`;',
    'throw\n`expression`',
    'throw\n`expression``end of line`e',
    'throw `expression`=;',
    'with(`expression`);',
    'switch(`expression`)',
    'switch(x)`{`;',
    'try`block`',
    'try {}`catch`',
    'try {} catch`(`;',
    'try {} catch(e)`block`;',
    '1+1`semicolon`:',
    '{a:`statement`}',
    'function `IDENTIFIER`() {}',
    'foo: `statement`function foo() {}',
    '[`expression`=',
    '[,,`expression`=',
    '({`propertyName`|:3})',
    '({1:2,3`:`})',
    '({1:2,`propertyName`',
    'x.`IDENTIFIER`,',
    'foo;`semicolon`:;',
    '1;`statement`=',
    'a+b`semicolon`=c;',
    'for(1+1 `semicolon`in {});',
    '`statement`=',
    'for(;`expression`var;) {}'
  ];
  _.each(trials, function (tr) {
    tester.badParse(tr);
  });
});

Tinytest.add("jsparse - regex division ambiguity", function (test) {
  var tester = makeTester(test);
  tester.goodParse("if (e) /f/g;",
                   "program(ifStmnt(if `(` identifier(e) `)` expressionStmnt(regex(/f/g) ;)))",
                   {4: true});
  tester.goodParse("++/x/.y;",
                   "program(expressionStmnt(unary(++ dot(regex(/x/) . y)) ;))",
                   {1: true});
  tester.goodParse("x++/2/g;",
                   "program(expressionStmnt(binary(binary(postfix(identifier(x) ++) / " +
                   "number(2)) / identifier(g)) ;))");
  tester.goodParse("(1+1)/2/g;",
                   "program(expressionStmnt(binary(binary(parens(`(` binary(number(1) + " +
                   "number(1)) `)`) / " +
                   "number(2)) / identifier(g)) ;))");
  tester.goodParse("/x/",
                   "program(expressionStmnt(regex(/x/) ;()))");
});

Tinytest.add("jsparse - semicolon insertion", function (test) {
  var tester = makeTester(test);
  // Spec section 7.9.2
  tester.badParse("{ 1 `semicolon`2 } 3");
  tester.goodParse("{ 1\n2 } 3", "program(blockStmnt({ expressionStmnt(number(1) " +
                   ";()) expressionStmnt(number(2) ;()) }) expressionStmnt(number(3) ;()))");
  tester.badParse("for (a; b\n`semicolon`)");
  tester.goodParse("return\na + b",
                   "program(returnStmnt(return nil() ;()) " +
                   "expressionStmnt(binary(identifier(a) + identifier(b)) ;()))");
  tester.goodParse("a = b\n++c",
                   "program(expressionStmnt(assignment(identifier(a) = identifier(b)) ;())" +
                   "expressionStmnt(unary(++ identifier(c)) ;()))");
  tester.badParse("if (a > b)\n`statement`else c = d");
  tester.goodParse("a = b + c\n(d + e).print()",
                   "program(expressionStmnt(assignment(identifier(a) = " +
                   "binary(identifier(b) + call(dot(call(identifier(c) `(` " +
                   "binary(identifier(d) + identifier(e)) `)`) . print) `(` `)`))) ;()))");
});

Tinytest.add("jsparse - comments", function (test) {
  var tester = makeTester(test);
  // newline in multi-line comment makes it into a line break for semicolon
  // insertion purposes
  tester.badParse("1/**/`semicolon`2");
  tester.goodParse("1/*\n*/2",
                   "program(expressionStmnt(number(1) ;()) expressionStmnt(number(2) ;()))");
});

Tinytest.add("jsparse - initial lex error", function (test) {
  var doTest = function (code) {
    // this shouldn't throw
    var parser = new JSParser(code, parserTestOptions);
    // this should throw
    try {
      parser.getSyntaxTree();
      test.fail();
    } catch (e) {
      test.isTrue(/^Bad token/.test(e.message), e.message);
    }
  };

  doTest('/');
  doTest('@');
});

})();


/* http://meyerweb.com/eric/tools/css/reset/
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}


(function() {

  Meteor._partials = {};

  // XXX Handlebars hooking is janky and gross

  Meteor._hook_handlebars = function () {
    Meteor._hook_handlebars = function(){}; // install the hook only once

    var orig = Handlebars._default_helpers.each;
    Handlebars._default_helpers.each = function (arg, options) {
      // if arg isn't an observable (like LocalCollection.Cursor),
      // don't use this reactive implementation of #each.
      if (!(arg && 'observe' in arg))
        return orig.call(this, arg, options);

      return Spark.list(
        arg,
        function (item) {
          return Spark.labelBranch(
            item._id || Spark.UNIQUE_LABEL, function () {
              var html = Spark.isolate(_.bind(options.fn, null, item));
              return Spark.setDataContext(item, html);
            });
        },
        function () {
          return options.inverse ?
            Spark.isolate(options.inverse) : '';
        }
      );
    };

    _.extend(Handlebars._default_helpers, {
      isolate: function (options) {
        var data = this;
        return Spark.isolate(function () {
          return options.fn(data);
        });
      },
      constant: function (options) {
        var data = this;
        return Spark.createLandmark({ constant: true }, function () {
          return options.fn(data);
        });
      }
    });
  };

  // map from landmark id, to the 'this' object for
  // created/rendered/destroyed callbacks on templates
  var templateInstanceData = {};

  var templateObjFromLandmark = function (landmark) {
    var template = templateInstanceData[landmark.id] || (
      templateInstanceData[landmark.id] = {
        // set these once
        find: function (selector) {
          if (! landmark.hasDom())
            throw new Error("Template not in DOM");
          return landmark.find(selector);
        },
        findAll: function (selector) {
          if (! landmark.hasDom())
            throw new Error("Template not in DOM");
          return landmark.findAll(selector);
        }
      });
    // set these each time
    template.firstNode = landmark.hasDom() ? landmark.firstNode() : null;
    template.lastNode = landmark.hasDom() ? landmark.lastNode() : null;
    return template;
  };

  // XXX forms hooks into this to add "bind"?
  Meteor._template_decl_methods = {
    // methods store data here (event map, etc.).  initialized per template.
    _tmpl_data: null,
    // these functions must be generic (i.e. use `this`)
    events: function (eventMap) {
      var events =
            (this._tmpl_data.events = (this._tmpl_data.events || {}));
      _.extend(events, eventMap);
    },
    preserve: function (preserveMap) {
      var preserve =
            (this._tmpl_data.preserve = (this._tmpl_data.preserve || {}));

      if (_.isArray(preserveMap))
        _.each(preserveMap, function (selector) {
          preserve[selector] = true;
        });
      else
        _.extend(preserve, preserveMap);
    },
    helpers: function (helperMap) {
      var helpers =
            (this._tmpl_data.helpers = (this._tmpl_data.helpers || {}));
      for(var h in helperMap)
        helpers[h] = helperMap[h];
    }
  };

  Meteor._def_template = function (name, raw_func) {
    Meteor._hook_handlebars();

    window.Template = window.Template || {};

    // Define the function assigned to Template.<name>.

    var partial = function (data) {
      var tmpl = name && Template[name] || {};
      var tmplData = tmpl._tmpl_data || {};

      var html = Spark.labelBranch("Template."+name, function () {
        var html = Spark.createLandmark({
          preserve: tmplData.preserve || {},
          created: function () {
            var template = templateObjFromLandmark(this);
            template.data = data;
            tmpl.created && tmpl.created.call(template);
          },
          rendered: function () {
            var template = templateObjFromLandmark(this);
            template.data = data;
            tmpl.rendered && tmpl.rendered.call(template);
          },
          destroyed: function () {
            // template.data is already set from previous callbacks
            tmpl.destroyed &&
              tmpl.destroyed.call(templateObjFromLandmark(this));
            delete templateInstanceData[this.id];
          }
        }, function (landmark) {
          var html = Spark.isolate(function () {
            // XXX Forms needs to run a hook before and after raw_func
            // (and receive 'landmark')
            return raw_func(data, {
              helpers: _.extend({}, partial, tmplData.helpers || {}),
              partials: Meteor._partials,
              name: name
            });
          });

          // take an event map with `function (event, template)` handlers
          // and produce one with `function (event, landmark)` handlers
          // for Spark, by inserting logic to create the template object.
          var wrapEventMap = function (oldEventMap) {
            var newEventMap = {};
            _.each(oldEventMap, function (handler, key) {
              newEventMap[key] = function (event, landmark) {
                return handler.call(this, event,
                                    templateObjFromLandmark(landmark));
              };
            });
            return newEventMap;
          };

          // support old Template.foo.events = {...} format
          var events =
                (tmpl.events !== Meteor._template_decl_methods.events ?
                 tmpl.events : tmplData.events);
          // events need to be inside the landmark, not outside, so
          // that when an event fires, you can retrieve the enclosing
          // landmark to get the template data
          if (tmpl.events)
            html = Spark.attachEvents(wrapEventMap(events), html);
          return html;
        });
        html = Spark.setDataContext(data, html);
        return html;
      });

      return html;
    };

    // XXX hack.. copy all of Handlebars' built in helpers over to
    // the partial. it would be better to hook helperMissing (or
    // something like that?) so that Template.foo is searched only
    // if it's not a built-in helper.
    _.extend(partial, Handlebars.helpers);

    if (name) {
      if (Template[name])
        throw new Error("There are multiple templates named '" + name +
                        "'. Each template needs a unique name.");

      Template[name] = partial;
      _.extend(partial, Meteor._template_decl_methods);
      partial._tmpl_data = {};

      Meteor._partials[name] = partial;
    }

    // useful for unnamed templates, like body
    return partial;
  };

})();


Timers = new Meteor.Collection(null);

///////////////////////////////////////////////////////////////////////////////

if (! Session.get("x")) {
  Session.set("x", 1);
}

if (! Session.get("y")) {
  Session.set("y", 1);
}

if (! Session.get("z")) {
  Session.set("z", 1);
}

Template.preserveDemo.x =
Template.constantDemo.x =
Template.stateDemo.x =
function () {
  return Session.get("x");
};

Template.timer.y = function () {
  return Session.get("y");
};

Template.stateDemo.z =
function () {
  return Session.get("z");
};

Template.page.events({
  'click input.x': function () {
    Session.set("x", Session.get("x") + 1);
  },

  'click input.y': function () {
    Session.set("y", Session.get("y") + 1);
  },

  'click input.z': function () {
    Session.set("z", Session.get("z") + 1);
  }
});

///////////////////////////////////////////////////////////////////////////////

if (typeof Session.get("spinForward") !== 'boolean') {
  Session.set("spinForward", true);
}

Template.preserveDemo.preserve([ '.spinner', '.spinforward' ]);

Template.preserveDemo.spinForwardChecked = function () {
  return Session.get('spinForward') ? 'checked="checked"' : '';
};

Template.preserveDemo.spinAnim = function () {
  return Session.get('spinForward') ? 'spinForward' : 'spinBackward';
};

Template.preserveDemo.events({
  'change .spinforward' : function (event) {
    Session.set('spinForward', event.currentTarget.checked);
  }
});

///////////////////////////////////////////////////////////////////////////////

Template.constantDemo.checked = function (which) {
  return Session.get('mapchecked' + which) ? 'checked="checked"' : '';
};

Template.constantDemo.show = function (which) {
  return ! Session.get('mapchecked' + which);
};

Template.constantDemo.events({
  'change .remove' : function (event) {
    var tgt = event.currentTarget;
    Session.set('mapchecked' + tgt.getAttribute("which"), tgt.checked);
  }
});

///////////////////////////////////////////////////////////////////////////////

Template.stateDemo.events({
  'click .create': function () {
    Timers.insert({});
  }
});

Template.stateDemo.timers = function () {
  return Timers.find();
};

Template.timer.events({
  'click .reset': function (event, template) {
    template.elapsed = 0;
    updateTimer(template);
  },
  'click .delete': function () {
    Timers.remove(this._id);
  }
});

var updateTimer = function (timer) {
  timer.node.innerHTML = timer.elapsed + " second" +
    ((timer.elapsed === 1) ? "" : "s");
};

Template.timer.created = function () {
  var self = this;
  self.elapsed = 0;
  self.node = null;
};

Template.timer.rendered = function () {
  var self = this;
  self.node = this.find(".elapsed");
  updateTimer(self);

  if (! self.timer) {
    var tick = function () {
      self.elapsed++;
      self.timer = setTimeout(tick, 1000);
      updateTimer(self);
    };
    tick();
  }
};

Template.timer.destroyed = function () {
  clearInterval(this.timer);
};

///////////////////////////////////////////////////////////////////////////////

Template.d3Demo.left = function () {
  return { group: "left" };
};

Template.d3Demo.right = function () {
  return { group: "right" };
};

Template.circles.events({
  'mousedown circle': function (evt, template) {
    Session.set("selectedCircle:" + this.group, evt.currentTarget.id);
  },
  'click .add': function () {
    Circles.insert({x: Meteor.random(), y: Meteor.random(),
                    r: Meteor.random() * .1 + .02,
                    color: {
                      r: Meteor.random(),
                      g: Meteor.random(),
                      b: Meteor.random()
                    },
                    group: this.group
                   });
  },
  'click .remove': function () {
    var selected = Session.get("selectedCircle:" + this.group);
    if (selected) {
      Circles.remove(selected);
      Session.set("selectedCircle:" + this.group, null);
    }
  },
  'click .scram': function () {
    Circles.find({group: this.group}).forEach(function (r) {
      Circles.update(r._id, {
        $set: {
          x: Meteor.random(), y: Meteor.random(), r: Meteor.random() * .1 + .02
        }
      });
    });
  },
  'click .clear': function () {
    Circles.remove({group: this.group});
  }
});

var colorToString = function (color) {
  var f = function (x) { return Math.floor(x * 256); };
  return "rgb(" + f(color.r) + "," +
    + f(color.g) + "," + + f(color.b) + ")";
};

Template.circles.count = function () {
  return Circles.find({group: this.group}).count();
};

Template.circles.disabled = function () {
  return Session.get("selectedCircle:" + this.group) ?
    '' : 'disabled="disabled"';
};

Template.circles.created = function () {
};

Template.circles.rendered = function () {
  var self = this;
  self.node = self.find("svg");

  var data = self.data;

  if (! self.handle) {
    d3.select(self.node).append("rect");
    self.handle = Meteor.autorun(function () {
      var circle = d3.select(self.node).selectAll("circle")
        .data(Circles.find({group: data.group}).fetch(),
              function (d) { return d._id; });

      circle.enter().append("circle")
        .attr("id", function (d) {
          return d._id;
        })
        .attr("cx", function (d) {
          return d.x * 272;
        })
        .attr("cy", function (d) {
          return d.y * 272;
        })
        .attr("r", 50)
        .style("fill", function (d) {
          return colorToString(d.color);
        })
        .style("opacity", 0);

      circle.transition()
        .duration(250)
        .attr("cx", function (d) {
          return d.x * 272;
        })
        .attr("cy", function (d) {
          return d.y * 272;
        })
        .attr("r", function (d) {
          return d.r * 272;
        })
        .style("fill", function (d) {
          return colorToString(d.color);
        })
        .style("opacity", .9)
        .ease("cubic-out");

      circle.exit().transition()
        .duration(250)
        .attr("r", 0)
        .remove();

      var selectionId = Session.get("selectedCircle:" + data.group);
      var s = selectionId && Circles.findOne(selectionId);
      var rect = d3.select(self.node).select("rect");
      if (s)
        rect.attr("x", (s.x - s.r) * 272)
        .attr("y", (s.y - s.r) * 272)
        .attr("width", s.r * 2 * 272)
        .attr("height", s.r * 2 * 272)
        .attr("display", '')
        .style("fill", "none")
        .style("stroke", "red")
        .style("stroke-width", 3);
      else
        rect.attr("display", 'none');
    });
  }
};

Template.circles.destroyed = function () {
  this.handle && this.handle.stop();
};


var functionMaybeNameRequired = booleanFlaggedParser(

keep keep keep keep replace replace replace keep keep keep


<title>Todos</title>

Todos

        <input id="new-todo" placeholder="What needs to be done?" type="text" />
        Press Enter to save this task

Double-click to edit a todo.

View the annotated source.

    Created by

    Jérôme Gravel-Niquet


/** CSS reset **/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    font-weight: inherit;
    font-style: inherit;
    font-family: inherit;
    font-size: 100%;
    vertical-align: baseline;
}
body {
  line-height: 1;
  color: black;
  background: white;
}
ol, ul {
  list-style: none;
}
a img {
  border: none;
}

/** Basics **/

body {
    font-family: 'Helvetica Neue', Helvetica, Arial;
}

pre {
    font-family: monospace;
}

code {
    font-family: monospace;
    font-size: 1.1em;
}

ul {
    list-style-type: disc;
    margin-top: 1em;
    margin-bottom: 1em;
    padding-left: 40px;
}

dl {
    margin-top: 1em;
    margin-bottom: 1em;
}

dd {
    margin-left: 40px;
}

h1, h2, h3, h4 {
    color: black;
    font-weight: bold;
}

h1 {
    font-size: 2em;
    letter-spacing: -1px;
}

h1 {
    margin: 1.5em 0 .67em 0;
}

h2 {
    font-size: 1.5em;
    margin: 1.25em 0 .5em 0;
    letter-spacing: -1px;
}

h3 {
    font-size: 1.17em;
    margin: 1em 0 1em 0;
    letter-spacing: -1px;
}

h4 {
}

p {
    margin-bottom: 1em;
}

em {
    font-style: italic;
}

a:hover {
    background-color: yellow;
}

/** Section nav **/

#nav {
    line-height: 1.2;
    border-width: 1px;
    border-color: black;
    border-style: none dotted none none;
}

#nav-inner {
    padding-left: 30px;
    padding-right: 30px;
    padding-top: -10px;
    padding-bottom: 30px;
}

#nav a {
    text-decoration: none;
    color: black;
}

#nav a:hover {
    margin: 0px -5px 0px -5px;
    padding: 2px 5px 2px 5px;
}

#nav h1 {
    font-size: 1.8em;
    margin: 1.2em 0 .2em 0;
}

#nav h2 {
    font-size: 1.33em;
    font-weight: normal;
    margin: .6em 0 .3em 0;
}

#nav h3 {
    font-size: .8em;
    font-weight: normal;
    margin: 4px 0px 4px 0px;
    padding-left: 10px;
    color: #333333;
    letter-spacing: 0px;
}

#nav h4 {
    font-size: .8em;
    font-weight: normal;
    margin: 4px 0px 4px 0px;
    padding-left: 20px;
    color: #333333;
    letter-spacing: 0px;
}

#nav .spacer {
    height: .33em;
}

#nav .noncode {
    font-style: italic;
}

#nav .current {
    color: red;
}

/** Main pane **/

#main {
    margin: 10px;
    line-height: 1.3;
    color: #333333;
}

#main strong {
    font-weight: bold;
}

#main h1 {
    border-style: solid none none none;
    border-color: #999;
    border-width: 2px;
}

#main h2 span {
    background-color: yellow;
    margin: 0px -5px 0px -5px;
    padding: 2px 5px 2px 5px;
}

#main a:link, #main a:visited {
    text-decoration: none;
    border-bottom: 1px dashed #000;
    color: inherit;
}

#main code a:link, #main code a:visited {
    border: none;
}

dl.involved {
    padding-left: 10px;
}

dl.involved dt {
    text-transform: uppercase;
    font-weight: bold;
}

dl.involved dt span {
    background-color: yellow;
}

dl.involved dd {
    margin-left: 20px;
    margin-bottom: 1em;
}

dl.involved a {
    font-weight: bold;
}

dl.involved code {
    font-weight: bold;
}

dl.example dt {
    font-weight: bold;
    margin-left: 10px;
}

dl.example dd {
    margin-bottom: 10px;
}

dl.objdesc dt {
    margin-top: 1em;
    margin-left: 1.5em;
}

dl.objdesc dt .name {
    font-weight: bold;
}

dl.objdesc dt .type {
    margin-left: 15px;
    font-size: .9em;
    font-weight: 200;
    color: black;
}

dl.objdesc dd {
    margin-bottom: 1em;
    margin-left: 1.5em;
}

dl.callbacks dt .name, dl.methods dt .name {
  /* like code tag */
  font-family: monospace;
  font-size: 1.1em;
}

dl.callbacks {
  margin-left: 1.5em;
}

#main dd p {
    margin-top: 0.5em;
}

/** API **/

.warning {
    border-width: 0px 0px 0px 3px;
    border-style: solid;
    border-color: red;
    margin: 21px 0px 21px 10px;
    padding-left: 15px;
    font-weight: bold;
    color: red;
}

.note {
    border-width: 0px 0px 0px 2px;
    border-style: solid;
    border-color: black;
    margin: 21px 0px 21px 10px;
    padding-left: 15px;
    font-style: italic;
    color: black;
}

.locus {
    font-style: italic;
}

.api {
    border-width: 0px 0px 2px 0px;
    border-style: dashed;
    border-color: #ccc;
    padding-top: 2em;
    margin-bottom: 1em;
}

.bareapi {
    border-width: 0;
}

.api h3 {
    margin: 0px 0px 1.33em 0px;
    border-width: 2px 0px 2px 0px;
    border-style: solid;
    border-color: black;
    padding-bottom: 1px;
}

.api h4 {
    font-weight: normal;
    font-style: italic;
    margin-bottom: .25em;
    text-decoration: underline;
    margin-left: 20px;
}

.api dl {
    margin-top: .25em;
}

.api dl.args {
    margin-left:  40px;
}

.api dl.constants {
    margin-left:  20px;
}

.api dt {
    margin-top: 1em;
}

.api dt .name {
    font-weight: bold;
}

.api dt .type {
    margin-left: 15px;
    font-size: .9em;
    font-weight: 200;
    color: black;
}

.api dd {
    margin-bottom: 1em;
    margin-left: 0;
}

.api .desc {
    margin: 1em 0 1em 20px;
}

.api .locus {
    float: right;
    font-weight: normal;
    padding-right: 5px;
}

#main pre {
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    -o-border-radius: 10px;
    -ms-border-radius: 10px;
    -khtml-border-radius: 10px;
    border-radius: 10px;
    border-color: #777;
    border-style: solid;
    background-color: white;
    border-width: 1px;
    padding: 5px;
    margin: 1em 0 1em 0;
}

#main pre, #main pre code { /* override inline code font and white-space*/
  font-size: 12px;
  font-weight: bold;
}

#main a.selflink:link, #main a.selflink:visited, #main a.selflink:hover {
  text-decoration: none;
  border: none;
  color: inherit;
  background: none;
}

/** layout control **/

/* default to no sidebar */
#nav {
    display: none;
}
.github-ribbon {
    display: none;
}
pre {
    white-space: pre-wrap;
}

@media (min-width: 768px) {
/* ipad portrait or better */
#main {
    width: 440px;
    height: 100%;
    margin-left: 260px; /* nav width + padding */
    padding: 30px;
}
#nav {
    display: block;
    width: 200px;
    position: fixed;
    overflow: auto;
    height: 100%;
    top: 0;
    left: 0;
}
.main-headline {
    display: none;
}
}

@media (min-width: 1024px) {
/* ipad landscape and desktop */
#main {
    width: 610px;
    margin-left: 330px; /* nav width + padding */
}
#nav {
    width: 270px;
}
.github-ribbon {
    display: block;
}
}


Package.describe({
  summary: "Utility functions for DOM manipulation",
  internal: true
});

Package.on_use(function (api) {
  // XXX
  // Doesn't actually require jQuery (but uses it if available).
  //
  // For now we are going to keep shipping jQuery with all apps
  // so as not to break existing apps, but any time now we will
  // cut this dependency.
  api.use('jquery', 'client');

  api.add_files('domutils.js', 'client');
});

Package.on_test(function (api) {
  api.use(['tinytest']);
  api.use(['domutils', 'test-helpers'], 'client');

  api.add_files([
    'domutils_tests.js'
  ], 'client');
});


constructMessage(whatExpected, pos, found, after));

keep keep keep replace keep keep keep keep

constructMessage(whatExpected, pos, found, after),


@red: #842210;

h1 {
    color: @red;
}

img {
    display: block;
    position: absolute;
    top: 0px;
    right: 0px;
}


// Meteor Universal Events -- Normalized cross-browser event handling library
//
// This module lets you set up a function f that will be called
// whenever an event fires on any element in the DOM. Specifically,
// when an event fires on node N, f will be called with N. Then, if
// the event is a bubbling event, f will be called again with N's
// parent, then called again with N's grandparent, etc, until the root
// of the document is reached. This provides a good base on top of
// which custom event handling systems can be implemented.
//
// f also receives the event object for the event that fired. The
// event object is normalized and extended to smooth over
// cross-browser differences in event handling. See the details in
// setHandler.
//
// Usage:
//   var listener = new UniversalEventListener(function (event) { ... });
//   listener.addType("click");
//
// If you want to support IE <= 8, you must also call installHandler
// on each subtree of DOM nodes on which you wish to receive events,
// eg, before inserting them into the document.
//
// Universal Events works reliably for events that fire on any DOM
// element. It may not work consistently across browsers for events
// that are intended to fire on non-element nodes (eg, text nodes).
// We're not sure if it's possible to handle those events consistently
// across browsers, but in any event, it's not a common use case.
//
// Implementation notes:
//
// Internally, there are two separate implementations, one for modern
// browsers (in events-w3c.js), and one for old browsers with no
// event capturing support (in events-ie.js.) The correct
// implementation will be chosen for you automatically at runtime.

(function () {

  var listeners = [];

  var returnFalse = function() { return false; };
  var returnTrue = function() { return true; };

  // inspired by jquery fix()
  var normalizeEvent = function (event) {
    var originalStopPropagation = event.stopPropagation;
    var originalPreventDefault = event.preventDefault;
    event.isPropagationStopped = returnFalse;
    event.isImmediatePropagationStopped = returnFalse;
    event.isDefaultPrevented = returnFalse;
    event.stopPropagation = function() {
      event.isPropagationStopped = returnTrue;
      if (originalStopPropagation)
        originalStopPropagation.call(event);
      else
        event.cancelBubble = true; // IE
    };
    event.preventDefault = function() {
      event.isDefaultPrevented = returnTrue;
      if (originalPreventDefault)
        originalPreventDefault.call(event);
      else
        event.returnValue = false; // IE
    };
    event.stopImmediatePropagation = function() {
      event.stopPropagation();
      event.isImmediatePropagationStopped = returnTrue;
    };

    var type = event.type;

    // adapted from jquery
    if (event.metaKey === undefined)
      event.metaKey = event.ctrlKey;
    if (/^key/.test(type)) {
      // KEY EVENTS
      // Add which.  Technically char codes and key codes are
      // different things; the former is ASCII/unicode/etc and the
      // latter is arbitrary.  But browsers that lack charCode
      // seem to put character info in keyCode.
      // (foo == null) tests for null or undefined
      if (event.which == null)
	event.which = (event.charCode != null ? event.charCode : event.keyCode);
    } else if (/^(?:mouse|contextmenu)|click/.test(type)) {
      // MOUSE EVENTS
      // Add relatedTarget, if necessary
      if (! event.relatedTarget && event.fromElement)
	event.relatedTarget = (event.fromElement === event.target ?
                               event.toElement : event.fromElement);
      // Add which for click: 1 === left; 2 === middle; 3 === right
      if (! event.which && event.button !== undefined ) {
        var button = event.button;
	event.which = (button & 1 ? 1 :
                       (button & 2 ? 3 :
                         (button & 4 ? 2 : 0 )));
      }
    }

    return event;
  };

  var deliver = function (event) {
    event = normalizeEvent(event);
    _.each(listeners, function (listener) {
      if (listener.types[event.type]) {
        // if in debug mode, filter out events where the user forgot
        // to call installHandler, even if we're not on IE
        if (!(listener._checkIECompliance &&
              ! event.currentTarget['_uevents_test_eventtype_' + event.type]))
          listener.handler.call(null, event);
      }
    });
  };

  // When IE8 is dead, we can remove this springboard logic.
  var impl;
  var getImpl = function () {
    if (! impl)
      impl = (document.addEventListener ?
              new UniversalEventListener._impl.w3c(deliver) :
              new UniversalEventListener._impl.ie(deliver));
    return impl;
  };

  var typeCounts = {};

  ////////// PUBLIC API

  // Create a new universal event listener with a given handler.
  // Until some event types are turned on with `addType`, the handler
  // will not receive any events.
  //
  // Whenever an event of the appropriate type fires anywhere in the
  // document, `handler` will be called with one argument, the
  // event. If the event is a bubbling event (most events are
  // bubbling, eg, 'click'), then `handler` will be called not only
  // for the element that was the origin of the event (eg, the button
  // that was clicked), but for each parent element as the event
  // bubbles up to the top of the tree.
  //
  // The event object that's passed to `handler` will be normalized
  // across browsers so that it contains the following fields and
  // methods:
  //
  // - type (e.g. "click")
  // - target
  // - currentTarget
  // - stopPropagation()
  // - preventDefault()
  // - isPropagationStopped()
  // - isDefaultPrevented()
  //
  // NOTE: If you want compatibility with IE <= 8, you will need to
  // call `installHandler` to prepare each subtree of the DOM to receive
  // the events you are interested in.
  //
  // Debugging only:
  //
  // The _checkIECompliance flag enables extra checking that the user
  // is correctly registering new DOM nodes with installHandler, even
  // in browsers that don't require it. In other words, when the flag
  // is set, modern browsers will require the same API calls as IE <=
  // 8. This is only used for tests and is private for now.
  UniversalEventListener = function (handler, _checkIECompliance) {
    this.handler = handler;
    this.types = {}; // map from event type name to 'true'
    this.impl = getImpl();
    this._checkIECompliance = _checkIECompliance;
    listeners.push(this);
  };

  _.extend(UniversalEventListener.prototype, {
    // Adds `type` to the set of event types that this listener will
    // listen to and deliver to the handler.  Has no effect if `type`
    // is already in the set.
    addType: function (type) {
      if (!this.types[type]) {
        this.types[type] = true;
        typeCounts[type] = (typeCounts[type] || 0) + 1;
        if (typeCounts[type] === 1)
          this.impl.addType(type);
      }
    },

    // Removes `type` from the set of event types that this listener
    // will listen to and deliver to the handler.  Has no effect if `type`
    // is not in the set.
    removeType: function (type) {
      if (this.types[type]) {
        delete this.types[type];
        typeCounts[type]--;
        if (! typeCounts[type])
          this.impl.removeType(type);
      }
    },

    // It is only necessary to call this method if you want to support
    // IE <= 8. On those browsers, you must call this method on each
    // set of nodes before adding them to the DOM (or at least, before
    // expecting to receive events on them), and you must specify the
    // types of events you'll be receiving.
    //
    // `node` and all of its descendents will be set up to handle
    // events of type `type` (eg, 'click'). Only current descendents
    // of `node` are affected; if new nodes are added to the subtree
    // later, installHandler must be called again to ensure events are
    // received on those nodes. To set up to handle multiple event
    // types, make multiple calls.
    //
    // It is safe to call installHandler any number of times on the same
    // arguments (it is idempotent).
    //
    // If you forget to call this function for a given node, it's
    // unspecified whether you'll receive events on IE <= 8 (you may,
    // you may not.) If you don't care about supporting IE <= 8 you
    // can ignore this function.
    installHandler: function (node, type) {
      // Only work on element nodes, not e.g. text nodes or fragments
      if (node.nodeType !== 1)
        return;
      this.impl.installHandler(node, type);

      // When in checkIECompliance mode, mark all the nodes in the current subtree.
      // We will later block events on nodes that weren't marked.  This
      // tests that Spark is generating calls to registerEventType
      // with proper subtree information, even in browsers that don't need
      // it.
      if (this._checkIECompliance) {
        // set flag to mark the node for this type, recording the
        // fact that installHandler was called for this node and type.
        // the property value can be any non-primitive value (to prevent
        // showing up as an HTML attribute in IE) so we use `node` itself.
        node['_uevents_test_eventtype_'+type] = node;
        if (node.firstChild) {
          _.each(node.getElementsByTagName('*'), function(x) {
            x['_uevents_test_eventtype_'+type] = x;
          });
        }
      }
    },

    // Tear down this UniversalEventListener so that no more events
    // are delivered.
    destroy: function () {
      var self = this;

      listeners = _.without(listeners, self);
      _.each(_.keys(self.types), function (type) {
        self.removeType(type);
      });
    }
  });
})();


(function () {
  Accounts.oauth.registerService('github', 2, function(query) {

    var accessToken = getAccessToken(query);
    var identity = getIdentity(accessToken);

    return {
      serviceData: {
        id: identity.id,
        accessToken: accessToken,
	email: identity.email,
	username: identity.login
      },
      options: {profile: {name: identity.name}}
    };
  });

  var getAccessToken = function (query) {
    var config = Accounts.loginServiceConfiguration.findOne({service: 'github'});
    if (!config)
      throw new Accounts.ConfigError("Service not configured");

    var result = Meteor.http.post(
      "https://github.com/login/oauth/access_token", {headers: {Accept: 'application/json'}, params: {
	code: query.code,
	client_id: config.clientId,
	client_secret: config.secret,
	redirect_uri: Meteor.absoluteUrl("_oauth/github?close"),
	state: query.state
      }});
    if (result.error) // if the http response was an error
      throw result.error;
    if (result.data.error) // if the http response was a json object with an error attribute
      throw result.data;
    return result.data.access_token;
  };

  var getIdentity = function (accessToken) {
    var result = Meteor.http.get(
      "https://api.github.com/user",
      {params: {access_token: accessToken}});
    if (result.error)
      throw result.error;
    return result.data;
  };
}) ();


"program(expressionStmnt(parens(`(` object({ prop(idPropName(null) : number(3)) }) `)`) ;()))"],

"program(expressionStmnt(parens(`(` object({ prop(idPropName(if) : number(3)) }) `)`) ;()))"],

// ES5 line continuations in string literals

keep keep keep keep keep replace keep keep keep keep

"program(varStmnt(var varDecl(x = string(`'a\\\nb\\\nc'`)) ;))"],

"program(expressionStmnt(parens(`(` object({ }) `)`) ;))"],

"program(expressionStmnt(parens(`(` object({ prop(idPropName(x) : number(1)) }) `)`) ;))"],

"program(expressionStmnt(parens(`(` object({ prop(idPropName(x) : number(1)) , }) `)`) ;))"],

"program(expressionStmnt(parens(`(` object({ prop(idPropName(x) : number(1)) , " +

"prop(idPropName(y) : number(2)) }) `)`) ;))"],

"prop(idPropName(y) : number(2)) , }) `)`) ;))"]


// Lists -- {name: String}
Lists = new Meteor.Collection("lists");

// Publish complete set of lists to all clients.
Meteor.publish('lists', function () {
  return Lists.find();
});

// Todos -- {text: String,
//           done: Boolean,
//           tags: [String, ...],
//           list_id: String,
//           timestamp: Number}
Todos = new Meteor.Collection("todos");

// Publish all items for requested list_id.
Meteor.publish('todos', function (list_id) {
  return Todos.find({list_id: list_id});
});


Package.describe({
  summary: "Logging facility.",
  internal: true
});

Package.on_use(function (api, where) {
  where = where || ['client', 'server'];
  api.add_files('logging.js', where);
});

Package.on_test(function (api) {
  api.use('logging', 'client');
  api.add_files('logging_test.js', 'client');
});


Tinytest.add("jsparse - bad parses", function (test) {

// string between backticks is pulled out and becomes what's "expected"

// at that location, according to the correct error message


(function () {
  // XXX support options.requestPermissions as we do for Facebook, Google, Github
  Meteor.loginWithTwitter = function (options, callback) {
    // support both (options, callback) and (callback).
    if (!callback && typeof options === 'function') {
      callback = options;
      options = {};
    }

    var config = Accounts.loginServiceConfiguration.findOne({service: 'twitter'});
    if (!config) {
      callback && callback(new Accounts.ConfigError("Service not configured"));
      return;
    }

    var state = Meteor.uuid();
    // We need to keep state across the next two 'steps' so we're adding
    // a state parameter to the url and the callback url that we'll be returned
    // to by oauth provider

    // url back to app, enters "step 2" as described in
    // packages/accounts-oauth1-helper/oauth1_server.js
    var callbackUrl = Meteor.absoluteUrl('_oauth/twitter?close&state=' + state);

    // url to app, enters "step 1" as described in
    // packages/accounts-oauth1-helper/oauth1_server.js
    var url = '/_oauth/twitter/?requestTokenAndRedirect='
          + encodeURIComponent(callbackUrl)
          + '&state=' + state;

    Accounts.oauth.initiateLogin(state, url, callback);
  };

})();


Tinytest.add("stylus - presence", function(test) {

  var d = OnscreenDiv(Meteor.render(function() {
    return '

'; }));
  d.node().style.display = 'block';

  var p = d.node().firstChild;
  var leftBorder = getStyleProperty(p, 'border-left-style');
  test.equal(leftBorder, "dashed");

  d.kill();

});


'1;`statement`=',
'a+b`semicolon`=c;',
'for(1+1 `semicolon`in {});',
'`statement`=',
'for(;`expression`var;) {}'
];
_.each(trials, function (tr) {
tester.badParse(tr);

keep keep keep keep replace keep keep keep