// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NW_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
#define NW_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_

#include <string>

#include "base/compiler_specific.h"
#include "content/public/browser/browser_message_filter.h"

#if defined(OS_WIN)
#include "base/shared_memory.h"
#endif

struct PrintHostMsg_ScriptedPrint_Params;

namespace base {
class DictionaryValue;
class FilePath;
}

namespace content {
class WebContents;
}

namespace printing {
class PrinterQuery;
class PrintJobManager;
}

// This class filters out incoming printing related IPC messages for the
// renderer process on the IPC thread.
class PrintingMessageFilter : public content::BrowserMessageFilter {
 public:
  PrintingMessageFilter(int render_process_id);

  // content::BrowserMessageFilter methods.
  virtual void OverrideThreadForMessage(
      const IPC::Message& message,
      content::BrowserThread::ID* thread) OVERRIDE;
  virtual bool OnMessageReceived(const IPC::Message& message,
                                 bool* message_was_ok) OVERRIDE;

 private:
  virtual ~PrintingMessageFilter();

#if defined(OS_WIN)
  // Used to pass resulting EMF from renderer to browser in printing.
  void OnDuplicateSection(base::SharedMemoryHandle renderer_handle,
                          base::SharedMemoryHandle* browser_handle);
#endif

#if defined(OS_CHROMEOS)
  // Used to ask the browser allocate a temporary file for the renderer
  // to fill in resulting PDF in renderer.
  void OnAllocateTempFileForPrinting(base::FileDescriptor* temp_file_fd,
                                     int* sequence_number);
  void OnTempFileForPrintingWritten(int render_view_id, int sequence_number);
  void CreatePrintDialogForFile(int render_view_id, const base::FilePath& path);
#endif

  // Given a render_view_id get the corresponding WebContents.
  // Must be called on the UI thread.
  content::WebContents* GetWebContentsForRenderView(int render_view_id);

  // GetPrintSettingsForRenderView must be called via PostTask and
  // base::Bind.  Collapse the settings-specific params into a
  // struct to avoid running into issues with too many params
  // to base::Bind.
  struct GetPrintSettingsForRenderViewParams;

  // Retrieve print settings.  Uses |render_view_id| to get a parent
  // for any UI created if needed.
  void GetPrintSettingsForRenderView(
      int render_view_id,
      GetPrintSettingsForRenderViewParams params,
      const base::Closure& callback,
      scoped_refptr<printing::PrinterQuery> printer_query);

  // Checks if printing is enabled.
  void OnIsPrintingEnabled(bool* is_enabled);

  // Get the default print setting.
  void OnGetDefaultPrintSettings(IPC::Message* reply_msg);
  void OnGetDefaultPrintSettingsReply(
      scoped_refptr<printing::PrinterQuery> printer_query,
      IPC::Message* reply_msg);

  // The renderer host have to show to the user the print dialog and returns
  // the selected print settings. The task is handled by the print worker
  // thread and the UI thread. The reply occurs on the IO thread.
  void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params,
                       IPC::Message* reply_msg);
  void OnScriptedPrintReply(
      scoped_refptr<printing::PrinterQuery> printer_query,
      IPC::Message* reply_msg);

  // Modify the current print settings based on |job_settings|. The task is
  // handled by the print worker thread and the UI thread. The reply occurs on
  // the IO thread.
  void OnUpdatePrintSettings(int document_cookie,
                             const base::DictionaryValue& job_settings,
                             IPC::Message* reply_msg);
  void OnUpdatePrintSettingsReply(
      scoped_refptr<printing::PrinterQuery> printer_query,
      IPC::Message* reply_msg);

  // Check to see if print preview has been cancelled.
  void OnCheckForCancel(int32 preview_ui_id,
                        int preview_request_id,
                        bool* cancel);

  printing::PrintJobManager* print_job_manager_;

  int render_process_id_;

  DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter);
};

#endif  // CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_


keep keep keep keep keep replace keep keep keep keep keep

if (force || windows[i]->ShouldCloseWindow())

// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/api/app/app.h"

#include "base/command_line.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/values.h"
#include "content/nw/src/api/api_messages.h"
#include "content/nw/src/browser/net_disk_cache_remover.h"
#include "content/nw/src/nw_package.h"
#include "content/nw/src/nw_shell.h"
#include "content/nw/src/shell_browser_context.h"
#include "content/common/view_messages.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/render_process_host.h"

using base::MessageLoop;
using content::Shell;
using content::ShellBrowserContext;
using content::RenderProcessHost;

namespace api {

namespace {

// Get render process host.
RenderProcessHost* GetRenderProcessHost() {
  RenderProcessHost* render_process_host = NULL;
  std::vector<Shell*> windows = Shell::windows();
  for (size_t i = 0; i < windows.size(); ++i) {
    if (!windows[i]->is_devtools()) {
      render_process_host = windows[i]->web_contents()->GetRenderProcessHost();
      break;
    }
  }

  return render_process_host;
}

void GetRenderProcessHosts(std::set<RenderProcessHost*>& rphs) {
  RenderProcessHost* render_process_host = NULL;
  std::vector<Shell*> windows = Shell::windows();
  for (size_t i = 0; i < windows.size(); ++i) {
    if (!windows[i]->is_devtools()) {
      render_process_host = windows[i]->web_contents()->GetRenderProcessHost();
      rphs.insert(render_process_host);
    }
  }
}

}  // namespace

// static
void App::Call(const std::string& method,
               const base::ListValue& arguments) {
  if (method == "Quit") {
    Quit();
    return;
  } else if (method == "CloseAllWindows") {
    CloseAllWindows();
    return;
  }
  NOTREACHED() << "Calling unknown method " << method << " of App";
}

// static
void App::Call(Shell* shell,
               const std::string& method,
               const base::ListValue& arguments,
               base::ListValue* result) {
  if (method == "GetDataPath") {
    ShellBrowserContext* browser_context =
      static_cast<ShellBrowserContext*>(shell->web_contents()->GetBrowserContext());
    result->AppendString(browser_context->GetPath().value());
    return;
  }else if (method == "GetArgv") {
    nw::Package* package = shell->GetPackage();
    CommandLine* command_line = CommandLine::ForCurrentProcess();
    CommandLine::StringVector args = command_line->GetArgs();
    CommandLine::StringVector argv = command_line->original_argv();

    // Ignore first non-switch arg if it's not a standalone package.
    bool ignore_arg = !package->self_extract();
    for (unsigned i = 1; i < argv.size(); ++i) {
      if (ignore_arg && argv[i] == args[0]) {
        ignore_arg = false;
        continue;
      }

      result->AppendString(argv[i]);
    }

    return;
  } else if (method == "ClearCache") {
    ClearCache(GetRenderProcessHost());
  } else if (method == "GetPackage") {
    result->AppendString(shell->GetPackage()->package_string());
    return;
  }

  NOTREACHED() << "Calling unknown sync method " << method << " of App";
}

// static
void App::CloseAllWindows() {
  std::vector<Shell*> windows = Shell::windows();

  for (size_t i = 0; i < windows.size(); ++i) {
    // Only send close event to browser windows, since devtools windows will
    // be automatically closed.
    if (!windows[i]->is_devtools()) {
      // If there is no js object bound to the window, then just close.
      if (windows[i]->ShouldCloseWindow())
        delete windows[i];
    }
  }
}

// static
void App::Quit(RenderProcessHost* render_process_host) {
  // Send the quit message.
  int no_use;
  if (render_process_host) {
    render_process_host->Send(new ViewMsg_WillQuit(&no_use));
  }else{
    std::set<RenderProcessHost*> rphs;
    std::set<RenderProcessHost*>::iterator it;

    GetRenderProcessHosts(rphs);
    for (it = rphs.begin(); it != rphs.end(); it++) {
      RenderProcessHost* rph = *it;
      DCHECK(rph != NULL);

      rph->Send(new ViewMsg_WillQuit(&no_use));
    }
  }
  // Then quit.
  MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure());
}

// static
void App::EmitOpenEvent(const std::string& path) {
  std::set<RenderProcessHost*> rphs;
  std::set<RenderProcessHost*>::iterator it;

  GetRenderProcessHosts(rphs);
  for (it = rphs.begin(); it != rphs.end(); it++) {
    RenderProcessHost* rph = *it;
    DCHECK(rph != NULL);

    rph->Send(new ShellViewMsg_Open(path));
  }
}

void App::ClearCache(content::RenderProcessHost* render_process_host) {
  render_process_host->Send(new ShellViewMsg_ClearCache());
  nw::RemoveHttpDiskCache(render_process_host->GetBrowserContext(),
                          render_process_host->GetID());
}

}  // namespace api


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef CONTENT_NW_SRC_RENDERER_NW_RENDER_VIEW_OBSERVER_H_
#define CONTENT_NW_SRC_RENDERER_NW_RENDER_VIEW_OBSERVER_H_

#include "content/public/renderer/render_view_observer.h"

class SkBitmap;

namespace WebKit {
class WebView;
}

namespace nw {

class NwRenderViewObserver : public content::RenderViewObserver {
 public:
  NwRenderViewObserver(content::RenderView* render_view);
  virtual ~NwRenderViewObserver();

 private:
  // RenderViewObserver implementation.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;

  void OnCaptureSnapshot();

  // Capture a snapshot of a view.  This is used to allow an extension
  // to get a snapshot of a tab using chrome.tabs.captureVisibleTab().
  bool CaptureSnapshot(WebKit::WebView* view, SkBitmap* snapshot);

  DISALLOW_COPY_AND_ASSIGN(NwRenderViewObserver);
};

}  // namespace content

#endif  // CONTENT_NW_SRC_RENDERER_NW_RENDER_VIEW_OBSERVER_H_


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/browser/shell_login_dialog.h"

#include <gtk/gtk.h>

#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/resource_dispatcher_host.h"
#include "content/public/browser/resource_request_info.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view.h"
#include "ui/base/gtk/gtk_hig_constants.h"

namespace content {

void ShellLoginDialog::PlatformCreateDialog(const string16& message) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  int render_process_id;
  int render_view_id;
  if (!ResourceRequestInfo::ForRequest(request_)->GetAssociatedRenderView(
          &render_process_id,  &render_view_id)) {
    NOTREACHED();
  }

  WebContents* web_contents = NULL;
  RenderViewHost* render_view_host =
      RenderViewHost::FromID(render_process_id, render_view_id);
  if (render_view_host)
    web_contents = WebContents::FromRenderViewHost(render_view_host);
  DCHECK(web_contents);

  gfx::NativeWindow parent_window =
      web_contents->GetView()->GetTopLevelNativeWindow();

  root_ = gtk_message_dialog_new(parent_window,
                                 GTK_DIALOG_MODAL,
                                 GTK_MESSAGE_INFO,
                                 GTK_BUTTONS_OK_CANCEL,
                                 "Please log in.");

  GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(root_));
  GtkWidget* label = gtk_label_new(UTF16ToUTF8(message).c_str());
  gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
  gtk_box_pack_start(GTK_BOX(content_area), label, FALSE, FALSE, 0);

  username_entry_ = gtk_entry_new();
  gtk_entry_set_activates_default(GTK_ENTRY(username_entry_), TRUE);

  password_entry_ = gtk_entry_new();
  gtk_entry_set_activates_default(GTK_ENTRY(password_entry_), TRUE);
  gtk_entry_set_visibility(GTK_ENTRY(password_entry_), FALSE);

  GtkWidget* table = gtk_table_new(2, 2, FALSE);
  gtk_table_set_col_spacing(GTK_TABLE(table), 0, ui::kLabelSpacing);
  gtk_table_set_row_spacings(GTK_TABLE(table), ui::kControlSpacing);

  GtkWidget* username_label = gtk_label_new("Username:");
  gtk_misc_set_alignment(GTK_MISC(username_label), 0, 0.5);

  gtk_table_attach(GTK_TABLE(table), username_label, 0, 1, 0, 1, GTK_FILL,
                   GTK_FILL, 0, 0);
  gtk_table_attach_defaults(GTK_TABLE(table), username_entry_, 1, 2, 0, 1);

  GtkWidget* password_label = gtk_label_new("Password:");
  gtk_misc_set_alignment(GTK_MISC(password_label), 0, 0.5);

  gtk_table_attach(GTK_TABLE(table), password_label, 0, 1, 1, 2, GTK_FILL,
                   GTK_FILL, 0, 0);
  gtk_table_attach_defaults(GTK_TABLE(table), password_entry_, 1, 2, 1, 2);

  gtk_box_pack_start(GTK_BOX(content_area), table, FALSE, FALSE, 0);

  g_signal_connect(root_, "response", G_CALLBACK(OnResponseThunk), this);
  gtk_widget_grab_focus(username_entry_);
  gtk_widget_show_all(GTK_WIDGET(root_));
}

void ShellLoginDialog::PlatformCleanUp() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

void ShellLoginDialog::PlatformRequestCancelled() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}

void ShellLoginDialog::OnResponse(GtkWidget* sender, int response_id) {
  switch (response_id) {
    case GTK_RESPONSE_OK:
      UserAcceptedAuth(
          UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(username_entry_))),
          UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(password_entry_))));
      break;
    case GTK_RESPONSE_CANCEL:
    case GTK_RESPONSE_DELETE_EVENT:
      UserCancelledAuth();
      break;
    default:
      NOTREACHED();
  }

  gtk_widget_destroy(root_);
}

}  // namespace content


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include "content/nw/src/browser/shell_login_dialog.h"

#import <Cocoa/Cocoa.h>

#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#import "base/memory/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#import "ui/base/cocoa/nib_loading.h"

namespace {

const int kUsernameFieldTag = 1;
const int kPasswordFieldTag = 2;

}  // namespace

// Helper object that receives the notification that the dialog/sheet is
// going away.
@interface ShellLoginDialogHelper : NSObject<NSAlertDelegate> {
 @private
  scoped_nsobject<NSAlert> alert_;
  NSTextField* usernameField_;  // WEAK; owned by alert_
  NSSecureTextField* passwordField_;  // WEAK; owned by alert_
}

- (NSAlert*)alert;
- (NSView*)accessoryView;
- (void)focus;
- (void)alertDidEnd:(NSAlert*)alert
         returnCode:(int)returnCode
        contextInfo:(void*)contextInfo;
- (void)cancel;

@end

@implementation ShellLoginDialogHelper

- (NSAlert*)alert {
  alert_.reset([[NSAlert alloc] init]);
  [alert_ setAccessoryView:[self accessoryView]];
  return alert_;
}

- (NSView*)accessoryView {
  NSView* accessory_view = ui::GetViewFromNib(@"HttpAuth");
  if (!accessory_view)
    return nil;

  usernameField_ = [accessory_view viewWithTag:kUsernameFieldTag];
  passwordField_ = [accessory_view viewWithTag:kPasswordFieldTag];
  return accessory_view;
}

- (void)focus {
  [[alert_ window] makeFirstResponder:usernameField_];
}

- (void)alertDidEnd:(NSAlert*)alert
         returnCode:(int)returnCode
        contextInfo:(void*)contextInfo {
  if (returnCode == NSRunStoppedResponse)
    return;

  content::ShellLoginDialog* this_dialog =
      reinterpret_cast<content::ShellLoginDialog*>(contextInfo);
  if (returnCode == NSAlertFirstButtonReturn) {
    this_dialog->UserAcceptedAuth(
        base::SysNSStringToUTF16([usernameField_ stringValue]),
        base::SysNSStringToUTF16([passwordField_ stringValue]));
  } else {
    this_dialog->UserCancelledAuth();
  }
}

- (void)cancel {
  [NSApp endSheet:[alert_ window]];
  alert_.reset();
}

@end

namespace content {

void ShellLoginDialog::PlatformCreateDialog(const string16& message) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  helper_ = [[ShellLoginDialogHelper alloc] init];

  // Show the modal dialog.
  NSAlert* alert = [helper_ alert];
  [alert setDelegate:helper_];
  [alert setInformativeText:base::SysUTF16ToNSString(message)];
  [alert setMessageText:@"Please log in."];
  [alert addButtonWithTitle:@"OK"];
  NSButton* other = [alert addButtonWithTitle:@"Cancel"];
  [other setKeyEquivalent:@"\e"];
  [alert
      beginSheetModalForWindow:nil  // nil here makes it app-modal
                 modalDelegate:helper_
                didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:)
                   contextInfo:this];

  [helper_ focus];
}

void ShellLoginDialog::PlatformCleanUp() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  [helper_ release];
  helper_ = nil;
}

void ShellLoginDialog::PlatformRequestCancelled() {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  [helper_ cancel];
}

}  // namespace content


// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_SHELL_SHELL_MAIN_DELEGATE_H_
#define CONTENT_SHELL_SHELL_MAIN_DELEGATE_H_

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "content/nw/src/shell_content_client.h"
#include "content/public/app/content_main_delegate.h"

namespace content {
class ShellContentBrowserClient;
class ShellContentRendererClient;

class ShellMainDelegate : public ContentMainDelegate {
 public:
  ShellMainDelegate();
  virtual ~ShellMainDelegate();

  // ContentMainDelegate implementation:
  virtual bool BasicStartupComplete(int* exit_code) OVERRIDE;
  virtual void PreSandboxStartup() OVERRIDE;
  virtual int RunProcess(
      const std::string& process_type,
      const MainFunctionParams& main_function_params) OVERRIDE;
  virtual ContentBrowserClient* CreateContentBrowserClient() OVERRIDE;
  virtual ContentRendererClient* CreateContentRendererClient() OVERRIDE;

  static void InitializeResourceBundle();

 private:
  scoped_ptr<ShellContentBrowserClient> browser_client_;
  scoped_ptr<ShellContentRendererClient> renderer_client_;
  ShellContentClient content_client_;

  DISALLOW_COPY_AND_ASSIGN(ShellMainDelegate);
};

}  // namespace content

#endif  // CONTENT_SHELL_SHELL_MAIN_DELEGATE_H_


<html>
<head>
  <script type="text/javascript" src="../../zepto.min.js"></script>
  <script type="text/javascript" src="../../assert.js"></script>
</head>
<body>
<script>
  var gui = require('nw.gui');

  var menu = new gui.Menu();

  menu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 1', checked: true, enabled: false }));
  menu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 2', checked: false, enabled: true }));
  menu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 3', checked: false, enabled: true }));
  menu.append(new gui.MenuItem({ type: 'checkbox', label: 'Sub 4', checked: false, enabled: true }));

  // Click to flip other items
  var lastone = menu.items[0];
  menu.items[0].click = menu.items[1].click = menu.items[2].click = menu.items[3].click = function() {
    lastone.checked = false;
    lastone.enabled = true;
    this.enabled = false;
    lastone = this;
  }

  var tray = new gui.Tray({
    title: 'Test Tray',
    icon: 'icon.png',
    menu: menu
  });

  assert(
  tray.icon == 'icon.png',
  'Initial Tray Icon');

  tray.icon = '';
  assert(
  tray.icon == '',
  'Clear Tray Icon');

  tray.icon = 'icon.png';

  assert(
  tray.title == 'Test Tray',
  tray.tooltip == '',
  tray.menu == menu,
  'Init Tray');

  tray.title = 'Love Tray';
  tray.tooltip = 'Love Tooltip';
  assert(
  tray.title == 'Love Tray',
  tray.tooltip == 'Love Tooltip',
  'Change Tray');

  var tray2 = new gui.Tray({
    title: 'Test Tray 2',
    menu: menu
  });

  assert(
  tray.menu == tray2.menu,
  tray2.title == 'Test Tray 2',
  'Second Tray');

  tray.on('click', function() {
    console.log('click on tray');
  });

  gc();

</script>
</body>
</html>


// Try to close all windows (then will cause whole app to quit).

static void Quit(content::RenderProcessHost* rph = NULL);

keep keep keep replace keep keep keep keep keep

static void CloseAllWindows(bool force = false);


<title>testing data path</title>

Testing data path

the data path should be set as


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIdentifier</key>
	<string>com.intel.nw.helper</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>LSFileQuarantineEnabled</key>
	<true/>
	<key>LSMinimumSystemVersion</key>
	<string>${MACOSX_DEPLOYMENT_TARGET}.0</string>
	<key>LSUIElement</key>
	<string>1</string>
	<key>NSSupportsAutomaticGraphicsSwitching</key>
	<true/>
</dict>
</plist>


// Copyright (c) 2012 Intel Corp
// Copyright (c) 2012 The Chromium Authors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell co
// pies of the Software, and to permit persons to whom the Software is furnished
//  to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in al
// l copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IM
// PLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNES
// S FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
//  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WH
// ETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
//  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef CONTENT_NW_SRC_API_WINDOW_BINDINGS_H_
#define CONTENT_NW_SRC_API_WINDOW_BINDINGS_H_

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "v8/include/v8.h"

namespace api {

class WindowBindings : public v8::Extension {
 public:
  WindowBindings();
  virtual ~WindowBindings();

  // v8::Extension implementation.
  virtual v8::Handle<v8::FunctionTemplate>
      GetNativeFunction(v8::Handle<v8::String> name) OVERRIDE;

 private:
  // Tell browser to bind a js object to Shell.
  static v8::Handle<v8::Value> BindToShell(const v8::Arguments& args);

  // Call method of an object in browser.
  static v8::Handle<v8::Value> CallObjectMethod(const v8::Arguments& args);

  // Call method of an object in browser synchrounously.
  static v8::Handle<v8::Value> CallObjectMethodSync(const v8::Arguments& args);

  // Get the window object of current render view.
  static v8::Handle<v8::Value> GetWindowObject(const v8::Arguments& args);

  DISALLOW_COPY_AND_ASSIGN(WindowBindings);
};

}  // namespace api

#endif  // CONTENT_NW_SRC_API_WINDOW_BINDINGS_H_


keep keep keep replace keep keep keep keep

// quick jquery extension to bind text inputs to blur and RET.
$.fn.onBlurOrEnter = function (callback) {
  this.bind('blur', callback);
  this.bind('keypress', function (evt) {
    if (evt.keyCode === 13 && $(this).val())
      callback.call(this, evt);
  });
};

// everything else happens after DOM is ready
$(function () {
  $('body').layout({north__minSize: 50,
                    spacing_open: 10,
                    north__fxSettings: { direction: "vertical" }});

  // cache the template function for a single item.
  var item_template = _.template($('#item-template').html());

  // this render function could be replaced with a handlebars
  // template.  underscore template isn't safe for user-entered data
  // like the item text (XSS).
  function renderItem (obj) {
    // generate template for todo
    var elt = $(item_template(obj));

    // set text through jquery for XSS protection
    elt.find('.todo-text').text(obj.text);

    // clicking the checkbox toggles done state
    elt.find('.check').click(function () {
      Todos.update(obj._id, {$set: {done: !obj.done}});
    });

    // clicking destroy button removes the item
    elt.find('.destroy').click(function () {
      Todos.remove(obj._id);
    });

    // wire up tag destruction links
    elt.find('.tag .remove').click(function () {
      var tag = $(this).attr('name');
      $(this).parent().fadeOut(500, function () {
        Todos.update(obj._id, {$pull: {tags: tag}});
      });
    });

    // wire up add tag
    elt.find('.addtag').click(function () {
      $(this).hide();
      elt.find('.edittag').show();
      elt.find('.edittag input').focus();
    });

    // wire up edit tag
    elt.find('.edittag input').onBlurOrEnter(function () {
      elt.find('.edittag').hide();
      elt.find('.addtag').show();
      if ($(this).val() !== '')
        Todos.update(obj._id, {$addToSet: {tags: $(this).val()}});
    });

    // doubleclick on todo text brings up the editor
    elt.find('.todo-text').dblclick(function () {
      elt.addClass('editing');

      var input = elt.find('.todo-input');
      input.val(obj.text);
      input.focus();
      input.select();

      input.onBlurOrEnter(function () {
        elt.removeClass('editing');
        if ($(this).val() !== '')
          Todos.update(obj._id, {$set: {text: elt.find('.todo-input').val()}});
      });
    });

    return elt[0];
  };

  // construct new todo from text box
  $('#new-todo').bind('keypress', function (evt) {
    var list_id = Session.get('list_id');
    var tag = Session.get('tag_filter');

    // prevent creation of a new todo if nothing is selected
    if (!list_id) return;

    var text = $('#new-todo').val();

    if (evt.keyCode === 13 && text) {
      var obj = {text: text,
                 list_id: list_id,
                 done: false,
                 timestamp: (new Date()).getTime()};
      if (tag) obj.tags = [tag];

      Todos.insert(obj);
      $('#new-todo').val('');
    }
  });

  var current_list_stop;
  function setCurrentList (list_id) {
    Session.set('list_id', list_id);

    $('#items-view').show();

    // kill current findLive render
    if (current_list_stop)
      current_list_stop.stop();

    var query = {list_id: list_id};
    if (Session.get('tag_filter'))
      query.tags = Session.get('tag_filter')

    // render individual todo list, stash kill function
    current_list_stop =
      Sky.ui.renderList(Todos, $('#item-list'), {
        query: query,
        sort: {timestamp: 1},
        render: renderItem,
        events: {}
      });
  };

  // render list of lists in the left sidebar.
  Sky.ui.renderList(Lists, $('#lists'), {
    query: {}, // everything
    sort: {name: 1},
    template: $('#list-template'),
    events: {
      'click': function (evt) {
        window.History.pushState({list_id: this._id},
                                 "Todos: " + this.name,
                                 "/" + this._id);
      },
      'dblclick': function (evt) {
        var list_elt = $(evt.currentTarget);
        var input = list_elt.find('.list-name-input');

        list_elt.addClass('editing');

        input.val(this.name);
        input.focus();
        input.select();

        var _id = this._id;
        input.onBlurOrEnter(function () {
          list_elt.removeClass('editing');
          if (input.val() !== '')
            Lists.update(_id, {$set: {name: input.val()}});
        });
      }
    }
  });

  // construct new todo list from text box
  $('#new-list').bind('keypress', function (evt) {
    var text = $('#new-list').val();

    if (evt.keyCode === 13 && text) {
      var list = Lists.insert({name: text});
      $('#new-list').val('');
      window.History.pushState({list_id: list._id},
                               "Todos: " + list.name,
                               "/" + list._id);
    }
  });

  // tags and filters

  // the tag filter bar is easy to generate using a simple
  // renderList() against a minimongo query.  since minimongo doesn't
  // support aggregate queries, construct a local collection to serve
  // the same purpose, and drive the renderList() off of it.

  var LocalTags = Sky.Collection();
  (function () {
    function updateLocalTags() {
      var real = _(Todos.find()).chain().pluck('tags').compact().flatten().uniq().value();
      real.unshift(null); // XXX fake tag

      var computed = _(LocalTags.find()).pluck('tag');

      _.each(_.difference(real, computed), function (new_tag) {
        LocalTags.insert({tag: new_tag});
      });

      _.each(_.difference(computed, real), function (dead_tag) {
        LocalTags.remove({tag: dead_tag});
      });
    };

    Todos.findLive({}, {
      added: function (obj, before_idx) { _.defer(updateLocalTags); },
      removed: function (id, at_idx) { _.defer(updateLocalTags); },
      changed: function (obj, at_idx) { _.defer(updateLocalTags); },
    });
  })();

  // findLive() against the computed tag table.  since we also want a
  // show-all button, arrange for the computed table to always include
  // a null placeholder tag, and for the template to render that as
  // "Show all".  always begin the user session with a null filter.

  Session.set('tag_filter', null);

  Sky.ui.renderList(LocalTags, $('#tag-filter'), {
    query: {},
    sort: {tag: 1},
    template: $('#tag-filter-template'),
    events: {
      'click': function (evt) {
        if (Session.equals('tag_filter', this.tag))
          Session.set('tag_filter', null);
        else
          Session.set('tag_filter', this.tag);

        setCurrentList(Session.get('list_id'));
      }
    }
  });

  // load list on statechange (which we drive from several places).
  window.History.Adapter.bind(window, 'statechange', function () {
    var state = window.History.getState();
    var list = Lists.find(state.data.list_id);
    setCurrentList(list._id);
  });

  // subscribe to all available todo lists.  once the inital load
  // completes, navigate to the list specified by URL, if any.
  Sky.subscribe('lists', {}, function () {
    var initial_list_id = window.location.pathname.split('/')[1];
    var list;

    if (initial_list_id) {
      list = Lists.find(initial_list_id);
    } else {
      var lists = Lists.find({}, {sort: {name: 1}, limit: 1});
      list = lists[0];
    }

    if (list) {
      window.History.replaceState({list_id: list._id},
                                  "Todos: " + list.name,
                                  "/" + list._id);
      // replaceState doesn't always trigger statechange on reload. if
      // you last reloaded the same page and the state is the same, it
      // won't fire. so call this here. double calling is not great, but
      // OK.
      setCurrentList(list._id);
    }
  });

  // subscribe to all the items in each list.  no need for a callback
  // here: todo items are never queried using collection.find().
  Sky.subscribe('todos');
});


# Skybreak packages used by this project, one per line.
#
# 'skybreak add' and 'skybreak remove' will edit this file for you,
# but you can also edit it by hand.

less
coffeescript


Package.describe({
  summary: "Skybreak's latency-compensated distributed data framework",
  internal: true
});

Package.require('underscore');
Package.require('session');
Package.require('minimongo');

Package.client_file('livedata_client.js');

Package.server_file('uuid.js');
Package.server_file('livedata_server.js');
Package.server_file('mongo_driver.js');


#!/bin/bash

set -e

BUNDLE_VERSION=0.0.5
UNAME=$(uname)

if [ "$UNAME" == "Linux" ] ; then
    MONGO_NAME="mongodb-linux-x86_64-2.0.1"
    MONGO_URL="http://fastdl.mongodb.org/linux/${MONGO_NAME}.tgz"
elif [ "$UNAME" == "Darwin" ] ; then
    MONGO_NAME="mongodb-osx-x86_64-2.0.1"
    MONGO_URL="http://fastdl.mongodb.org/osx/${MONGO_NAME}.tgz"
else
    echo "This OS not yet supported"
    exit 1
fi

# save off skybreak checkout dir as final target
cd `dirname $0`/..
TARGET_DIR=`pwd`

DIR=`mktemp -d -t generate-dev-bundle-XXXXXXXX`
trap 'rm -rf "$DIR" >/dev/null 2>&1' 0

echo BUILDING IN "$DIR"

cd "$DIR"
mkdir build
cd build

git clone git://github.com/joyent/node.git
cd node
git checkout v0.6.5

# Disable obnoxious print. No easy way to disable that I found.
patch -p1 <<EOF
diff --git a/lib/sys.js b/lib/sys.js
index c37e2a7..d4e71bc 100644
--- a/lib/sys.js
+++ b/lib/sys.js
@@ -21,15 +21,16 @@

 var util = require('util');

-var sysWarning;
-if (!sysWarning) {
-  sysWarning = 'The "sys" module is now called "util". ' +
-               'It should have a similar interface.';
-  if (process.env.NODE_DEBUG && process.env.NODE_DEBUG.indexOf('sys') != -1)
-    console.trace(sysWarning);
-  else
-    console.error(sysWarning);
-}
+// XXX Skybreak disabled
+// var sysWarning;
+// if (!sysWarning) {
+//   sysWarning = 'The "sys" module is now called "util". ' +
+//                'It should have a similar interface.';
+//   if (process.env.NODE_DEBUG && process.env.NODE_DEBUG.indexOf('sys') != -1)
+//     console.trace(sysWarning);
+//   else
+//     console.error(sysWarning);
+// }

 exports.print = util.print;
 exports.puts = util.puts;
EOF

export JOBS=4
./configure --prefix="$DIR"
make
make install

# export path so we use our new node for later builds
export PATH="$DIR/bin:$PATH"

which node

which npm

cd "$DIR/lib/node_modules"
npm install connect@1.8.2
npm install connect-gzip@0.1.5
npm install optimist@0.2.8
npm install socket.io@0.8.7
npm install coffee-script@1.1.3
npm install less@1.1.5
npm install mime@1.2.4
npm install semver@1.0.12
npm install wrench@1.3.2
npm install handlebars@1.0.2beta
npm install mongodb@0.9.7-1.4
npm install uglify-js@1.1.1
npm install clean-css@0.3.0
npm install progress@0.0.4
npm install fibers@0.6.3
npm install useragent@1.0.3
npm install request@2.2.9

# patched versions to allow node 0.6.
# This breaks websockets!
# see https://github.com/nodejitsu/node-http-proxy/pull/152
# and https://github.com/nodejitsu/node-http-proxy/tree/0.6-compatibility
npm install 'git+https://github.com/lukasberns/node-http-proxy.git#2688d308bc'

cd "$DIR"
curl "$MONGO_URL" | tar -xz
mv "$MONGO_NAME" mongodb

# don't ship a number of mongo binaries. they are big and unused. these
# could be deleted from git dev_bundle but not sure which we'll end up
# needing.
cd mongodb/bin
rm bsondump mongodump mongoexport mongofiles mongoimport mongorestore mongos mongosniff mongostat mongotop
cd ../..

patch -p2 <<EOF
diff --git a/dev_bundle/lib/node_modules/socket.io/lib/manager.js b/dev_bundle/lib/node_modules/socket.io/lib/manager.js
index ee2bf49..a68f9cb 100644
--- a/dev_bundle/lib/node_modules/socket.io/lib/manager.js
+++ b/dev_bundle/lib/node_modules/socket.io/lib/manager.js
@@ -114,7 +114,7 @@ function Manager (server, options) {
     }
   }

-  this.log.info('socket.io started');
+  // this.log.info('socket.io started'); // XXX skybreak disabled
 };

 Manager.prototype.__proto__ = EventEmitter.prototype
EOF

echo BUNDLING

cd "$DIR"
echo "${BUNDLE_VERSION}" > .bundle_version.txt
rm -rf build

tar czf "${TARGET_DIR}/dev_bundle_${UNAME}_${BUNDLE_VERSION}.tar.gz" .

echo DONE


// render list of lists in the left sidebar.


root = exports ? this # export Presses globally.
root.Presses = Sky.Collection 'presses'

Sky.publish 'presses'


// Source: https://github.com/coreyti/showdown

// XXX rename to 'markdown' and credit showdown some other way?

Package.describe({
  summary: "Markdown-to-HTML processor"
});

Package.client_file("showdown.js");

// XXX what we really want to do is, load template-integration after
// handlebars, iff handlebars was included in the project
Package.require("handlebars");
Package.client_file("template-integration.js");


keep keep keep keep replace keep keep keep keep keep


Clicks = Sky.Collection('clicks');

if (Sky.is_server) {
  Sky.publish('clicks', {});
}


/*
 Copyright (C) 2011 Google Inc.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/
var a=null;
PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[([{]+/,a,"([{"],["clo",/^[)\]}]+/,a,")]}"],["com",/^;[^\n\r]*/,a,";"],["pln",/^[\t\n\r \xa0]+/,a,"\t\n\r \xa0"],["str",/^"(?:[^"\\]|\\[\S\s])*(?:"|$)/,a,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,a],
["typ",/^:[\dA-Za-z-]+/]]),["clj"]);


#!/bin/bash

BUNDLE_VERSION=0.0.5

# Find the script dir, following one level of symlink.
if [ -L "$0" ] ; then
    SCRIPT_DIR=$(dirname $(readlink "$0") )
else
    SCRIPT_DIR=$(dirname "$0")
fi

# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.
UNAME=$(uname)
if [ "$UNAME" != "Linux" -a "$UNAME" != "Darwin" ] ; then
    echo "Sorry, this OS is not supported."
    exit 1
fi

if [ "$UNAME" = "Darwin" ] ; then
    if [ "i386" != $(uname -p) -o "1" != $(sysctl -n hw.cpu64bit_capable 2>/dev/null || echo 0) ] ; then

        # Can't just test uname -m = x86_64, because Snow Leopard can
        # return other values.
        echo "Only 64-bit Intel processors are supported at this time."
    fi
elif [ "$UNAME" = "Linux" -a "x86_64" != $(uname -m) ] ; then
    echo "Only 64-bit Intel processors are supported at this time."
fi

function install_dev_bundle {
    set -e
    trap "echo Failed to install dependency kit." EXIT

    TARBALL="dev_bundle_${UNAME}_${BUNDLE_VERSION}.tar.gz"
    TMPDIR="$SCRIPT_DIR/dev_bundle.xxx"

    rm -rf "$TMPDIR"
    mkdir "$TMPDIR"

    if [ -f "$SCRIPT_DIR/$TARBALL" ] ; then
        echo "Skipping download and installing kit from $SCRIPT_DIR/$TARBALL"
        tar -xzf "$SCRIPT_DIR/$TARBALL" -C "$TMPDIR"
    else
        curl -# https://d377jur38fl888.cloudfront.net/$TARBALL | tar -xzf - -C "$TMPDIR"
        test -x "${TMPDIR}/bin/node" # bomb out if it didn't work, eg no net
    fi

    mv "$TMPDIR" "$SCRIPT_DIR/dev_bundle"

    echo "Installed dependency kit v${BUNDLE_VERSION} in dev_bundle."
    echo

    trap - EXIT
    set +e
}

if [ -d "$SCRIPT_DIR/.git" ]; then
    # In a checkout.

    if [ ! -d "$SCRIPT_DIR/dev_bundle" ] ; then
        echo "It's the first time you've run Skybreak from a git checkout."
        echo "I will download a kit containing all of Skybreak's dependencies."
        install_dev_bundle
    elif [ ! -f "$SCRIPT_DIR/dev_bundle/.bundle_version.txt" ] ||
        grep -qvx "$BUNDLE_VERSION" "$SCRIPT_DIR/dev_bundle/.bundle_version.txt" ; then
        echo "Your dependency kit is out of date. I will download the new one."
        rm -rf "$SCRIPT_DIR/dev_bundle"
        install_dev_bundle
    fi

    DEV_BUNDLE="$SCRIPT_DIR/dev_bundle"
    SKYBREAK="$SCRIPT_DIR/app/skybreak/skybreak.js"
else
    # In an install
    DEV_BUNDLE=$(dirname "$SCRIPT_DIR")
    SKYBREAK="$DEV_BUNDLE/app/skybreak/skybreak.js"
fi

export NODE_PATH="$DEV_BUNDLE/lib/node_modules"
exec "$DEV_BUNDLE/bin/node" "$SKYBREAK" "$@"


keep keep keep keep replace keep keep keep

///  selector: minimongo selector (default: {})

Sky.ui = Sky.ui || {};

// update in place by hollowing out old element(s), and copying
// over all of the children and attributes. unfortunately there
// is no way to change the tag name. leave the events in place.
Sky.ui._patch = function (old_elt_in, new_elt_in) {
  // Don't let more than one instance of patch run simultaneously.
  // http://code.google.com/p/chromium/issues/detail?id=104397
  //
  // XXX this is a pretty lame solution. Normally users see
  // their changes to Session, the database, etc, reflected in
  // the DOM immediately.. except if, somewhere above them on
  // the stack, is a onblur handler triggered by patch, in which
  // case updates are queued up?? That makes no kind of sense.
  var already_in_patch = !!Sky.ui._patch_queue;
  if (!already_in_patch)
    Sky.ui._patch_queue = [];
  Sky.ui._patch_queue.push([old_elt_in, new_elt_in])
  if (already_in_patch)
    return;

  while (Sky.ui._patch_queue.length) {
    var x = Sky.ui._patch_queue.splice(0, 10)[0];
    var old_elt = x[0];
    var new_elt = x[1];

    if (old_elt.nodeType !== new_elt.nodeType)
      throw new Error("The top-level element type can't change when " +
                      "an element is rerendered (changed from type " +
                      old_elt.nodeType + " to " + new_elt.nodeType + ")");
    if (old_elt.nodeType === 3) { // text node
      old_elt.nodeValue = new_elt.nodeValue;
      return;
    }
    if (old_elt.tagName !== new_elt.tagName)
      throw new Error("The top-level element type can't change when " +
                      "an element is rerendered (changed from " +
                      old_elt.tagName + " to " + new_elt.tagName + ")");
    while (old_elt.childNodes.length)
      old_elt.removeChild(old_elt.childNodes[0]);
    while (new_elt.childNodes.length)
      old_elt.appendChild(new_elt.childNodes[0]);
    while (old_elt.attributes.length)
      old_elt.removeAttribute(old_elt.attributes[0].name);
    for (var i = 0; i < new_elt.attributes.length; i++)
      old_elt.setAttribute(new_elt.attributes[i].name,
                           new_elt.attributes[i].value);
  };

  delete Sky.ui._patch_queue;
};

/// Render some HTML, resulting in a DOM node, which is
/// returned. Update that DOM node in place when any of the rendering
/// dependencies change. (The tag name of the node returned from the
/// template mustn't change -- it must not be a function of any
/// dependencies.)
///
/// render_func should return either a single DOM element, or an array
/// of elements. In the latter case, on rerendering, it must still
/// return an array, and the number and tag names of the elements in
/// the array must not change.
///
/// 'events' is optional. if given, takes the same kind of event map
/// as renderLive. If render_func retruns an array, the event map will
/// be applied to each element in the array. If 'event_data' is
/// provided, it will be passed to events when they fire, as their
/// object data agument.
///
/// render() may be called recursively (that is, 'what' can call
/// render.) when this happens, a change to a dependency in the inner
/// render() won't cause the stuff in the outer render() to be
/// re-evaluated, so it serves as a recomputation fence.
///
/// XXX refactor renderList to make it use this?
/// XXX need to provide a way to stop the updating and let GC happen!!!
Sky.ui.render = function (render_func, events, event_data) {
  var result = null;
  var update = function () {
    var new_result = Sky.deps.monitor(render_func, update);
    if (result === null) {
      result = new_result;
      if (result instanceof Array)
        _.each(result, function (elt) {
          Sky.ui._setupEvents(elt, events || {}, event_data);
        });
      else
        Sky.ui._setupEvents(result, events || {}, event_data);
    } else {
      if ((new_result instanceof Array) !==
          (result instanceof Array))
        throw new Error("A template function can't change from returning an " +
                        "array to returning a single element (or vice versa)");
      if (new_result instanceof Array) {
        if (new_result.length !== result.length)
          throw new Error("A template function can't change the number of " +
                          "elements it returns (from " + result.length +
                          " to " + new_result.length + ")");
        for (var i = 0; i < result.length; i++)
          Sky.ui._patch(result[i], new_result[i]);
      } else
        Sky.ui._patch(result, new_result);
    }
  };

  update();
  return result;
};

/// Do a query on 'collection', and replace the children of 'element'
/// with the results.
///
/// If jQuery is present, then 'element' may be a jQuery result set,
/// in which case the first element is used.
///
/// options to include:
///  query: minimongo selector (default: {})
///  sort: minimongo sort specification (default: natural order)
///  render: render function (from object to element)
/// .. plus optionally
///  events: vaguely backbone-style live event specification
///    {'click #selector #path' : function (obj) { } }
///
/// returns an object with:
///  stop(): stop updating, tear everything down and let it get GC'd
Sky.ui.renderList = function (collection, element, options) {
  if ((typeof $ !== "undefined") && (element instanceof $))
    // allow element to be a jQuery result set
    element = element[0];
  var dead = false;

  var changed = function (obj, at_idx) {
    if (dead)
      return;
    var rendered = render(obj);
    element.insertBefore(rendered, element.children[at_idx]);
    element.removeChild(element.children[at_idx + 1]);
  };

  var render = function (obj) {
    var elt = Sky.deps.monitor(
      function () { return options.render(obj); },
      function () {
        var idx = query.indexOf(obj._id);
        if (idx !== -1) {
          element.insertBefore(render(obj), element.children[idx]);
          element.removeChild(element.children[idx + 1]);
        }
        // if idx === -1, then the liveQuery remove handler will have
        // taken care of removing the rendered element
      });

    Sky.ui._setupEvents(elt, options.events || {}, obj);
    return elt;
  };

  while (element.childNodes.length > 0)
    element.removeChild(element.childNodes[0]);

  // XXX duplicated in sky_client.js (hook_handlebars_each)
  var query = collection.findLive(options.query, {
    added: function (obj, before_idx) {
      if (before_idx === element.childNodes.length)
        element.appendChild(render(obj));
      else
        element.insertBefore(render(obj), element.childNodes[before_idx])
    },
    removed: function (id, at_idx) {
      element.removeChild(element.childNodes[at_idx]);
    },
    changed: function (obj, at_idx) {
      element.insertBefore(render(obj), element.childNodes[at_idx]);
      element.removeChild(element.childNodes[at_idx + 1]);
    },
    moved: function (obj, old_idx, new_idx) {
      var elt = element.removeChild(element.childNodes[old_idx]);
      if (new_idx === element.childNodes.length)
        element.appendChild(elt);
      else
        element.insertBefore(elt, element.childNodes[new_idx]);
    },
    sort: options.sort
  });

  return {
    stop: function() {
      // XXX this has terrible GC semantics. we don't get to tear
      // everything down until each monitor block experiences its
      // callback. actually there are probably a ton of bad GC issues;
      // I haven't thought about it.
      //
      // XXX more generally, this pattern where the caller has to call
      // stop() is going to result in tears. more likely, we want to
      // detect when we're taken out of the DOM somehow (like jQuery
      // does?) (or maybe even by polling the DOM every few seconds??)
      // and tear everything down automatically.
      dead = true;
      query.stop();
    }
  };
};

// XXX jQuery dependency
// 'event_data' will be an additional argument to event callback
Sky.ui._setupEvents = function (elt, events, event_data) {
  events = events || {};
  function create_callback (callback) {
    // return a function that will be used as the jquery event
    // callback, in which "this" is bound to the DOM element bound
    // to the event.
    return function (evt) {
      callback.call(event_data, evt);
    };
  };

  for (spec in events) {
    var clauses = spec.split(/,\s+/);
    _.each(clauses, function (clause) {
      var parts = clause.split(/\s+/);
      if (parts.length === 0)
        return;

      if (parts.length === 1) {
        $(elt).bind(parts[0], create_callback(events[spec]));
      } else {
        var event = parts.splice(0, 1)[0];
        $(elt).delegate(parts.join(' '), event, create_callback(events[spec]));
      }
    });
  }
};


// Source: http://code.google.com/p/google-code-prettify

// XXX this sucks. I would much rather do this processing at
// bundle-time on the server, not in the client. (though I'd like to
// support both..)

Package.describe({
  summary: "Syntax highlighting of code, from Google"
});

// XXX this code dumps symbols into the global namespace (directly
// onto 'window'.) we need to fix that.
Package.client_file('prettify.js');
Package.client_css_file('prettify.css');
Package.client_css_file('styles/sunburst.css');


// XXX need a strategy for passing the binding of $ into this
// function, from the compiled selector
//
// maybe just {key.up.to.just.before.dollarsign: array_index}
//
// XXX atomicity: if one modification fails, do we roll back the whole
// change?
Collection._modify = function (doc, mod) {
  var is_modifier = false;
  for (var k in mod) {
    is_modifier = (k[0] === '$');
    break; // just check the first key.
  }

  var new_doc;

  if (!is_modifier) {
    if (mod._id && doc._id !== mod._id)
      throw Error("Cannot change the _id of a document");

    // replace the whole document
    for (var k in mod) {
      if (k[0] === '$')
        throw Error("Field name may not start with '$'");
      if (/\./.test(k))
        throw Error("Field name may not contain '.'");
    }
    new_doc = mod;
  } else {
    // apply modifiers
    var new_doc = Collection._deepcopy(doc);

    for (var op in mod) {
      var mod_func = Collection._modifiers[op];
      if (!mod_func)
        throw Error("Invalid modifier specified " + op);
      for (var keypath in mod[op]) {
        // XXX mongo doesn't allow mod field names to end in a period,
        // but I don't see why.. it allows '' as a key, as does JS
        if (keypath.length && keypath[keypath.length-1] === '.')
          throw Error("Invalid mod field name, may not end in a period");

        var arg = mod[op][keypath];
        var keyparts = keypath.split('.');
        var no_create = !!Collection._noCreateModifiers[op];
        var forbid_array = (op === "$rename");
        var target = Collection._findModTarget(new_doc, keyparts,
                                               no_create, forbid_array);
        var field = keyparts.pop();
        mod_func(target, field, arg, keypath, new_doc);
      }
    }
  }

  // move new document into place
  for (var k in doc) {
    if (k !== '_id')
      delete doc[k];
  }
  for (var k in new_doc) {
    doc[k] = new_doc[k];
  }
};

// for a.b.c.2.d.e, keyparts should be ['a', 'b', 'c', '2', 'd', 'e'],
// and then you would operate on the 'e' property of the returned
// object. if no_create is falsey, creates intermediate levels of
// structure as necessary, like mkdir -p (and raises an exception if
// that would mean giving a non-numeric property to an array.) if
// no_create is true, return undefined instead. may modify the last
// element of keyparts to signal to the caller that it needs to use a
// different value to index into the returned object (for example,
// ['a', '01'] -> ['a', 1]). if forbid_array is true, return null if
// the keypath goes through an array.
Collection._findModTarget = function (doc, keyparts, no_create,
                                      forbid_array) {
  for (var i = 0; i < keyparts.length; i++) {
    var last = (i === keyparts.length - 1);
    var keypart = keyparts[i];
    var numeric = /^[0-9]+$/.test(keypart);
    if (no_create && (!(typeof doc === "object") || !(keypart in doc)))
      return undefined;
    if (doc instanceof Array) {
      if (forbid_array)
        return null;
      if (!numeric)
        throw Error("can't append to array using string field name ["
                    + keypart + "]")
      keypart = parseInt(keypart);
      if (last)
        // handle 'a.01'
        keyparts[i] = keypart;
      while (doc.length < keypart)
        doc.push(null);
      if (!last) {
        if (doc.length === keypart)
          doc.push({});
        else if (typeof(doc[keypart]) !== "object")
          throw Error("can't modify field '" + keyparts[i + 1] +
                      "' of list value " + JSON.stringify(doc[keypart]));
      }
    } else {
      // XXX check valid fieldname (no $ at start, no .)
      if (!last && !(keypart in doc))
        doc[keypart] = {};
    }

    if (last)
      return doc;
    doc = doc[keypart];
  }

  // notreached
};

Collection._noCreateModifiers = {
  $unset: true,
  $pop: true,
  $rename: true,
  $pull: true,
  $pullAll: true
};

Collection._modifiers = {
  $inc: function (target, field, arg) {
    if (typeof(arg) !== "number")
      throw Error("Modifier $inc allowed for numbers only");
    if (field in target) {
      if (typeof(target[field]) !== "number")
        throw Error("Cannot apply $inc modifier to non-number");
      target[field] += arg;
    } else {
      target[field] = arg;
    }
  },
  $set: function (target, field, arg) {
    target[field] = Collection._deepcopy(arg);
  },
  $unset: function (target, field, arg) {
    if (target !== undefined) {
      if (target instanceof Array) {
        if (field in target)
          target[field] = null;
      } else
        delete target[field];
    }
  },
  $push: function (target, field, arg) {
    var x = target[field];
    if (x === undefined)
      target[field] = [arg];
    else if (!(x instanceof Array))
      throw Error("Cannot apply $push modifier to non-array");
    else
      x.push(Collection._deepcopy(arg));
  },
  $pushAll: function (target, field, arg) {
    if (!(typeof(arg) === "object" && arg instanceof Array))
      throw Error("Modifier $pushAll/pullAll allowed for arrays only");
    var x = target[field];
    if (x === undefined)
      target[field] = arg;
    else if (!(x instanceof Array))
      throw Error("Cannot apply $pushAll modifier to non-array");
    else {
      for (var i = 0; i < arg.length; i++)
        x.push(arg[i]);
    }
  },
  $addToSet: function (target, field, arg) {
    var x = target[field];
    if (x === undefined)
      target[field] = [arg];
    else if (!(x instanceof Array))
      throw Error("Cannot apply $addToSet modifier to non-array");
    else {
      var isEach = false;
      if (typeof(arg) === "object") {
        for (var k in arg) {
          if (k === "$each")
            isEach = true;
          break;
        }
      }
      var values = isEach ? arg["$each"] : [arg];
      _.each(values, function (value) {
        for (var i = 0; i < x.length; i++)
          if (Collection._f._equal(value, x[i]))
            return;
        x.push(value);
      });
    }
  },
  $pop: function (target, field, arg) {
    if (target === undefined)
      return;
    var x = target[field];
    if (x === undefined)
      return;
    else if (!(x instanceof Array))
      throw Error("Cannot apply $pop modifier to non-array");
    else {
      if (typeof(arg) === 'number' && arg < 0)
        x.splice(0, 1);
      else
        x.pop();
    }
  },
  $pull: function (target, field, arg) {
    if (target === undefined)
      return;
    var x = target[field];
    if (x === undefined)
      return;
    else if (!(x instanceof Array))
      throw Error("Cannot apply $pull/pullAll modifier to non-array");
    else {
      var out = []
      if (typeof(arg) === "object" && !(arg instanceof Array)) {
        // XXX would be much nicer to compile this once, rather than
        // for each document we modify.. but usually we're not
        // modifying that many documents, so we'll let it slide for
        // now

        // XXX _compileSelector isn't up for the job, because we need
        // to permit stuff like {$pull: {a: {$gt: 4}}}.. something
        // like {$gt: 4} is not normally a complete selector.
        // same issue as $elemMatch possibly?
        var match = Collection._compileSelector(arg);
        for (var i = 0; i < x.length; i++)
          if (!match(x[i]))
            out.push(x[i])
      } else {
        for (var i = 0; i < x.length; i++)
          if (!Collection._f._equal(x[i], arg))
            out.push(x[i]);
      }
      target[field] = out;
    }
  },
  $pullAll: function (target, field, arg) {
    if (!(typeof(arg) === "object" && arg instanceof Array))
      throw Error("Modifier $pushAll/pullAll allowed for arrays only");
    if (target === undefined)
      return;
    var x = target[field];
    if (x === undefined)
      return;
    else if (!(x instanceof Array))
      throw Error("Cannot apply $pull/pullAll modifier to non-array");
    else {
      var out = []
      for (var i = 0; i < x.length; i++) {
        var exclude = false;
        for (var j = 0; j < arg.length; j++) {
          if (Collection._f._equal(x[i], arg[j])) {
            exclude = true;
            break;
          }
        }
        if (!exclude)
          out.push(x[i]);
      }
      target[field] = out;
    }
  },
  $rename: function (target, field, arg, keypath, doc) {
    if (keypath === arg)
      // no idea why mongo has this restriction..
      throw Error("$rename source must differ from target");
    if (target === null)
      throw Error("$rename source field invalid");
    if (typeof(arg) !== "string")
      throw Error("$rename target must be a string");
    if (target === undefined)
      return;
    var v = target[field];
    delete target[field];

    var keyparts = arg.split('.');
    var target2 = Collection._findModTarget(doc, keyparts, false, true);
    if (target2 === null)
      throw Error("$rename target field invalid");
    var field2 = keyparts.pop();
    target2[field2] = v;
  },
  $bit: function (target, field, arg) {
    // XXX mongo only supports $bit on integers, and we only support
    // native javascript numbers (doubles) so far, so we can't support $bit
    throw Error("$bit is not supported");
  }
};


if (typeof Sky === "undefined") Sky = {};

(function() {
  var queue = [];
  var loaded = document.readyState === "loaded" ||
    document.readyState == "complete";

  var ready = function() {
    loaded = true;
    while (queue.length)
      (queue.shift())();
  };

  if (document.addEventListener) {
    document.addEventListener('DOMContentLoaded', ready, false);
    window.addEventListener('load', ready, false);
  } else {
    document.attachEvent('onreadystatechange', function () {
      if (document.readyState === "complete")
        ready();
    });
    window.attachEvent('load', ready);
  }

  Sky.startup = function (cb) {
    var doScroll = document.documentElement.doScroll;

    if (!doScroll || window !== top) {
      if (loaded)
        cb();
      else
        queue.push(cb);
    } else {
      try { doScroll('left'); }
      catch (e) {
        setTimeout(function() { Sky.startup(cb); }, 50);
        return;
      };
      cb();
    }
  };
})();


// XXX duplicated in sky_client.js (hook_handlebars_each)

var query = collection.findLive(options.query, {

if (before_idx === element.childNodes.length)

keep keep keep keep keep replace keep keep keep keep

var query = collection.findLive(options.selector, {


if (typeof Sky === "undefined") Sky = {};

Sky.startup = function (callback) {
  __skybreak_bootstrap__.startup_hooks.push(callback);
};


Lists = Sky.Collection("lists");

Todos = Sky.Collection("todos");

/* Schema support coming soon!

Lists.schema({text: String});

Todos.schema({text: String,
              done: Boolean,
              tags: [String]});
*/

Sky.publish('lists');
Sky.publish('todos', {
  selector: function (params) {
    return {list_id: params.list};
  }
});


Package.describe({
  summary: "Allows templates to be defined in .html files",
  internal: true
});

Package.require('underscore');
Package.require('liveui');

// XXX super lame! we actually have to give paths relative to
// app/inner/app.js, since that's who's evaling us.
var html_scanner = require('../../packages/templating/html_scanner.js');

// XXX the way we deal with encodings here is sloppy .. should get
// religion on that

var fs = require('fs');
var path = require('path');

Package.register_extension(
  "html", function (filename, rel_filename, is_client, is_server) {
    if (!is_client) return; // only for the client.

    var contents = fs.readFileSync(filename);
    var results = html_scanner.scan(contents.toString('utf8'));

    if (results.head)
      Package.append_head(results.head);

    if (results.body)
      Package.append_head(results.body);

    if (results.js) {
      var path_part = path.dirname(rel_filename)
      if (path_part === '.')
        path_part = '';
      if (path_part.length && path_part !== '/')
        path_part = path_part + "/";
      var ext = path.extname(filename);
      var basename = path.basename(rel_filename, ext);
      rel_filename = path_part + "template." + basename + ".js";

      Package.client_js_buffer(rel_filename, new Buffer(results.js));
    }
  });

// provides the runtime logic to instantiate our templates
Package.client_file('deftemplate.js');

// html_scanner.js emits client code that calls Sky.startup
// XXX the correct thing would be to require this only on the client
Package.require('startup');

// for now, the only templating system we support
Package.require('handlebars');


summary: 'Compiler for CoffeeScript code, supporting the coffeescript package',

// This version of NPM `coffeescript` module, with _1, _2 etc.

// If you change this, make sure to also update ../coffeescript/package.js to match.

Package.describe({
  name: 'coffeescript',
  summary: 'Javascript dialect with fewer braces and semicolons',
  // This package version should track the version of the `coffeescript-compiler`
  // package, because people will likely only have this one added to their apps;
  // so bumping the version of this package will be how they get newer versions
  // of `coffeescript-compiler`. If you change this, make sure to also update
  // ../coffeescript-compiler/package.js to match.
  version: '2.0.3_1'
});

Package.registerBuildPlugin({
  name: 'compile-coffeescript',
  use: ['caching-compiler@1.1.9', 'ecmascript@0.8.3', 'coffeescript-compiler@=2.0.3_1'],
  sources: ['compile-coffeescript.js']
});

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

  // Because the CoffeeScript plugin now calls
  // BabelCompiler.prototype.processOneFileForTarget for any ES2015+
  // JavaScript or JavaScript enclosed by backticks, it must provide the
  // same runtime environment that the 'ecmascript' package provides.
  // The following api.imply calls should match those in ../../ecmascript/package.js,
  // except that coffeescript does not api.imply('modules').
  api.imply('ecmascript-runtime-server@0.4.1', 'server');
  api.imply('babel-runtime@1.0.1');
  api.imply('promise@0.9.0');
});

Package.onTest(function (api) {
  api.use(['coffeescript', 'tinytest']);
  api.use(['coffeescript-test-helper', 'ecmascript'], ['client', 'server']); // Need ecmascript to compile tests/es2015_module.js
  api.addFiles('tests/bare_test_setup.coffee', ['client'], {bare: true});
  api.addFiles('tests/bare_tests.js', ['client']);
  api.addFiles([
    'tests/coffeescript_test_setup.js',
    'tests/coffeescript_tests.coffee',
    'tests/coffeescript_strict_tests.coffee',
    'tests/coffeescript_module.coffee',
    'tests/es2015_module.js',
    'tests/litcoffeescript_tests.litcoffee',
    'tests/litcoffeescript_tests.coffee.md',
    'tests/coffeescript_tests.js'
  ], ['client', 'server']);
});


if (Meteor.isClient) {
  var moveCount = 0;
  var MOVE_INTERVAL = 3000;
  var MOVE_DURATION = 2000;

  doMove = function () {
    moveCount++;
    if (moveCount % 2 === 1) {
      animateToBefore($('.green'), $('.yellow'));
      animateToBefore($('.red'), null);
      animateToBefore($('.blue'), null);
    } else {
      animateToBefore($('.red'), null);
      animateToBefore($('.green'), null);
      animateToBefore($('.blue'), null);
      animateToBefore($('.yellow'), null);
    }
  };

  Meteor.startup(function () {
    doMove();
    window.setInterval(doMove, MOVE_INTERVAL);
  });

  animateToBefore = function ($n, $newNext) {
    // we don't use jQuery's `.css()` for these because we want the
    // element's own style, not the computed style
    var oldTop = $n[0].style.top;
    var oldPosition = $n[0].style.position;
    var oldZIndex = $n[0].style.zIndex;
    var oldMarginBottom = $n[0].style.marginBottom;

    var outerHeight = $n.outerHeight(); // not margin
    var marginBottom = parseInt($n.css('margin-bottom'));

    // TODO: test interesting elements like table rows, etc.
    var placeholder = $(document.createElement($n[0].nodeName));
    var placeholderHeight = outerHeight + marginBottom;
    placeholder.css('height', placeholderHeight);
    // insert placeholder
    $n.before(placeholder);

    // move node
    if ($newNext)
      $newNext.before($n);
    else
      $n.parent().append($n);

    // XXX would tracking "left" as well as "top" magically get us
    // horizontal re-ordering?
    $n.css({marginBottom: -outerHeight,
            position: 'relative',
            zIndex: 2,
            top: 0});
    var vOffset = placeholder.offset().top - $n.offset().top;
    $n.css('top', vOffset);

    $({t:0}).animate({t:1}, {
      duration: MOVE_DURATION,
      step: function (t, fx) {
        var curPlaceholderHeight = Math.round(placeholderHeight * (1-t));
        var curMarginBottom = marginBottom - curPlaceholderHeight;
        var curTop = (-curPlaceholderHeight +
                      Math.round((1-t) * (vOffset + placeholderHeight)));
        $n.css({marginBottom: curMarginBottom,
                top: curTop});
        placeholder.css('height', curPlaceholderHeight);
      },
      progress: function (a, t) {
        //          if (t >= 0.5) {
        //            console.log(a);
        //            a.stop();
        //          }
      },
      complete: function () {
        placeholder.remove();
        $n[0].style.top = oldTop;
        $n[0].style.position = oldPosition;
        $n[0].style.zIndex = oldZIndex;
        $n[0].style.marginBottom = oldMarginBottom;
      }
    });
  };

}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}


var selftest = require('../tool-testing/selftest.js');
var files = require('../fs/files.js');
var expectEqual = selftest.expectEqual;

selftest.define("create and extract tarball with long paths", function () {
  var STAMP = "stamp";

  // Create a directory with a single file in a long subdirectory, to
  // be turned into a tarball.
  var tarballInputDir = files.mkdtemp("tarball-input");
  var longDir = tarballInputDir;
  while (longDir.length < 300) {
    longDir = files.pathJoin(longDir, "subdirectory");
  }
  files.mkdir_p(longDir);
  var inputStampedFile = files.pathJoin(longDir, "file");
  files.writeFile(inputStampedFile, STAMP);

  // Make the tarball
  var tarballOutputDir = files.mkdtemp("tarball");
  var tarballOutputFile = files.pathJoin(tarballOutputDir, "out.tar.gz");
  files.createTarball(tarballInputDir, tarballOutputFile);

  // Extract the tarball and verify that the single file we created is
  // present with the expected contents.
  var tarballExtractedDir = files.mkdtemp("tarball-extracted");
  files.extractTarGz(files.readFile(tarballOutputFile), tarballExtractedDir);
  var extractedStampedFile = inputStampedFile.replace(tarballInputDir, tarballExtractedDir);
  expectEqual(files.readFile(extractedStampedFile, "utf-8"), STAMP);
});


Package.describe({
  summary: "test local package reloading"
});

Package.onUse(function (api) {
  api.export('packageVar');
  api.addFiles('foo.js', 'client');
});


# mongo-dev-server

[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/mongo-dev-server) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/mongo-dev-server)
***

When the `mongo-dev-server` package is included in a Meteor application, a
local development MongoDB server is started alongside the application. This
package is mostly used internally, as it is included by default with any
application that has a dependency on `mongo` (which is most Meteor
applications). In some cases however, people might be interested in
using the Meteor Tool without having to start a local development Mongo
instance (e.g. when using Meteor as a build system). If an application has no
dependency on `mongo`, the `mongo-dev-server` package will be removed
(since it is a direct dependency of the `mongo` package), and no local
development Mongo server will be started.

Note this is a `debugOnly` package, meaning it will not be included in any
production bundles.


import selftest, {Sandbox} from '../tool-testing/selftest.js';

selftest.define('custom minifier - devel vs prod', function (options) {
  const s = new Sandbox({
    clients: options.clients
  });

  s.createApp('myapp', 'custom-minifier');
  s.cd('myapp');

  s.testWithAllClients(function (run) {
    run.waitSecs(5);
    run.match('myapp');
    run.match('proxy');
    run.match('MongoDB');

    run.connectClient();
    run.waitSecs(20);

    // XXX when minifiers start getting applied to server target, this
    // outcome would change
    run.match('Message: foo');

    run.match('production_css: rgb(255, 0, 0)');
    run.match('development_css: rgb(0, 0, 0)');
    run.match('Message (client): production_js');

    run.stop();
  }, '--production');

  s.testWithAllClients(function (run) {
    run.waitSecs(5);
    run.match('myapp');
    run.match('proxy');
    run.match('MongoDB');

    run.connectClient();
    run.waitSecs(20);

    // XXX when minifiers start getting applied to server target, this
    // outcome would change
    run.match('Message: foo');

    run.match('production_css: rgb(0, 0, 0)');
    run.match('development_css: rgb(255, 0, 0)');
    run.match('Message (client): development_js');

    run.stop();
  }/*, development*/);
});


Package.describe({
  summary: "Used by the coffeescript package's tests",
  version: "1.1.0"
});

Package.onUse(function (api) {
  api.use('coffeescript@2.0.3_1', ['client', 'server']);
  api.export('COFFEESCRIPT_EXPORTED');
  api.export('COFFEESCRIPT_EXPORTED_ONE_MORE');
  api.export('COFFEESCRIPT_EXPORTED_WITH_BACKTICKS');
  api.addFiles("exporting.coffee", ['client', 'server']);
});


autoupdate@1.2.2-plugins.0
babel-compiler@5.8.3-plugins.0_5
babel-runtime@0.1.2
base64@1.0.4-plugins.0
binary-heap@1.0.4-plugins.0
blaze@2.1.3-plugins.0
blaze-tools@1.0.4-plugins.0
boilerplate-generator@1.0.4-plugins.0
caching-compiler@1.0.0-plugins.1
caching-html-compiler@1.0.0
callback-hook@1.0.4-plugins.0
check@1.0.6-plugins.0
ddp@1.2.0-plugins.0
ddp-client@1.2.0-plugins.0
ddp-common@1.2.0-plugins.0
ddp-server@1.2.0-plugins.1
deps@1.0.8-plugins.0
diff-sequence@1.0.0-plugins.0
ecmascript@0.1.3-plugins.1
ejson@1.0.7-plugins.0
geojson-utils@1.0.4-plugins.0
html-tools@1.0.5-plugins.0
htmljs@1.0.5-plugins.0
http@1.1.1-plugins.0
id-map@1.0.4-plugins.0
jquery@1.11.3-plugins.0_3
json@1.0.4-plugins.0
logging@1.0.8-plugins.0
meteor@1.1.7-plugins.1
minifiers@1.1.6-plugins.0
minimongo@1.0.9-plugins.0
mongo@1.1.1-plugins.0
mongo-id@1.0.0-plugins.0
npm-mongo@1.4.32-plugins.0_1
observe-sequence@1.0.7-plugins.0
ordered-dict@1.0.4-plugins.0
promise@0.4.2-plugins.0
random@1.0.4-plugins.0
reactive-var@1.0.6-plugins.0
reload@1.1.4-plugins.0
retry@1.0.4-plugins.0
routepolicy@1.0.6-plugins.0
spacebars@1.0.7-plugins.0
spacebars-compiler@1.0.7-plugins.1
static-html@1.0.12
templating@1.1.2-plugins.1
templating-tools@0.0.1
tracker@1.0.8-plugins.0
ui@1.0.7-plugins.0
underscore@1.0.4-plugins.0
url@1.0.5-plugins.0
webapp@1.2.1-plugins.0
webapp-hashing@1.0.4-plugins.0


var selftest = require('../tool-testing/selftest.js');
var Sandbox = selftest.Sandbox;
const SIMPLE_WAREHOUSE = { v1: { recommended: true } };

selftest.define("create", function () {
  // We need a warehouse so the tool doesn't think we are running from checkout
  var s = new Sandbox({ warehouse: SIMPLE_WAREHOUSE });

  // Can we create an app? Yes!
  var run = s.run("create", "foobar");
  run.waitSecs(60);
  run.match("Created a new Meteor app in 'foobar'.");
  run.match("To run your new app");
  run.expectExit(0);

  // Test that the release constraints have been written to .meteor/packages
  s.cd("foobar");
  const packages = s.read(".meteor/packages");
  if (!packages.match('meteor-base@')) {
    selftest.fail("Failed to add a version specifier to `meteor-base` package");
  }

  const packageJson = JSON.parse(s.read("package.json"));
  if (! packageJson.dependencies.hasOwnProperty("@babel/runtime")) {
    selftest.fail("New app package.json does not depend on @babel/runtime");
  }

  // Install basic packages like babel-runtime and meteor-node-stubs from
  // package.json.
  run = s.run("npm", "install");
  run.waitSecs(15);
  run.expectExit(0);

  // Now, can we run it?
  run = s.run();
  run.waitSecs(60);
  run.match("foobar");
  run.match("proxy.");
  // Do not print out the changes to the versions file!
  run.waitSecs(15);
  run.read("\n=>");
  run.waitSecs(5);
  run.match("MongoDB");
  run.waitSecs(5);
  run.match("your app");
  run.waitSecs(5);
  run.match("running at");
  run.match("localhost");
  run.stop();

  run = s.run("create", "--list");
  run.waitSecs(5);
  run.read('Available');
  run.match('leaderboard');
  run.expectExit(0);
  // XXX test that --list always gives you the examples of the current
  // release!

  // XXX XXX more more
});


Import-Module "$PSScriptRoot\dev-bundle-lib.psm1"
$PLATFORM = Get-MeteorPlatform

$windows_scripts = split-path -parent $MyInvocation.MyCommand.Definition
$scripts_path = split-path -parent $windows_scripts
$CHECKOUT_DIR = split-path -parent $scripts_path

# extract the bundle version from the meteor bash script
$BUNDLE_VERSION = select-string -Path ($CHECKOUT_DIR + "\meteor") -Pattern 'BUNDLE_VERSION=(\S+)'  | % { $_.Matches[0].Groups[1].Value } | select-object -First 1
$BUNDLE_VERSION = $BUNDLE_VERSION.Trim()

Write-Host "Will get you a dev_bundle for $PLATFORM version $BUNDLE_VERSION"

$TARBALL="dev_bundle_${PLATFORM}_${BUNDLE_VERSION}.tar.gz"

$ErrorActionPreference = "Stop"

# duplicated in top-level meteor script:
$DEV_BUNDLE_URL_ROOT="https://d3sqy0vbqsdhku.cloudfront.net/"
# If you set $USE_TEST_DEV_BUNDLE_SERVER then we will download
# dev bundles copied by copy-dev-bundle-from-jenkins.sh without --prod.
# It still only does this if the version number has changed
# (setting it won't cause it to automatically delete a prod dev bundle).
if ("$env:USE_TEST_DEV_BUNDLE_SERVER" -ne "") {
    $DEV_BUNDLE_URL_ROOT="https://s3.amazonaws.com/com.meteor.static/test/"
}

$devbundle_link = $DEV_BUNDLE_URL_ROOT + $TARBALL
$devbundle_zip = $CHECKOUT_DIR + "\" + $TARBALL

if (Test-Path $devbundle_zip) {
  Write-Host "Skipping download and installing kit from $devbundle_zip"
} else {
  Write-Host "Going to download the dependency kit from the Internet"

  $webclient = New-Object System.Net.WebClient
  $webclient.DownloadFile($devbundle_link, $devbundle_zip)

  Write-Host "... downloaded"
}

Write-Host "Extracting $TARBALL to the dev_bundle directory"

cmd /C "7z.exe x $devbundle_zip -so | 7z.exe x -aoa -si -ttar -o$CHECKOUT_DIR\dev_bundle_XXX" | out-null
if ($LASTEXITCODE -ne 0) {
  Exit 1
}

$downloaded_tmp = $CHECKOUT_DIR + "\dev_bundle_XXX"
$downloaded_path = $downloaded_tmp + "\dev_bundle_" + $PLATFORM + "_" + $BUNDLE_VERSION
$target_path = $CHECKOUT_DIR + "\dev_bundle"
Move-Item  $downloaded_path $target_path

Remove-Item -Recurse -Force $downloaded_tmp

if ("$env:SAVE_DEV_BUNDLE_TARBALL" -ne "") {
  Write-Host "Saving the dev_bundle tarball since " -NoNewLine
  Write-Host "'SAVE_DEV_BUNDLE_TARBALL' is set in your environment. " -NoNewLine
  Write-Host "Remember, these can get quite large!"
} else {
  Write-Host "Removing dev_bundle tarball. You can preserve this in " -NoNewLine
  Write-Host "the future by setting 'SAVE_DEV_BUNDLE_TARBALL' in " -NoNewLine
  Write-Host "your environment."
  Remove-Item -Force $devbundle_zip
}


Log = function () {
  return Log.info.apply(this, arguments);
};

/// FOR TESTING
var intercept = 0;
var interceptedLines = [];
var suppress = 0;

// Intercept the next 'count' calls to a Log function. The actual
// lines printed to the console can be cleared and read by calling
// Log._intercepted().
Log._intercept = function (count) {
  intercept += count;
};

// Suppress the next 'count' calls to a Log function. Use this to stop
// tests from spamming the console, especially with red errors that
// might look like a failing test.
Log._suppress = function (count) {
  suppress += count;
};

// Returns intercepted lines and resets the intercept counter.
Log._intercepted = function () {
  var lines = interceptedLines;
  interceptedLines = [];
  intercept = 0;
  return lines;
};

// Either 'json' or 'colored-text'.
//
// When this is set to 'json', print JSON documents that are parsed by another
// process ('satellite' or 'meteor run'). This other process should call
// 'Log.format' for nice output.
//
// When this is set to 'colored-text', call 'Log.format' before printing.
// This should be used for logging from within satellite, since there is no
// other process that will be reading its standard output.
Log.outputFormat = 'json';

var LEVEL_COLORS = {
  debug: 'green',
  // leave info as the default color
  warn: 'magenta',
  error: 'red'
};

var META_COLOR = 'blue';

// Default colors cause readability problems on Windows Powershell,
// switch to bright variants. While still capable of millions of
// operations per second, the benchmark showed a 25%+ increase in
// ops per second (on Node 8) by caching "process.platform".
var isWin32 = typeof process === 'object' && process.platform === 'win32';
var platformColor = function (color) {
  if (isWin32 && typeof color === 'string' && color.slice(-6) !== 'Bright') {
    return color + 'Bright';
  }
  return color;
};

// XXX package
var RESTRICTED_KEYS = ['time', 'timeInexact', 'level', 'file', 'line',
                        'program', 'originApp', 'satellite', 'stderr'];

var FORMATTED_KEYS = RESTRICTED_KEYS.concat(['app', 'message']);

var logInBrowser = function (obj) {
  var str = Log.format(obj);

  // XXX Some levels should be probably be sent to the server
  var level = obj.level;

  if ((typeof console !== 'undefined') && console[level]) {
    console[level](str);
  } else {
    // XXX Uses of Meteor._debug should probably be replaced by Log.debug or
    //     Log.info, and we should have another name for "do your best to
    //     call call console.log".
    Meteor._debug(str);
  }
};

// @returns {Object: { line: Number, file: String }}
Log._getCallerDetails = function () {
  var getStack = function () {
    // We do NOT use Error.prepareStackTrace here (a V8 extension that gets us a
    // pre-parsed stack) since it's impossible to compose it with the use of
    // Error.prepareStackTrace used on the server for source maps.
    var err = new Error;
    var stack = err.stack;
    return stack;
  };

  var stack = getStack();

  if (!stack) return {};

  var lines = stack.split('\n');

  // looking for the first line outside the logging package (or an
  // eval if we find that first)
  var line;
  for (var i = 1; i < lines.length; ++i) {
    line = lines[i];
    if (line.match(/^\s*at eval \(eval/)) {
      return {file: "eval"};
    }

    if (!line.match(/packages\/(?:local-test[:_])?logging(?:\/|\.js)/))
      break;
  }

  var details = {};

  // The format for FF is 'functionName@filePath:lineNumber'
  // The format for V8 is 'functionName (packages/logging/logging.js:81)' or
  //                      'packages/logging/logging.js:81'
  var match = /(?:[@(]| at )([^(]+?):([0-9:]+)(?:\)|$)/.exec(line);
  if (!match)
    return details;
  // in case the matched block here is line:column
  details.line = match[2].split(':')[0];

  // Possible format: https://foo.bar.com/scripts/file.js?random=foobar
  // XXX: if you can write the following in better way, please do it
  // XXX: what about evals?
  details.file = match[1].split('/').slice(-1)[0].split('?')[0];

  return details;
};

_.each(['debug', 'info', 'warn', 'error'], function (level) {
  // @param arg {String|Object}
  Log[level] = function (arg) {
    if (suppress) {
      suppress--;
      return;
    }

    var intercepted = false;
    if (intercept) {
      intercept--;
      intercepted = true;
    }

    var obj = (_.isObject(arg) && !_.isRegExp(arg) && !_.isDate(arg) ) ?
              arg : {message: new String(arg).toString() };

    _.each(RESTRICTED_KEYS, function (key) {
      if (obj[key])
        throw new Error("Can't set '" + key + "' in log message");
    });

    if (_.has(obj, 'message') && !_.isString(obj.message))
      throw new Error("The 'message' field in log objects must be a string");
    if (!obj.omitCallerDetails)
      obj = _.extend(Log._getCallerDetails(), obj);
    obj.time = new Date();
    obj.level = level;

    // XXX allow you to enable 'debug', probably per-package
    if (level === 'debug')
      return;

    if (intercepted) {
      interceptedLines.push(EJSON.stringify(obj));
    } else if (Meteor.isServer) {
      if (Log.outputFormat === 'colored-text') {
        console.log(Log.format(obj, {color: true}));
      } else if (Log.outputFormat === 'json') {
        console.log(EJSON.stringify(obj));
      } else {
        throw new Error("Unknown logging output format: " + Log.outputFormat);
      }
    } else {
      logInBrowser(obj);
    }
  };
});

// tries to parse line as EJSON. returns object if parse is successful, or null if not
Log.parse = function (line) {
  var obj = null;
  if (line && line.charAt(0) === '{') { // might be json generated from calling 'Log'
    try { obj = EJSON.parse(line); } catch (e) {}
  }

  // XXX should probably check fields other than 'time'
  if (obj && obj.time && (obj.time instanceof Date))
    return obj;
  else
    return null;
};

// formats a log object into colored human and machine-readable text
Log.format = function (obj, options) {
  obj = EJSON.clone(obj); // don't mutate the argument
  options = options || {};

  var time = obj.time;
  if (!(time instanceof Date))
    throw new Error("'time' must be a Date object");
  var timeInexact = obj.timeInexact;

  // store fields that are in FORMATTED_KEYS since we strip them
  var level = obj.level || 'info';
  var file = obj.file;
  var lineNumber = obj.line;
  var appName = obj.app || '';
  var originApp = obj.originApp;
  var message = obj.message || '';
  var program = obj.program || '';
  var satellite = obj.satellite;
  var stderr = obj.stderr || '';

  _.each(FORMATTED_KEYS, function(key) {
    delete obj[key];
  });

  if (!_.isEmpty(obj)) {
    if (message) message += " ";
    message += EJSON.stringify(obj);
  }

  var pad2 = function(n) { return n < 10 ? '0' + n : n.toString(); };
  var pad3 = function(n) { return n < 100 ? '0' + pad2(n) : n.toString(); };

  var dateStamp = time.getFullYear().toString() +
    pad2(time.getMonth() + 1 /*0-based*/) +
    pad2(time.getDate());
  var timeStamp = pad2(time.getHours()) +
        ':' +
        pad2(time.getMinutes()) +
        ':' +
        pad2(time.getSeconds()) +
        '.' +
        pad3(time.getMilliseconds());

  // eg in San Francisco in June this will be '(-7)'
  var utcOffsetStr = '(' + (-(new Date().getTimezoneOffset() / 60)) + ')';

  var appInfo = '';
  if (appName) appInfo += appName;
  if (originApp && originApp !== appName) appInfo += ' via ' + originApp;
  if (appInfo) appInfo = '[' + appInfo + '] ';

  var sourceInfoParts = [];
  if (program) sourceInfoParts.push(program);
  if (file) sourceInfoParts.push(file);
  if (lineNumber) sourceInfoParts.push(lineNumber);
  var sourceInfo = _.isEmpty(sourceInfoParts) ?
    '' : '(' + sourceInfoParts.join(':') + ') ';

  if (satellite)
    sourceInfo += ['[', satellite, ']'].join('');

  var stderrIndicator = stderr ? '(STDERR) ' : '';

  var metaPrefix = [
    level.charAt(0).toUpperCase(),
    dateStamp,
    '-',
    timeStamp,
    utcOffsetStr,
    timeInexact ? '? ' : ' ',
    appInfo,
    sourceInfo,
    stderrIndicator].join('');

  var prettify = function (line, color) {
    return (options.color && Meteor.isServer && color) ?
      require('cli-color')[color](line) : line;
  };

  return prettify(metaPrefix, platformColor(options.metaColor || META_COLOR)) +
    prettify(message, platformColor(LEVEL_COLORS[level]));
};

// Turn a line of text into a loggable object.
// @param line {String}
// @param override {Object}
Log.objFromText = function (line, override) {
  var obj = {message: line, level: "info", time: new Date(), timeInexact: true};
  return _.extend(obj, override);
};


// This package version should track the version of the `coffeescript-compiler`

// package, because people will likely only have this one added to their apps;

// so bumping the version of this package will be how they get newer versions

// of `coffeescript-compiler`. If you change this, make sure to also update

// ../coffeescript-compiler/package.js to match.


<!--
* ❓ Questions?
          Use Stack Overflow (http://stackoverflow.com/questions/tagged/meteor)
          or start a discussion on the Meteor forums (https://forums.meteor.com/).
* 💡 Feature requests?
          Visit the feature request repository (https://github.com/meteor/meteor-feature-requests).
* ❗ Bug?
          This is the right place!

Before reporting a bug, please check for existing or closed issues
first and read the instructions for filing a bug report:
https://github.com/meteor/meteor/blob/devel/CONTRIBUTING.md#reporting-a-bug-in-meteor

### This bug report should include:
- [ ] A short, but descriptive title. The title doesn't need "Meteor" in it.
- [ ] The version of Meteor showing the problem.
- [ ] The last version of Meteor where the problem did _not_ occur, if applicable.
- [ ] The operating system you're running Meteor on.
- [ ] The expected behavior.
- [ ] The actual behavior.
- [ ] A **simple** reproduction!
      (example: A GitHub repository that anyone can clone to observe the problem.)

### Independent packages

Please ensure your issue is opened in the appropriate repository:

* Feature Requests: https://github.com/meteor/meteor-feature-requests
* Blaze: https://github.com/meteor/blaze/
* Docs: https://github.com/meteor/docs/
* Guide: https://github.com/meteor/guide
-->


// By adding this package, you get a default policy where only web pages on the
// same origin as your app can frame your app.
//
// For controlling which origins can frame this app,
// BrowserPolicy.framing.disallow()
// BrowserPolicy.framing.restrictToOrigin(origin)
// BrowserPolicy.framing.allowByAnyOrigin()

var defaultXFrameOptions = "SAMEORIGIN";
var xFrameOptions = defaultXFrameOptions;

const BrowserPolicy = require("meteor/browser-policy-common").BrowserPolicy;
BrowserPolicy.framing = {};

_.extend(BrowserPolicy.framing, {
  // Exported for tests and browser-policy-common.
  _constructXFrameOptions: function () {
    return xFrameOptions;
  },
  _reset: function () {
    xFrameOptions = defaultXFrameOptions;
  },

  disallow: function () {
    xFrameOptions = "DENY";
  },
  // ALLOW-FROM not supported in Chrome or Safari.
  restrictToOrigin: function (origin) {
    // Trying to specify two allow-from throws to prevent users from
    // accidentally overwriting an allow-from origin when they think they are
    // adding multiple origins.
    if (xFrameOptions && xFrameOptions.indexOf("ALLOW-FROM") === 0)
      throw new Error("You can only specify one origin that is allowed to" +
                      " frame this app.");
    xFrameOptions = "ALLOW-FROM " + origin;
  },
  allowAll: function () {
    xFrameOptions = null;
  }
});

exports.BrowserPolicy = BrowserPolicy;


use: ['caching-compiler@1.1.9', 'ecmascript@0.8.3', 'coffeescript-compiler@=2.0.3_1'],

use: ['caching-compiler@1.1.9', 'ecmascript@0.8.3', 'coffeescript-compiler@=2.0.3_2'],


Package.describe({
  summary: "a package",
  version: "1.0.0"
});

Package.onUse(function (api) {
  api.addFiles("blah.js");
});


// Because this file is contained by a client/compatibility directory, it
// should be evaluated as a bare file instead of as a module, so this
// variable declaration should take effect in the scope of the app, and
// thus be visible to other modules in the app.
var topLevelVariable = 1234;


summary: "Utility code for constructing URLs",

URL = {};

var encodeString = function (str) {
  return encodeURIComponent(str).replace(/\*/g, '%2A');
};

// Encode URL paramaters into a query string, handling nested objects and
// arrays properly.
URL._encodeParams = function (params, prefix) {
  var str = [];
  for (var p in params) {
    if (Object.prototype.hasOwnProperty.call(params, p)) {
      var k = prefix ? prefix + '[' + p + ']' : p, v = params[p];
      if (typeof v === 'object') {
        str.push(this._encodeParams(v, k));
      } else {
        var encodedKey =
          encodeString(k).replace('%5B', '[').replace('%5D', ']');
        str.push(encodedKey + '=' + encodeString(v));
      }
    }
  }
  return str.join('&').replace(/%20/g, '+');
};

buildUrl = function(before_qmark, from_qmark, opt_query, opt_params) {
  var url_without_query = before_qmark;
  var query = from_qmark ? from_qmark.slice(1) : null;

  if (typeof opt_query === "string")
    query = String(opt_query);

  if (opt_params) {
    query = query || "";
    var prms = URL._encodeParams(opt_params);
    if (query && prms)
      query += '&';
    query += prms;
  }

  var url = url_without_query;
  if (query !== null)
    url += ("?"+query);

  return url;
};


# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.

meteor-base
throwing-package
ecmascript