<commit_before>Components.utils.import('resource://greasemonkey/prefmanager.js');
Components.utils.import('resource://greasemonkey/util.js');

// this file is the JavaScript backing for the UI wrangling which happens in
// browser.xul. It also initializes the Greasemonkey singleton which contains
// all the main injection logic, though that should probably be a proper XPCOM
// service and wouldn't need to be initialized in that case.

function GM_BrowserUI() {};

/**
 * nsISupports.QueryInterface
 */
GM_BrowserUI.QueryInterface = function(aIID) {
  if (!aIID.equals(Components.interfaces.nsISupports) &&
      !aIID.equals(Components.interfaces.nsIObserver) &&
      !aIID.equals(Components.interfaces.nsISupportsWeakReference))
    throw Components.results.NS_ERROR_NO_INTERFACE;

  return GM_BrowserUI;
};

GM_BrowserUI.init = function() {
  window.addEventListener("load", GM_BrowserUI.chromeLoad, false);
  window.addEventListener("unload", GM_BrowserUI.chromeUnload, false);
};

/**
 * The browser XUL has loaded. Find the elements we need and set up our
 * listeners and wrapper objects.
 */
GM_BrowserUI.chromeLoad = function(e) {
  // Store DOM element references in this object, also for use elsewhere.
  GM_BrowserUI.tabBrowser = document.getElementById("content");
  GM_BrowserUI.bundle = document.getElementById("gm-browser-bundle");

  // Update visual status when enabled state changes.
  GM_prefRoot.watch("enabled", GM_BrowserUI.refreshStatus);
  GM_BrowserUI.refreshStatus();

  // Use the appcontent element specifically, see #1344.
  document.getElementById("appcontent")
      .addEventListener("DOMContentLoaded", GM_BrowserUI.contentLoad, true);
  gBrowser.addEventListener("pagehide", GM_BrowserUI.pagehide, true);
  gBrowser.addEventListener("pageshow", GM_BrowserUI.pageshow, true);

  var sidebar = document.getElementById("sidebar");
  sidebar.addEventListener("DOMContentLoaded", GM_BrowserUI.contentLoad, true);
  sidebar.addEventListener("pagehide", GM_BrowserUI.pagehide, true);
  sidebar.addEventListener("pageshow", GM_BrowserUI.pageshow, true);

  document.getElementById("contentAreaContextMenu")
    .addEventListener("popupshowing", GM_BrowserUI.contextMenuShowing, false);

  var observerService = Components.classes["@mozilla.org/observer-service;1"]
     .getService(Components.interfaces.nsIObserverService);
  observerService.addObserver(GM_BrowserUI, "install-userscript", true);
  observerService.addObserver(GM_BrowserUI, "inner-window-destroyed", true);

  // we use this to determine if we are the active window sometimes
  GM_BrowserUI.winWat = Components
      .classes["@mozilla.org/embedcomp/window-watcher;1"]
      .getService(Components.interfaces.nsIWindowWatcher);

  GM_BrowserUI.gmSvc = GM_util.getService();
  // Reference this once, so that the getter is called at least once, and the
  // initialization routines will run, no matter what.
  GM_BrowserUI.gmSvc.config;

  GM_BrowserUI.showToolbarButton();
};

GM_BrowserUI.contentLoad = function(event) {
  if (!GM_util.getEnabled()) return;

  var safeWin = event.target.defaultView;
  var href = safeWin.location.href;

  // Make sure we are still on the page that fired this event, see issue #1083.
  // But ignore hashes; see issue #1445.
  if (href.replace(/#.*/, '') == event.target.documentURI.replace(/#.*/, '')) {
    GM_BrowserUI.gmSvc.runScripts('document-end', safeWin);
  }
};

GM_BrowserUI.pagehide = function(aEvent) {
  var windowId = GM_util.windowIdForEvent(aEvent);
  if (aEvent.persisted) {
    GM_BrowserUI.gmSvc.contentFrozen(windowId);
  } else {
    GM_BrowserUI.gmSvc.contentDestroyed(windowId);
  }
};

GM_BrowserUI.pageshow = function(aEvent) {
  var windowId = GM_util.windowIdForEvent(aEvent);
  GM_BrowserUI.gmSvc.contentThawed(windowId);
};

/**
 * Implements nsIObserve.observe. Right now we're only observing our own
 * install-userscript, which happens when the install bar is clicked.
 */
GM_BrowserUI.observe = function(subject, topic, data) {
  if (topic == "install-userscript") {
    if (window == GM_BrowserUI.winWat.activeWindow) {
      GM_BrowserUI.installCurrentScript();
    }
  } else if (topic == "dom-window-destroyed") {
    GM_BrowserUI.gmSvc.contentDestroyed(GM_util.windowId(subject));
  } else if (topic == "inner-window-destroyed") {
    GM_BrowserUI.gmSvc.contentDestroyed(
        subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data);
  } else {
    throw new Error("Unexpected topic received: {" + topic + "}");
  }
};

GM_BrowserUI.openTab = function(url) {
  gBrowser.selectedTab = gBrowser.addTab(url);
};

/**
 * The browser XUL has unloaded. Destroy references/watchers/listeners.
 */
GM_BrowserUI.chromeUnload = function() {
  GM_prefRoot.unwatch("enabled", GM_BrowserUI.refreshStatus);
};

/**
 * Called when the content area context menu is showing. We figure out whether
 * to show our context items.
 */
GM_BrowserUI.contextMenuShowing = function() {
  var contextItem = document.getElementById("greasemonkey-view-userscript");
  var contextSep = document.getElementById("greasemonkey-install-sep");

  var culprit = document.popupNode;

  while (culprit && culprit.tagName && culprit.tagName.toLowerCase() != "a") {
     culprit = culprit.parentNode;
  }

  contextItem.hidden =
    contextSep.hidden =
    !GM_BrowserUI.getUserScriptLinkUnderPointer();
};

GM_BrowserUI.getUserScriptLinkUnderPointer = function() {
  var culprit = document.popupNode;

  while (culprit && culprit.tagName && culprit.tagName.toLowerCase() != "a") {
     culprit = culprit.parentNode;
  }

  if (!culprit || !culprit.href ||
      !culprit.href.match(/\.user\.js(\?|$)/i)) {
    return null;
  }

  var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]
                        .getService(Components.interfaces.nsIIOService);
  var uri = ioSvc.newURI(culprit.href, null, null);

  return uri;
};

GM_BrowserUI.refreshStatus = function() {
  var enabledEl = document.getElementById("gm_toggle_enabled");
  var checkedEl = document.getElementById("gm_toggle_checked");

  if (GM_util.getEnabled()) {
    checkedEl.setAttribute('checked', true);
    enabledEl.removeAttribute('disabled');
  } else {
    checkedEl.removeAttribute('checked');
    enabledEl.setAttribute('disabled', 'yes');
  }
};

// Not used directly, kept for GreaseFire.  See #1507.
GM_BrowserUI.startInstallScript = function(aUri) {
  GM_util.showInstallDialog(aUri.spec, gBrowser, GM_util.getService());
};

GM_BrowserUI.viewContextItemClicked = function() {
  var uri = GM_BrowserUI.getUserScriptLinkUnderPointer();
  if (!uri) return;

  var scope = {};
  Components.utils.import('resource://greasemonkey/remoteScript.js', scope);
  var rs = new scope.RemoteScript(uri.spec);
  rs.downloadScript(function(aSuccess) {
    if (aSuccess) {
      rs.showSource(gBrowser);
    } else {
      alert(rs.errorMessage);
    }
  });
};

GM_BrowserUI.showToolbarButton = function() {
  // Once, enforce that the toolbar button is present.  For discoverability.
  if (!GM_prefRoot.getValue('haveInsertedToolbarbutton')) {
    GM_prefRoot.setValue('haveInsertedToolbarbutton', true);

    var navbar = document.getElementById("nav-bar");
    var newset = navbar.currentSet + ",greasemonkey-tbb";
    navbar.currentSet = newset;
    navbar.setAttribute("currentset", newset);
    document.persist("nav-bar", "currentset");
  }
};

GM_BrowserUI.openOptions = function() {
  openDialog('chrome://greasemonkey/content/options.xul', null, 'modal');
};

GM_BrowserUI.init();

/**
 * Handle clicking one of the items in the popup. Left-click toggles the enabled
 * state, right-click opens in an editor.
 */
function GM_popupClicked(aEvent) {
  var script = aEvent.target.script;
  if (!script) return;

  if ('command' == aEvent.type) {
    // left-click: toggle enabled state
    script.enabled =! script.enabled;
  } else if ('click' == aEvent.type && aEvent.button == 2) {
    // right-click: open in editor
    GM_util.openInEditor(script);
  }

  closeMenus(aEvent.target);
}

/**
 * When a menu pops up, fill its contents with the list of scripts.
 */
function GM_showPopup(aEvent) {
  function urlsOfAllFrames(contentWindow) {
    var urls = [contentWindow.location.href];
    function collect(contentWindow) {
      urls = urls.concat(urlsOfAllFrames(contentWindow));
    }
    Array.prototype.slice.call(contentWindow.frames).forEach(collect);
    return urls;
  }

  function uniq(a) {
    var seen = {}, list = [], item;
    for (var i = 0; i < a.length; i++) {
      item = a[i];
      if (!seen.hasOwnProperty(item))
        seen[item] = list.push(item);
    }
    return list;
  }

  function scriptsMatching(urls) {
    function testMatchURLs(script) {
      function testMatchURL(url) {
        return script.matchesURL(url);
      }
      return urls.some(testMatchURL);
    }
    return GM_util.getService().config.getMatchingScripts(testMatchURLs);
  }

  function appendScriptAfter(script, point) {
    if (script.needsUninstall) return;
    var mi = document.createElement("menuitem");
    mi.setAttribute("label", script.name);
    mi.script = script;
    mi.setAttribute("type", "checkbox");
    mi.setAttribute("checked", script.enabled.toString());
    point.parentNode.insertBefore(mi, point.nextSibling);
    return mi;
  }

  var popup = aEvent.target;
  var scriptsFramedEl = popup.getElementsByClassName("scripts-framed-point")[0];
  var scriptsTopEl = popup.getElementsByClassName("scripts-top-point")[0];
  var scriptsSepEl = popup.getElementsByClassName("scripts-sep")[0];
  var noScriptsEl = popup.getElementsByClassName("no-scripts")[0];

  // Remove existing menu items, between separators.
  function removeMenuitemsAfter(el) {
    while (true) {
      var sibling = el.nextSibling;
      if (!sibling || 'menuseparator' == sibling.tagName) break;
      sibling.parentNode.removeChild(sibling);
    }
  }
  removeMenuitemsAfter(scriptsFramedEl);
  removeMenuitemsAfter(scriptsTopEl);

  var urls = uniq( urlsOfAllFrames( getBrowser().contentWindow ));
  var runsOnTop = scriptsMatching( [urls.shift()] ); // first url = top window
  var runsFramed = scriptsMatching( urls ); // remainder are all its subframes

  // drop all runsFramed scripts already present in runsOnTop
  for (var i = 0; i < runsOnTop.length; i++) {
    var j = 0, item = runsOnTop[i];
    while (j < runsFramed.length) {
      if (item === runsFramed[j]) {
        runsFramed.splice(j, 1);
      } else {
        j++;
      }
    }
  }

  scriptsSepEl.collapsed = !(runsOnTop.length && runsFramed.length);
  noScriptsEl.collapsed = !!(runsOnTop.length || runsFramed.length);

  if (runsFramed.length) {
    var point = scriptsFramedEl;
    runsFramed.forEach(
        function(script) { point = appendScriptAfter(script, point); });
  }
  var point = scriptsTopEl;
  runsOnTop.forEach(
      function(script) { point = appendScriptAfter(script, point); });

  // Delegate menu commands call.
  var menuCommandPopup = popup.getElementsByTagName('menupopup')[0];
  GM_MenuCommander.onPopupShowing(menuCommandPopup);
}

/**
 * Clean up the menu after it hides to prevent memory leaks
 */
function GM_hidePopup(aEvent) {
  var popup = aEvent.target;

  var menuCommandPopup = popup.getElementsByTagName('menupopup')[0];
  if(menuCommandPopup) GM_util.emptyEl(menuCommandPopup);
}

// Short-term workaround for #1406: Tab Mix Plus breaks opening links in
// new tabs because it depends on this function, and incorrectly checks for
// existance of GM_BrowserUI instead of it.
function GM_getEnabled() {
  return GM_util.getEnabled();
}
<commit_msg>Set command checked attribute to "false", don't remove.<commit_after>Components.utils.import('resource://greasemonkey/prefmanager.js');
Components.utils.import('resource://greasemonkey/util.js');

// this file is the JavaScript backing for the UI wrangling which happens in
// browser.xul. It also initializes the Greasemonkey singleton which contains
// all the main injection logic, though that should probably be a proper XPCOM
// service and wouldn't need to be initialized in that case.

function GM_BrowserUI() {};

/**
 * nsISupports.QueryInterface
 */
GM_BrowserUI.QueryInterface = function(aIID) {
  if (!aIID.equals(Components.interfaces.nsISupports) &&
      !aIID.equals(Components.interfaces.nsIObserver) &&
      !aIID.equals(Components.interfaces.nsISupportsWeakReference))
    throw Components.results.NS_ERROR_NO_INTERFACE;

  return GM_BrowserUI;
};

GM_BrowserUI.init = function() {
  window.addEventListener("load", GM_BrowserUI.chromeLoad, false);
  window.addEventListener("unload", GM_BrowserUI.chromeUnload, false);
};

/**
 * The browser XUL has loaded. Find the elements we need and set up our
 * listeners and wrapper objects.
 */
GM_BrowserUI.chromeLoad = function(e) {
  // Store DOM element references in this object, also for use elsewhere.
  GM_BrowserUI.tabBrowser = document.getElementById("content");
  GM_BrowserUI.bundle = document.getElementById("gm-browser-bundle");

  // Update visual status when enabled state changes.
  GM_prefRoot.watch("enabled", GM_BrowserUI.refreshStatus);
  GM_BrowserUI.refreshStatus();

  // Use the appcontent element specifically, see #1344.
  document.getElementById("appcontent")
      .addEventListener("DOMContentLoaded", GM_BrowserUI.contentLoad, true);
  gBrowser.addEventListener("pagehide", GM_BrowserUI.pagehide, true);
  gBrowser.addEventListener("pageshow", GM_BrowserUI.pageshow, true);

  var sidebar = document.getElementById("sidebar");
  sidebar.addEventListener("DOMContentLoaded", GM_BrowserUI.contentLoad, true);
  sidebar.addEventListener("pagehide", GM_BrowserUI.pagehide, true);
  sidebar.addEventListener("pageshow", GM_BrowserUI.pageshow, true);

  document.getElementById("contentAreaContextMenu")
    .addEventListener("popupshowing", GM_BrowserUI.contextMenuShowing, false);

  var observerService = Components.classes["@mozilla.org/observer-service;1"]
     .getService(Components.interfaces.nsIObserverService);
  observerService.addObserver(GM_BrowserUI, "install-userscript", true);
  observerService.addObserver(GM_BrowserUI, "inner-window-destroyed", true);

  // we use this to determine if we are the active window sometimes
  GM_BrowserUI.winWat = Components
      .classes["@mozilla.org/embedcomp/window-watcher;1"]
      .getService(Components.interfaces.nsIWindowWatcher);

  GM_BrowserUI.gmSvc = GM_util.getService();
  // Reference this once, so that the getter is called at least once, and the
  // initialization routines will run, no matter what.
  GM_BrowserUI.gmSvc.config;

  GM_BrowserUI.showToolbarButton();
};

GM_BrowserUI.contentLoad = function(event) {
  if (!GM_util.getEnabled()) return;

  var safeWin = event.target.defaultView;
  var href = safeWin.location.href;

  // Make sure we are still on the page that fired this event, see issue #1083.
  // But ignore hashes; see issue #1445.
  if (href.replace(/#.*/, '') == event.target.documentURI.replace(/#.*/, '')) {
    GM_BrowserUI.gmSvc.runScripts('document-end', safeWin);
  }
};

GM_BrowserUI.pagehide = function(aEvent) {
  var windowId = GM_util.windowIdForEvent(aEvent);
  if (aEvent.persisted) {
    GM_BrowserUI.gmSvc.contentFrozen(windowId);
  } else {
    GM_BrowserUI.gmSvc.contentDestroyed(windowId);
  }
};

GM_BrowserUI.pageshow = function(aEvent) {
  var windowId = GM_util.windowIdForEvent(aEvent);
  GM_BrowserUI.gmSvc.contentThawed(windowId);
};

/**
 * Implements nsIObserve.observe. Right now we're only observing our own
 * install-userscript, which happens when the install bar is clicked.
 */
GM_BrowserUI.observe = function(subject, topic, data) {
  if (topic == "install-userscript") {
    if (window == GM_BrowserUI.winWat.activeWindow) {
      GM_BrowserUI.installCurrentScript();
    }
  } else if (topic == "dom-window-destroyed") {
    GM_BrowserUI.gmSvc.contentDestroyed(GM_util.windowId(subject));
  } else if (topic == "inner-window-destroyed") {
    GM_BrowserUI.gmSvc.contentDestroyed(
        subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data);
  } else {
    throw new Error("Unexpected topic received: {" + topic + "}");
  }
};

GM_BrowserUI.openTab = function(url) {
  gBrowser.selectedTab = gBrowser.addTab(url);
};

/**
 * The browser XUL has unloaded. Destroy references/watchers/listeners.
 */
GM_BrowserUI.chromeUnload = function() {
  GM_prefRoot.unwatch("enabled", GM_BrowserUI.refreshStatus);
};

/**
 * Called when the content area context menu is showing. We figure out whether
 * to show our context items.
 */
GM_BrowserUI.contextMenuShowing = function() {
  var contextItem = document.getElementById("greasemonkey-view-userscript");
  var contextSep = document.getElementById("greasemonkey-install-sep");

  var culprit = document.popupNode;

  while (culprit && culprit.tagName && culprit.tagName.toLowerCase() != "a") {
     culprit = culprit.parentNode;
  }

  contextItem.hidden =
    contextSep.hidden =
    !GM_BrowserUI.getUserScriptLinkUnderPointer();
};

GM_BrowserUI.getUserScriptLinkUnderPointer = function() {
  var culprit = document.popupNode;

  while (culprit && culprit.tagName && culprit.tagName.toLowerCase() != "a") {
     culprit = culprit.parentNode;
  }

  if (!culprit || !culprit.href ||
      !culprit.href.match(/\.user\.js(\?|$)/i)) {
    return null;
  }

  var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]
                        .getService(Components.interfaces.nsIIOService);
  var uri = ioSvc.newURI(culprit.href, null, null);

  return uri;
};

GM_BrowserUI.refreshStatus = function() {
  var enabledEl = document.getElementById("gm_toggle_enabled");
  var checkedEl = document.getElementById("gm_toggle_checked");

  if (GM_util.getEnabled()) {
    checkedEl.setAttribute('checked', true);
    enabledEl.removeAttribute('disabled');
  } else {
    checkedEl.setAttribute('checked', false);
    enabledEl.setAttribute('disabled', 'yes');
  }
};

// Not used directly, kept for GreaseFire.  See #1507.
GM_BrowserUI.startInstallScript = function(aUri) {
  GM_util.showInstallDialog(aUri.spec, gBrowser, GM_util.getService());
};

GM_BrowserUI.viewContextItemClicked = function() {
  var uri = GM_BrowserUI.getUserScriptLinkUnderPointer();
  if (!uri) return;

  var scope = {};
  Components.utils.import('resource://greasemonkey/remoteScript.js', scope);
  var rs = new scope.RemoteScript(uri.spec);
  rs.downloadScript(function(aSuccess) {
    if (aSuccess) {
      rs.showSource(gBrowser);
    } else {
      alert(rs.errorMessage);
    }
  });
};

GM_BrowserUI.showToolbarButton = function() {
  // Once, enforce that the toolbar button is present.  For discoverability.
  if (!GM_prefRoot.getValue('haveInsertedToolbarbutton')) {
    GM_prefRoot.setValue('haveInsertedToolbarbutton', true);

    var navbar = document.getElementById("nav-bar");
    var newset = navbar.currentSet + ",greasemonkey-tbb";
    navbar.currentSet = newset;
    navbar.setAttribute("currentset", newset);
    document.persist("nav-bar", "currentset");
  }
};

GM_BrowserUI.openOptions = function() {
  openDialog('chrome://greasemonkey/content/options.xul', null, 'modal');
};

GM_BrowserUI.init();

/**
 * Handle clicking one of the items in the popup. Left-click toggles the enabled
 * state, right-click opens in an editor.
 */
function GM_popupClicked(aEvent) {
  var script = aEvent.target.script;
  if (!script) return;

  if ('command' == aEvent.type) {
    // left-click: toggle enabled state
    script.enabled =! script.enabled;
  } else if ('click' == aEvent.type && aEvent.button == 2) {
    // right-click: open in editor
    GM_util.openInEditor(script);
  }

  closeMenus(aEvent.target);
}

/**
 * When a menu pops up, fill its contents with the list of scripts.
 */
function GM_showPopup(aEvent) {
  function urlsOfAllFrames(contentWindow) {
    var urls = [contentWindow.location.href];
    function collect(contentWindow) {
      urls = urls.concat(urlsOfAllFrames(contentWindow));
    }
    Array.prototype.slice.call(contentWindow.frames).forEach(collect);
    return urls;
  }

  function uniq(a) {
    var seen = {}, list = [], item;
    for (var i = 0; i < a.length; i++) {
      item = a[i];
      if (!seen.hasOwnProperty(item))
        seen[item] = list.push(item);
    }
    return list;
  }

  function scriptsMatching(urls) {
    function testMatchURLs(script) {
      function testMatchURL(url) {
        return script.matchesURL(url);
      }
      return urls.some(testMatchURL);
    }
    return GM_util.getService().config.getMatchingScripts(testMatchURLs);
  }

  function appendScriptAfter(script, point) {
    if (script.needsUninstall) return;
    var mi = document.createElement("menuitem");
    mi.setAttribute("label", script.name);
    mi.script = script;
    mi.setAttribute("type", "checkbox");
    mi.setAttribute("checked", script.enabled.toString());
    point.parentNode.insertBefore(mi, point.nextSibling);
    return mi;
  }

  var popup = aEvent.target;
  var scriptsFramedEl = popup.getElementsByClassName("scripts-framed-point")[0];
  var scriptsTopEl = popup.getElementsByClassName("scripts-top-point")[0];
  var scriptsSepEl = popup.getElementsByClassName("scripts-sep")[0];
  var noScriptsEl = popup.getElementsByClassName("no-scripts")[0];

  // Remove existing menu items, between separators.
  function removeMenuitemsAfter(el) {
    while (true) {
      var sibling = el.nextSibling;
      if (!sibling || 'menuseparator' == sibling.tagName) break;
      sibling.parentNode.removeChild(sibling);
    }
  }
  removeMenuitemsAfter(scriptsFramedEl);
  removeMenuitemsAfter(scriptsTopEl);

  var urls = uniq( urlsOfAllFrames( getBrowser().contentWindow ));
  var runsOnTop = scriptsMatching( [urls.shift()] ); // first url = top window
  var runsFramed = scriptsMatching( urls ); // remainder are all its subframes

  // drop all runsFramed scripts already present in runsOnTop
  for (var i = 0; i < runsOnTop.length; i++) {
    var j = 0, item = runsOnTop[i];
    while (j < runsFramed.length) {
      if (item === runsFramed[j]) {
        runsFramed.splice(j, 1);
      } else {
        j++;
      }
    }
  }

  scriptsSepEl.collapsed = !(runsOnTop.length && runsFramed.length);
  noScriptsEl.collapsed = !!(runsOnTop.length || runsFramed.length);

  if (runsFramed.length) {
    var point = scriptsFramedEl;
    runsFramed.forEach(
        function(script) { point = appendScriptAfter(script, point); });
  }
  var point = scriptsTopEl;
  runsOnTop.forEach(
      function(script) { point = appendScriptAfter(script, point); });

  // Delegate menu commands call.
  var menuCommandPopup = popup.getElementsByTagName('menupopup')[0];
  GM_MenuCommander.onPopupShowing(menuCommandPopup);
}

/**
 * Clean up the menu after it hides to prevent memory leaks
 */
function GM_hidePopup(aEvent) {
  var popup = aEvent.target;

  var menuCommandPopup = popup.getElementsByTagName('menupopup')[0];
  if(menuCommandPopup) GM_util.emptyEl(menuCommandPopup);
}

// Short-term workaround for #1406: Tab Mix Plus breaks opening links in
// new tabs because it depends on this function, and incorrectly checks for
// existance of GM_BrowserUI instead of it.
function GM_getEnabled() {
  return GM_util.getEnabled();
}
<|endoftext|>


<commit_before>'use babel';

import { CompositeDisposable } from 'atom';
import $ from 'jquery';
import { html as beautify } from 'js-beautify';

export default {

  subscriptions: null,

  activate() {
    this.subscriptions = new CompositeDisposable();

    // Register command that toggles this view
    this.subscriptions.add(atom.commands.add('atom-workspace', {
      'bc-form-toolkit:fullCleanup': () => this.fullCleanup()
    }));
  },

  deactivate() {
    this.subscriptions.dispose();
  },

  tidyEditorContents() {
    let editor;
    if (editor = atom.workspace.getActiveTextEditor()) {
      let editorContents = editor.getText();
      editorContents = beautify(editorContents, {'indent_size': atom.config.get('editor.tabLength')});

      return editor.setText(editorContents);
    }
  },

  fullCleanup() {
    let editor;
    if (editor = atom.workspace.getActiveTextEditor()) {
      let editorContents = editor.getText();
      let bcForm = $('
').addClass('bc-form').append(editor.getText());

      this.removeTableStructure(bcForm);
      this.removeTitleField(bcForm);
      this.removeBreaks(bcForm);
      this.formatBootstrap(bcForm);
      this.labelRequired(bcForm);
      this.modernizeInputs(bcForm);
      this.addPlaceholderAttributes(bcForm);
      //this.modernizeCaptchas(bcForm);

      let bcFormClean = $(bcForm).html();
      editor.setText(bcFormClean);
      this.tidyEditorContents();
    }
  },

  removeTableStructure(bcForm) {
    $(bcForm).find('table.webform').contents().unwrap();
    $(bcForm).find('thead').contents().unwrap();
    $(bcForm).find('tbody').contents().unwrap();
    $(bcForm).find('tfoot').contents().unwrap();
    $(bcForm).find('tr').contents().unwrap();

    $(bcForm).find('td').each(function(){
      $(this).wrap('<fieldset/>');
      $(this).children().filter(':first').unwrap();
    });

    return bcForm;
  },

  removeTitleField(bcForm) {
    $(bcForm).find('label[for="Title"]').parent().remove();

    return bcForm;
  },

  removeBreaks(bcForm) {
    $(bcForm).find('br').remove();

    return bcForm;
  },

  formatBootstrap(bcForm) {

    //convert default BC classes to bootstrap classes
    $(bcForm).find('fieldset').addClass('form-group');
    $(bcForm).find('.cat_textbox').addClass('form-control').removeClass('cat_textbox');
    $(bcForm).find('.cat_dropdown').addClass('form-control').removeClass('cat_dropdown');
    $(bcForm).find('.cat_dropdown_smaller').addClass('form-control').removeClass('cat_dropdown_smaller');
    $(bcForm).find('.cat_listbox').addClass('form-control').removeClass('cat_listbox');
    $(bcForm).find('.cat_button').addClass('btn btn-default').removeClass('cat_button');

    return bcForm;
  },

  labelRequired(bcForm) {
    //remove top "required" text
    $(bcForm).find('form').contents().filter(function() { return this.nodeType === 3; }).remove();

    //reformat "required" labels
    $(bcForm).find('span.req').parents('fieldset').addClass('required');
    $(bcForm).find('fieldset.required label').append('Required');
    $(bcForm).find('span.req').remove();

    return bcForm;
  },

  modernizeInputs(bcForm) {
    //email address fields
    $(bcForm).find('input[name="EmailAddress"]').attr('type','email');

    //phone number fields
    $(bcForm).find('input[name="PhoneNumber"]').attr('type','tel');
    $(bcForm).find('input[name="HomePhone"]').attr('type','tel');
    $(bcForm).find('input[name="WorkPhone"]').attr('type','tel');
    $(bcForm).find('input[name="CellPhone"]').attr('type','tel');
    $(bcForm).find('input[name="HomeFax"]').attr('type','tel');
    $(bcForm).find('input[name="WorkFax"]').attr('type','tel');

    //number fields
    $(bcForm).find('input[name="HomeZip"]').attr('type','number');
    $(bcForm).find('input[name="WorkZip"]').attr('type','number');
    $(bcForm).find('input[name="BillingZip"]').attr('type','number');
    $(bcForm).find('input[name="ShippingZip"]').attr('type','number');
    $(bcForm).find('input[name="CardNumber"]').attr('type','number');
    $(bcForm).find('input[name="CardCCV"]').attr('type','number');
    $(bcForm).find('input[name="Amount"]').attr('type','number');

    return bcForm;
  },

  addPlaceholderAttributes(bcForm) {
    //email address fields
    $(bcForm).find('input[type="email"]').attr('placeholder','janedoe@gmail.com');

    //phone number fields
    $(bcForm).find('input[type="tel"]').attr('placeholder','(234) 555-6789');

    return bcForm;
  },

  modernizeCaptchas(bcForm) {
    //remove top "required" note
    bcForm = bcForm.replace('{module_captcha}', '{module_recaptcha version="2" size="compact"}');
    bcForm = bcForm.replace('{module_captchav2}', '{module_recaptcha version="2" size="compact"}');
    bcForm = bcForm.replace('{module_recaptcha}', '{module_recaptcha version="2" size="compact"}');
    bcForm = bcForm.replace('{module_recaptcha version="2"}', '{module_recaptcha version="2" size="compact"}');

    return bcForm;
  }

};
<commit_msg>Added support for selecting text<commit_after>'use babel';

import {
	CompositeDisposable
}
from 'atom';
import $ from 'jquery';
import {
	html as beautify
}
from 'js-beautify';

export default {

	subscriptions: null,

	activate() {
		this.subscriptions = new CompositeDisposable();

		// Register command that toggles this view
		this.subscriptions.add(atom.commands.add('atom-workspace', {
			'bc-form-toolkit:fullCleanup': () => this.fullCleanup()
		}));
	},

	deactivate() {
		this.subscriptions.dispose();
	},

	tidyMarkup(markup) {
		markup = beautify(markup, {
			'indent_size': atom.config.get('editor.tabLength')
		});

		return markup;
	},

	fullCleanup() {
		let editor;
		if (editor = atom.workspace.getActiveTextEditor()) {
			let editorContents = editor.getText();
			let editorSelected = editor.getSelectedText();
			let bcForm, bcFormOriginal;

			if (editorSelected.length > 0) { //if anything is selected, use that
				bcForm = editorSelected;
			}
			else {
				bcForm = editorContents;
			}

			bcFormOriginal = bcForm; //for a search and replace later;
			bcForm = $('
').addClass('bc-form').append(bcForm); //have to make this to do jQuery operations on

			this.removeTableStructure(bcForm);
			this.removeTitleField(bcForm);
			this.removeBreaks(bcForm);
			this.formatBootstrap(bcForm);
			this.labelRequired(bcForm);
			this.modernizeInputs(bcForm);
			this.addPlaceholderAttributes(bcForm);
			//this.modernizeCaptchas(bcForm);

			bcForm = $(bcForm).html();
			editorContents = editorContents.replace(bcFormOriginal,bcForm);
			editorContents = this.tidyMarkup(editorContents);

			editor.setText(editorContents);
		}
	},

	removeTableStructure(bcForm) {
		$(bcForm).find('table.webform').contents().unwrap();
		$(bcForm).find('thead').contents().unwrap();
		$(bcForm).find('tbody').contents().unwrap();
		$(bcForm).find('tfoot').contents().unwrap();
		$(bcForm).find('tr').contents().unwrap();

		$(bcForm).find('td').each(function() {
			$(this).wrap('<fieldset/>');
			$(this).children().filter(':first').unwrap();
		});

		return bcForm;
	},

	removeTitleField(bcForm) {
		$(bcForm).find('label[for="Title"]').parent().remove();

		return bcForm;
	},

	removeBreaks(bcForm) {
		$(bcForm).find('br').remove();

		return bcForm;
	},

	formatBootstrap(bcForm) {

		//convert default BC classes to bootstrap classes
		$(bcForm).find('fieldset').addClass('form-group');
		$(bcForm).find('.cat_textbox').addClass('form-control').removeClass('cat_textbox');
		$(bcForm).find('.cat_dropdown').addClass('form-control').removeClass('cat_dropdown');
		$(bcForm).find('.cat_dropdown_smaller').addClass('form-control').removeClass('cat_dropdown_smaller');
		$(bcForm).find('.cat_listbox').addClass('form-control').removeClass('cat_listbox');
		$(bcForm).find('.cat_button').addClass('btn btn-default').removeClass('cat_button');

		return bcForm;
	},

	labelRequired(bcForm) {
		//remove top "required" text
		$(bcForm).find('form').contents().filter(function() {
			return this.nodeType === 3;
		}).remove();

		//reformat "required" labels
		$(bcForm).find('span.req').parents('fieldset').addClass('required');
		$(bcForm).find('fieldset.required label').append('Required');
		$(bcForm).find('span.req').remove();

		return bcForm;
	},

	modernizeInputs(bcForm) {
		//email address fields
		$(bcForm).find('input[name="EmailAddress"]').attr('type', 'email');

		//phone number fields
		$(bcForm).find('input[name="PhoneNumber"]').attr('type', 'tel');
		$(bcForm).find('input[name="HomePhone"]').attr('type', 'tel');
		$(bcForm).find('input[name="WorkPhone"]').attr('type', 'tel');
		$(bcForm).find('input[name="CellPhone"]').attr('type', 'tel');
		$(bcForm).find('input[name="HomeFax"]').attr('type', 'tel');
		$(bcForm).find('input[name="WorkFax"]').attr('type', 'tel');

		//number fields
		$(bcForm).find('input[name="HomeZip"]').attr('type', 'number');
		$(bcForm).find('input[name="WorkZip"]').attr('type', 'number');
		$(bcForm).find('input[name="BillingZip"]').attr('type', 'number');
		$(bcForm).find('input[name="ShippingZip"]').attr('type', 'number');
		$(bcForm).find('input[name="CardNumber"]').attr('type', 'number');
		$(bcForm).find('input[name="CardCCV"]').attr('type', 'number');
		$(bcForm).find('input[name="Amount"]').attr('type', 'number');

		return bcForm;
	},

	addPlaceholderAttributes(bcForm) {
		//email address fields
		$(bcForm).find('input[type="email"]').attr('placeholder', 'janedoe@gmail.com');

		//phone number fields
		$(bcForm).find('input[type="tel"]').attr('placeholder', '(234) 555-6789');

		return bcForm;
	},

	modernizeCaptchas(bcForm) {
		//remove top "required" note
		bcForm = bcForm.replace('{module_captcha}', '{module_recaptcha version="2" size="compact"}');
		bcForm = bcForm.replace('{module_captchav2}', '{module_recaptcha version="2" size="compact"}');
		bcForm = bcForm.replace('{module_recaptcha}', '{module_recaptcha version="2" size="compact"}');
		bcForm = bcForm.replace('{module_recaptcha version="2"}', '{module_recaptcha version="2" size="compact"}');

		return bcForm;
	}

};
<|endoftext|>


<commit_before>var NULL = require('./helper/null').NULL
var BehaviorContainer = require('./behavior-container').BehaviorContainer

function BehaviorSystem () {
  this._init()
}

BehaviorSystem.prototype = {

  _init: function () {
    this._children = []
  },

  enable: function (gameObject) {
    if (gameObject.behaviors === NULL) {
      var container = new BehaviorContainer(gameObject, this)
      container.id = this._children.push(container) - 1
      gameObject.behaviors = container
    }
    return gameObject
  },

  disable: function (gameObject) {
    var index = (gameObject.behaviors !== NULL) && (gameObject.behaviors.id >= 0)
    if (index) {
      gameObject.behaviors = NULL
      this._children[index] = NULL
    }
    return gameObject
  },

  processAll: function (methodName) {
    var i = 0
    var len = this._children.length

    for (; i < len; i++) {
      var child = this._children[i]
      if (child !== NULL) {
        child.process(methodName)
      }
    }
  }
}

module.exports = {
  BehaviorSystem: BehaviorSystem
}
<commit_msg>fix .disable<commit_after>var NULL = require('./helper/null').NULL
var BehaviorContainer = require('./behavior-container').BehaviorContainer

function BehaviorSystem () {
  this._init()
}

BehaviorSystem.prototype = {

  _init: function () {
    this._children = []
  },

  enable: function (gameObject) {
    if (gameObject.behaviors === NULL) {
      var container = new BehaviorContainer(gameObject, this)
      container.id = this._children.push(container) - 1
      gameObject.behaviors = container
    }
    return gameObject
  },

  disable: function (gameObject) {
    if (gameObject.behaviors !== NULL) {
      var index = gameObject.behaviors.id
      gameObject.behaviors = NULL
      this._children[index] = NULL
    }
    return gameObject
  },

  processAll: function (methodName) {
    var i = 0
    var len = this._children.length

    for (; i < len; i++) {
      var child = this._children[i]
      if (child !== NULL) {
        child.process(methodName)
      }
    }
  }
}

module.exports = {
  BehaviorSystem: BehaviorSystem
}
<|endoftext|>


<commit_before>/*  Mod.js - v0.1.0
 *  Copyright 2012, Aron Carroll
 *  Released under the MIT license
 *  More Information: http://github.com/aron/mod.js
 */
(function (context) {
  var _module = context.module;

  /* Public: Returns the object under the provided namespace, if it does
   * not exist then creates one. The value of the object cab also be set
   * via the second argument. This will take a basic value or a factory
   * function that should then return the actual value.
   *
   * keypath - The keypath for the module to return.
   * value   - The value to set the module to.
   *
   * Examples
   *
   *   // Get a module.
   *   module('app.views.list'); //=> {}
   *
   *   // Create a module.
   *   module('app.config', {setting: 'a'});
   *   module('app.config'); //=> {setting: 'a'}
   *
   *   // Create a module with a function. Extra arguments are passed into
   *   // the factory function.
   *   module('app.views.list, function ($, window, undefined) {
   *     return function ListView() {};
   *   }, jQuery, window);
   *
   * Returns the module if called with a single argument otherwise returns
   * the module() function.
   */
  function module(keypath, properties) {
    var object = module.modules,
        keys = (keypath || '').split(module.options.delimiter),
        key, previous;

    while (object && keys.length && keys[0]) {
      key = keys.shift();

      previous = object;
      if (object.hasOwnProperty(key)) {
        object = object[key];

        if (keys.length === 0 && object !== undefined) {
          break;
        }
      } else {
        object = object[key] = {};
      }
    }

    if (properties) {
      if (typeof properties === 'function') {
        properties = module.run(properties, [].slice.call(arguments, 2));
      }

      if (module.isObject(properties)) {
        module.extend(object, properties);
      } else {
        previous[key] = properties;
      }
      return module;
    }

    return object;
  }

  /* Helper function that calls the factory function with the correct context
   * and arguments and returns the result.
   *
   * fn   - The function to be called.
   * args - The intial arguments to call the function with.
   *
   * Returns the result of the function call.
   */
  module.run = function (fn, args) {
    args = args.concat(module.options.arguments);
    return fn.apply(module.options.context, args);
  };

  /* Checks to see whether the object passed is an object literal.
   *
   * object - The object to test.
   *
   * Examples
   *
   *   module.isObject({});     // true
   *   module.isObject([]);     // false
   *   module.isObject(window); // false
   *
   * Returns true if the object is a simple object.
   */
  module.isObject = function (object) {
    return typeof object === 'object'
        && object !== null
        && object.constructor === Object;
  };

  /* Simple extend function that copies the properties of object onto target.
   *
   * target - The receiver object.
   * object - The object of key/value pairs.
   *
   * Returns the target object.
   */
  module.extend = function (target, object) {
    var key;
    for (key in object) {
      if (object.hasOwnProperty(key)) {
        target[key] = object[key];
      }
    }
    return target;
  };

  /* Public: Override the default options by passing in an object.
   *
   * options - An object literal of options.
   *           modules:   The object to extend with new modules.
   *           context:   The context for the factory function (default: []).
   *           arguments: Default arguments for the factory function (default: null).
   *           delimiter: The keypath delimiter (default: ".")
   *
   * Examples
   *
   *   module.setup({
   *     context: window,
   *     arguments: [jQuery, _],
   *     delimiter: '/'
   *   });
   *
   *   module('views/list', function (jQuery, _) {
   *     return new View();
   *   });
   *
   * Returns the module() function.
   */
  module.setup = function (options) {
    options = options || {};
    if (options.modules) {
      module.modules = options.modules;
    }

    delete options.modules;
    module.extend(module.options, options);
    return module;
  };

  /* Public: Restores the previous value of the module() property in the
   * global scope. This method only applies to the browser.
   *
   * options - An optional options object to save calling .setup().
   *
   * Examples
   *
   *   // Create your application namespace.
   *   var MyApp = {};
   *
   *   // Assign module to the namespace.
   *   MyApp.module = module.noConflict();
   *
   *   // Assign module to the namespace and use MyApp as the module store.
   *   MyApp.module = module.noConflict({modules: MyApp});
   *
   *   MyApp.module('router', new Router());
   *   MyApp.module('router') === MyApp.router; // true
   *
   * Returns the module() function.
   */
  module.noConflict = function (options) {
    context.module = _module;
    return module.setup(options);
  };

  /* Default object that holds the registered modules. This can be
   * overridden using the setup method.
   */
  module.modules  = {};

  /* Default options to be used by the module function. */
  module.defaults = {
    context: null,
    arguments: [],
    delimiter: '.'
  };

  /* Holds the options used by the module function. */
  module.options = module.extend({}, module.defaults);

  if (typeof context.exports !== 'undefined') {
    context.exports = module;
  } else {
    context.module = module;
  }

})(typeof module !== 'undefined' ? module : this);
<commit_msg>Rename module() arguments for clarity<commit_after>/*  Mod.js - v0.1.0
 *  Copyright 2012, Aron Carroll
 *  Released under the MIT license
 *  More Information: http://github.com/aron/mod.js
 */
(function (context) {
  var _module = context.module;

  /* Public: Returns the object under the provided namespace, if it does
   * not exist then creates one.
   *
   * The value of the object can also be set by passing the value argument.
   * This can take a basic value or a factory function that should then return
   * the actual value to be used.
   *
   * Additional arguments passed into the function will be provided as
   * arguments to the factory function. This can be used in the same way as
   * you would a wrapping a script in a closure e.g. passing in jQuery to
   * create the $ shorthand.
   *
   * keypath - The keypath for the module to return.
   * value   - The value to set the module to.
   *
   * Examples
   *
   *   // Get a module.
   *   module('app.views.list'); //=> {}
   *
   *   // Create a module.
   *   module('app.config', {setting: 'a'});
   *   module('app.config'); //=> {setting: 'a'}
   *
   *   // Create a module with a function. Extra arguments are passed into
   *   // the factory function.
   *   module('app.views.list, function ($, window, undefined) {
   *     return function ListView() {};
   *   }, jQuery, window);
   *
   * Returns the module if called with a single argument otherwise returns
   * the module() function.
   */
  function module(keypath, value) {
    var object = module.modules,
        keys = (keypath || '').split(module.options.delimiter),
        key, previous;

    while (object && keys.length && keys[0]) {
      key = keys.shift();

      previous = object;
      if (object.hasOwnProperty(key)) {
        object = object[key];

        if (keys.length === 0 && object !== undefined) {
          break;
        }
      } else {
        object = object[key] = {};
      }
    }

    if (value) {
      if (typeof value === 'function') {
        value = module.run(value, [].slice.call(arguments, 2));
      }

      if (module.isObject(value)) {
        module.extend(object, value);
      } else {
        previous[key] = value;
      }
      return module;
    }

    return object;
  }

  /* Helper function that calls the factory function with the correct context
   * and arguments and returns the result.
   *
   * fn   - The function to be called.
   * args - The intial arguments to call the function with.
   *
   * Returns the result of the function call.
   */
  module.run = function (fn, args) {
    args = args.concat(module.options.arguments);
    return fn.apply(module.options.context, args);
  };

  /* Checks to see whether the object passed is an object literal.
   *
   * object - The object to test.
   *
   * Examples
   *
   *   module.isObject({});     // true
   *   module.isObject([]);     // false
   *   module.isObject(window); // false
   *
   * Returns true if the object is a simple object.
   */
  module.isObject = function (object) {
    return typeof object === 'object'
        && object !== null
        && object.constructor === Object;
  };

  /* Simple extend function that copies the properties of object onto target.
   *
   * target - The receiver object.
   * object - The object of key/value pairs.
   *
   * Returns the target object.
   */
  module.extend = function (target, object) {
    var key;
    for (key in object) {
      if (object.hasOwnProperty(key)) {
        target[key] = object[key];
      }
    }
    return target;
  };

  /* Public: Override the default options by passing in an object.
   *
   * options - An object literal of options.
   *           modules:   The object to extend with new modules.
   *           context:   The context for the factory function (default: []).
   *           arguments: Default arguments for the factory function (default: null).
   *           delimiter: The keypath delimiter (default: ".")
   *
   * Examples
   *
   *   module.setup({
   *     context: window,
   *     arguments: [jQuery, _],
   *     delimiter: '/'
   *   });
   *
   *   module('views/list', function (jQuery, _) {
   *     return new View();
   *   });
   *
   * Returns the module() function.
   */
  module.setup = function (options) {
    options = options || {};
    if (options.modules) {
      module.modules = options.modules;
    }

    delete options.modules;
    module.extend(module.options, options);
    return module;
  };

  /* Public: Restores the previous value of the module() property in the
   * global scope. This method only applies to the browser.
   *
   * options - An optional options object to save calling .setup().
   *
   * Examples
   *
   *   // Create your application namespace.
   *   var MyApp = {};
   *
   *   // Assign module to the namespace.
   *   MyApp.module = module.noConflict();
   *
   *   // Assign module to the namespace and use MyApp as the module store.
   *   MyApp.module = module.noConflict({modules: MyApp});
   *
   *   MyApp.module('router', new Router());
   *   MyApp.module('router') === MyApp.router; // true
   *
   * Returns the module() function.
   */
  module.noConflict = function (options) {
    context.module = _module;
    return module.setup(options);
  };

  /* Default object that holds the registered modules. This can be
   * overridden using the setup method.
   */
  module.modules  = {};

  /* Default options to be used by the module function. */
  module.defaults = {
    context: null,
    arguments: [],
    delimiter: '.'
  };

  /* Holds the options used by the module function. */
  module.options = module.extend({}, module.defaults);

  if (typeof context.exports !== 'undefined') {
    context.exports = module;
  } else {
    context.module = module;
  }

})(typeof module !== 'undefined' ? module : this);
<|endoftext|>


<commit_before>/**
 * Sync the data from rest point
 * @method sync
 * @param  {Function} callback Callback(error, result)
 */
CacheSync.prototype.load = Meteor.wrapAsync(function(callback) {
  var self = this;

  // Check if caching is paused
  if (CacheSync.isPaused) {
    CacheSync.state.emit('waiting', {
      name: self.name,
      type: 'paused',
      message: 'load waiting, CacheSync is paused'
    });
    return callback();
  }

  if (typeof self.paginatedUrl !== 'function') {
    CacheSync.state.emit('error', {
      name: self.name,
      type: 'load not supported',
      message: 'load requires paginatedUrl function'
    });
    callback(new Error('CacheSync: "' + self.name + '" is missing paginatedUrl setting'));
    return;
  }

  var status;

  try {
    // Lookup last status
    status = CacheSync.getStatus(self.name);
  } catch(error) {
    CacheSync.state.emit('error', {
      name: self.name,
      type: 'get status',
      message: error && error.message || error
    });
    callback(new Error('CacheSync: "' + self.name + '" could not get status'));
    return;
  }

  CacheSync.state.emit('loadCalled', { name: self.name });

  // Use the correct url - we either fetch pages or load latest updates
  var url = self.paginatedUrl(status.page * self.limit, self.limit);

  CacheSync.state.emit('loading', { name: self.name, page: status.page });

  var modifier = {
    $set: {
      loadAt: new Date(),
      page: status.page + 1,
      updatedAt: status.updatedAt
    },
    $inc: {
      'count.inserted': 0,
      'count.updated': 0,
      'count.removed': 0,
      'count.issues': 0
    }
  };

  // If the collection is initialized and loading page 0 we add an "__old_cache_document" flag to each of the
  // documents in the collection. When the load has completed we strip all documents still carrying
  // the "__old_cache_document" flag in order to sync removal of documents.
  if (self.autoRemove && status.initialized && status.page === 0) {
    try {
      self.collection.update({}, { $inc: { '__old_cache_document': 1 } }, { multi: true});
    } catch(error) {
      CacheSync.state.emit('error', {
        name: self.name,
        type: 'old cache flag',
        message: error && error.message || error
      });
    }
  }

  // Call rest point
  self.httpCall(url, function(err, list) {
    if (!err) {

      list.forEach(function(doc) {

        // Get updated at
        self.calcLastUpdated(doc);

        // Set the updatedAt
        modifier.$set.updatedAt = self.lastUpdatedAt;

        var cacheStatus = {};

        try {
          cacheStatus = self.cacheDocument(doc);
        } catch(error) {
          cacheStatus.error = error;

          CacheSync.state.emit('error', {
            name: self.name,
            type: 'cache document',
            message: error && error.message || error
          });
        }

        if (cacheStatus.error) {

          modifier.$inc['count.issues']++;

        }

        if (cacheStatus.inserted) {

          CacheSync.state.emit('inserted', { name: self.name, id: cacheStatus.transformedDocument._id });
          modifier.$inc['count.inserted']++;

        }

        if (cacheStatus.updated) {

          CacheSync.state.emit('updated', { name: self.name, id: cacheStatus.transformedDocument._id });
          modifier.$inc['count.updated']++;

        }

      });

      // If page loading and no more documents, then stop page loading
      if (list.length < self.limit) {
        // Set page to 0 for the the next run
        modifier.$set.page = 0;

        try {

          // Remove all documents not synchronized, these are removed from the collection
          self.collection.find({ '__old_cache_document': { $gte: self.removalLimit } }).forEach(function(doc) {
            // Document to remove

            // Emit event if any documents were removed
            if (self.collection.remove({ _id: doc._id })) {
              CacheSync.state.emit('removed', { name: self.name, id: doc._id });
              modifier.$inc['count.removed']++;
            }
          });

        } catch(error) {
          CacheSync.state.emit('error', {
            name: self.name,
            type: 'old cache remove',
            message: error && error.message || error
          });
        }

        // Emit end event
        CacheSync.state.emit(self.name + '.loaded', { name: self.name, page: status.page });
        CacheSync.state.emit('loaded', { name: self.name, page: status.page });

        modifier.$set.loadedAt = new Date();

        if (!status.initialized) {
          modifier.$set.initialized = true;

          CacheSync.state.emitState(self.name + '.initialized', { name: self.name, page: status.page });
          CacheSync.state.emitState('initialized', { name: self.name, page: status.page });

        }

      } else {
        CacheSync.state.emit(self.name + '.load', { name: self.name, page: status.page });
        CacheSync.state.emit('load', { name: self.name, page: status.page });
      }

      // Update the load status
      // Load updates
      // $set
      // * loadAt
      // * updatedAt
      // $inc
      // * count.inserted
      // * count.updated
      // * count.removed
      // * count.issues
      // load done
      CacheSync.status.update({ _id: self.name }, modifier, callback);

    } else {
      // Error callback
      callback(err);
    }
  });

});
<commit_msg>Add auto remove check<commit_after>/**
 * Sync the data from rest point
 * @method sync
 * @param  {Function} callback Callback(error, result)
 */
CacheSync.prototype.load = Meteor.wrapAsync(function(callback) {
  var self = this;

  // Check if caching is paused
  if (CacheSync.isPaused) {
    CacheSync.state.emit('waiting', {
      name: self.name,
      type: 'paused',
      message: 'load waiting, CacheSync is paused'
    });
    return callback();
  }

  if (typeof self.paginatedUrl !== 'function') {
    CacheSync.state.emit('error', {
      name: self.name,
      type: 'load not supported',
      message: 'load requires paginatedUrl function'
    });
    callback(new Error('CacheSync: "' + self.name + '" is missing paginatedUrl setting'));
    return;
  }

  var status;

  try {
    // Lookup last status
    status = CacheSync.getStatus(self.name);
  } catch(error) {
    CacheSync.state.emit('error', {
      name: self.name,
      type: 'get status',
      message: error && error.message || error
    });
    callback(new Error('CacheSync: "' + self.name + '" could not get status'));
    return;
  }

  CacheSync.state.emit('loadCalled', { name: self.name });

  // Use the correct url - we either fetch pages or load latest updates
  var url = self.paginatedUrl(status.page * self.limit, self.limit);

  CacheSync.state.emit('loading', { name: self.name, page: status.page });

  var modifier = {
    $set: {
      loadAt: new Date(),
      page: status.page + 1,
      updatedAt: status.updatedAt
    },
    $inc: {
      'count.inserted': 0,
      'count.updated': 0,
      'count.removed': 0,
      'count.issues': 0
    }
  };

  // If the collection is initialized and loading page 0 we add an "__old_cache_document" flag to each of the
  // documents in the collection. When the load has completed we strip all documents still carrying
  // the "__old_cache_document" flag in order to sync removal of documents.
  if (self.autoRemove && status.initialized && status.page === 0) {
    try {
      self.collection.update({}, { $inc: { '__old_cache_document': 1 } }, { multi: true});
    } catch(error) {
      CacheSync.state.emit('error', {
        name: self.name,
        type: 'old cache flag',
        message: error && error.message || error
      });
    }
  }

  // Call rest point
  self.httpCall(url, function(err, list) {
    if (!err) {

      list.forEach(function(doc) {

        // Get updated at
        self.calcLastUpdated(doc);

        // Set the updatedAt
        modifier.$set.updatedAt = self.lastUpdatedAt;

        var cacheStatus = {};

        try {
          cacheStatus = self.cacheDocument(doc);
        } catch(error) {
          cacheStatus.error = error;

          CacheSync.state.emit('error', {
            name: self.name,
            type: 'cache document',
            message: error && error.message || error
          });
        }

        if (cacheStatus.error) {

          modifier.$inc['count.issues']++;

        }

        if (cacheStatus.inserted) {

          CacheSync.state.emit('inserted', { name: self.name, id: cacheStatus.transformedDocument._id });
          modifier.$inc['count.inserted']++;

        }

        if (cacheStatus.updated) {

          CacheSync.state.emit('updated', { name: self.name, id: cacheStatus.transformedDocument._id });
          modifier.$inc['count.updated']++;

        }

      });

      // If page loading and no more documents, then stop page loading
      if (list.length < self.limit) {
        // Set page to 0 for the the next run
        modifier.$set.page = 0;

        if (self.autoRemove) {

          try {

            // Remove all documents not synchronized, these are removed from the collection
            self.collection.find({ '__old_cache_document': { $gte: self.removalLimit } }).forEach(function(doc) {
              // Document to remove

              // Emit event if any documents were removed
              if (self.collection.remove({ _id: doc._id })) {
                CacheSync.state.emit('removed', { name: self.name, id: doc._id });
                modifier.$inc['count.removed']++;
              }
            });

          } catch(error) {
            CacheSync.state.emit('error', {
              name: self.name,
              type: 'old cache remove',
              message: error && error.message || error
            });
          }

        }

        // Emit end event
        CacheSync.state.emit(self.name + '.loaded', { name: self.name, page: status.page });
        CacheSync.state.emit('loaded', { name: self.name, page: status.page });

        modifier.$set.loadedAt = new Date();

        if (!status.initialized) {
          modifier.$set.initialized = true;

          CacheSync.state.emitState(self.name + '.initialized', { name: self.name, page: status.page });
          CacheSync.state.emitState('initialized', { name: self.name, page: status.page });

        }

      } else {
        CacheSync.state.emit(self.name + '.load', { name: self.name, page: status.page });
        CacheSync.state.emit('load', { name: self.name, page: status.page });
      }

      // Update the load status
      // Load updates
      // $set
      // * loadAt
      // * updatedAt
      // $inc
      // * count.inserted
      // * count.updated
      // * count.removed
      // * count.issues
      // load done
      CacheSync.status.update({ _id: self.name }, modifier, callback);

    } else {
      // Error callback
      callback(err);
    }
  });

});
<|endoftext|>


<commit_before>steal('can/util','can/view', 'can/util/string', 'can/observe/compute').then(function( can ) {

	// ## ejs.js
	// `can.EJS`
	// _Embedded JavaScript Templates._

	// Helper methods.
	var myEval = function( script ) {
		eval(script);
	},
		extend = can.extend,
		// Regular expressions for caching.
		quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
		attrReg = /([^\s]+)=$/,
		newLine = /(\r|\n)+/g,
		attributeReplace = /__!!__/g,
		tagMap = {
			"": "span",
			table: "tr",
			tr: "td",
			ol: "li",
			ul: "li",
			tbody: "tr",
			thead: "tr",
			tfoot: "tr",
			select: "option",
			optgroup: "option"
		},
		tagToContentPropMap= {
			option: "textContent",
			textarea: "value"
		},
		// Escapes characters starting with `\`.
		clean = function( content ) {
			return content
				.split('\\').join("\\\\")
				.split("\n").join("\\n")
				.split('"').join('\\"')
				.split("\t").join("\\t");
		},
		bracketNum = function(content){
			return (--content.split("{").length) - (--content.split("}").length);
		},
		// Cross-browser attribute methods.
		// These should be mapped to the underlying library.
		attrMap = {
			"class" : "className",
			"value": "value",
			"textContent" : "textContent"
		},
		bool = can.each(["checked","disabled","readonly","required"], function(n){
			attrMap[n] = n;
		}),
		setAttr = function(el, attrName, val){
			// if this is a special property
			attrMap[attrName] ?
				// set the value as true / false
				(el[attrMap[attrName]] = can.inArray(attrName,bool) > -1? true  : val):
				el.setAttribute(attrName, val);
		},
		getAttr = function(el, attrName){
			return attrMap[attrName]?
				el[attrMap[attrName]]:
				el.getAttribute(attrName);
		},
		removeAttr = function(el, attrName){
			if(can.inArray(attrName,bool) > -1){
				el[attrName] = false;
			} else{
				el.removeAttribute(attrName)
			}
		},
		// a helper to get the parentNode for a given element el
		// if el is in a documentFragment, it will return defaultParentNode
		getParentNode = function(el, defaultParentNode){
			return defaultParentNode && el.parentNode.nodeType === 11 ? defaultParentNode : el.parentNode;
		},
		// helper to know if property is not an expando on oldObserved's list of observes
		// this should probably be removed and oldObserved should just have a
		// property with observes
		observeProp = function(name){
			return name.indexOf("|") >= 0;
		},
		// a mapping of element ids to nodeList ids
		nodeMap = {},
		// a mapping of nodeList ids to nodeList
		nodeListMap = {},
		expando = "ejs_"+Math.random(),
		_id=0,
		id = function(node){
			if( node[expando]){
				return node[expando]
			} else {
				return node[expando] = (node.nodeName ? "element_" : "obj_")+(++_id);
			}
		},
		//
		register= function(nodeList){
			var nLId = id(nodeList)
			nodeListMap[nLId] = nodeList;

			can.each(nodeList, function(node){
				addNodeListId(node, nLId)
			})
		},
		addNodeListId = function(node, nodeListId){
			var nodeListIds = nodeMap[id(node)];
				if(!nodeListIds){
					nodeListIds = nodeMap[id(node)] = []
				}
				nodeListIds.push(nodeListId)
		},
		unregister= function(nodeList){
			var nLId = id(nodeList);
			can.each(nodeList, function(node){
				removeNodeListId(node, nLId);
			});
			delete nodeListMap[nLId];
		},
		// removes a nodeListId from a node's nodeListIds
		removeNodeListId= function(node, nodeListId){
			var nodeListIds = nodeMap[id(node)],
				index = can.inArray(nodeListId, nodeListIds);
			index >=0 && nodeListIds.splice( index ,  1 )
			if(!nodeListIds.length){
				delete nodeMap[id(node)];
			}
		},
		replace= function(oldNodeList, newNodes){
			// for each node in the node list
			oldNodeList = can.makeArray(oldNodeList);
			can.each(oldNodeList, function(node){
				// for each nodeList the node is in
				can.each( can.makeArray( nodeMap[id(node)] ), function( nodeListId ){
					var nodeList = nodeListMap[nodeListId];
					var startIndex = can.inArray( node, nodeList );

					// remove this nodeListId from each node
					if(startIndex >=0){
						//
						for( var i = startIndex; i < startIndex + oldNodeList.length; i++){
							var n = nodeList[i];
							removeNodeListId(n, nodeListId)
						}
					}
					// swap in new nodes into the nodeLIst
					nodeList.splice.apply(nodeList, [startIndex,oldNodeList.length   ].concat(newNodes))
					// tell these new nodes they belong to the nodeList
					can.each(newNodes, function(node){
						addNodeListId(node, nodeListId)
					})
				});

			})
		},
		// Returns escaped/sanatized content for anything other than a live-binding
		contentEscape = function( txt ) {
			return (typeof txt == 'string' || typeof txt == 'number') ?
				can.esc( txt ) :
				contentText(txt);
		},
		// Returns text content for anything other than a live-binding
		contentText =  function( input ) {

			// If it's a string, return.
			if ( typeof input == 'string' ) {
				return input;
			}
			// If has no value, return an empty string.
			if ( !input && input != 0 ) {
				return '';
			}

			// If it's an object, and it has a hookup method.
			var hook = (input.hookup &&

			// Make a function call the hookup method.
			function( el, id ) {
				input.hookup.call(input, el, id);
			}) ||

			// Or if it's a `function`, just use the input.
			(typeof input == 'function' && input);

			// Finally, if there is a `function` to hookup on some dom,
			// add it to pending hookups.
			if ( hook ) {
				pendingHookups.push(hook);
				return '';
			}

			// Finally, if all else is `false`, `toString()` it.
			return "" + input;
		},
		// The EJS constructor function
		EJS = function( options ) {
			// Supports calling EJS without the constructor
			// This returns a function that renders the template.
			if ( this.constructor != EJS ) {
				var ejs = new EJS(options);
				return function( data, helpers ) {
					return ejs.render(data, helpers);
				};
			}
			// If we get a `function` directly, it probably is coming from
			// a `steal`-packaged view.
			if ( typeof options == "function" ) {
				this.template = {
					fn: options
				};
				return;
			}
			// Set options on self.
			extend(this, options);
			this.template = scan(this.text, this.name);
		};

	can.EJS = EJS;
	/**
	 * @Prototype
	 */
	EJS.prototype.
	/**
	 * Renders an object with view helpers attached to the view.
	 *
	 *     new EJS({text: "<%= message %>"}).render({
	 *       message: "foo"
	 *     },{helper: function(){ ... }})
	 *
	 * @param {Object} object data to be rendered
	 * @param {Object} [extraHelpers] an object with view helpers
	 * @return {String} returns the result of the string
	 */
	render = function( object, extraHelpers ) {
		object = object || {};
		return this.template.fn.call(object, object, new EJS.Helpers(object, extraHelpers || {}));
	};
	/**
	 * @Static
	 */
	extend(EJS, {
		// Called to return the content within a magic tag like `<%= %>`.
		// - escape - if the content returned should be escaped
		// - tagName - the tag name the magic tag is within or the one that proceeds the magic tag
		// - status - where the tag is in.  The status can be:
		//    - _STRING_ - The name of the attribute the magic tag is within
		//    - `1` - The magic tag is within a tag like `<div <%= %>>`
		//    - `0` - The magic tag is outside (or between) tags like `<div><%= %></div>`
		// - self - the `this` the template was called with
		// - func - the "wrapping" function.  For example:  `<%= task.attr('name') %>` becomes
		//   `(function(){return task.attr('name')})
		/**
		 * @hide
		 * called to setup unescaped text
		 * @param {Number|String} status
		 *   - "string" - the name of the attribute

		 *   - 1 - in an html tag

		 *   - 0 - in the content of a tag
HERE

		 *
		 * @param {Object} self
		 * @param {Object} func
		 */
		txt: function(escape, tagName, status, self, func){
			// call the "wrapping" function and get the binding information
			var binding = can.compute.binder(func, self, function(newVal, oldVal){
				// call the update method we will define for each
				// type of attribute
				update(newVal, oldVal)
			});

			// If we had no observes just return the value returned by func.
			if(!binding.isListening){
				return (escape || status !== 0? contentEscape : contentText)(binding.value);
			}
			// The following are helper methods or varaibles that will
			// be defined by one of the various live-updating schemes.

			// The parent element we are listening to for teardown
			var	parentElement,
				nodeList,
				teardown = function(){
					nodeList && unregister(nodeList);
					binding.teardown();
				},
				// if the parent element is removed, teardown the binding
				setupTeardownOnDestroy = function(el, nl){
					nodeList = nl;
					can.bind.call(el,'destroyed', teardown)
					parentElement = el;
				},
				// if there is no parent, undo bindings
				teardownCheck = function(parent){
					if(!parent){
						teardown();
						can.unbind.call(parentElement,'destroyed', teardown)
					}
				},
				// the tag type to insert
				tag = (tagMap[tagName] || "span"),
				// this will be filled in if binding.isListening
				update,
				// the property (instead of innerHTML elements) to adjust. For
				// example options should use textContent
				contentProp = tagToContentPropMap[tagName];

			// The magic tag is outside or between tags.
			if( status == 0 && !contentProp ) {
				// Return an element tag with a hookup in place of the content
				return "<" +tag+can.view.hook(
				escape ?
					// If we are escaping, replace the parentNode with
					// a text node who's value is `func`'s return value.
					function(el, parentNode){
						// updates the text of the text node
						update = function(newVal){
							node.nodeValue = ""+newVal;
							teardownCheck(node.parentNode);
						};

						var parent = getParentNode(el, parentNode),
							node = document.createTextNode(binding.value);

						parent.insertBefore(node, el);
						parent.removeChild(el);
						setupTeardownOnDestroy(parent);
					}
					:
					// If we are not escaping, replace the parentNode with a
					// documentFragment created as with `func`'s return value.
					function( span, parentNode ) {
						// updates the elements with the new content
						update = function(newVal){
							// is this still part of the DOM?
							var attached = nodes[0].parentNode;
							// update the nodes in the DOM with the new rendered value
							if( attached ) {
								makeAndPut(newVal);
							} else {
								console.log("NO longer attached!")
							}
							teardownCheck(nodes[0].parentNode)
						};

						// make sure we have a valid parentNode
						parentNode = getParentNode(span, parentNode)
						// A helper function to manage inserting the contents
						// and removing the old contents
						var nodes = [span],
							makeAndPut = function(val){
								// create the fragment, but don't hook it up
								// we need to insert it into the document first
								//console.log("updating with ",val, nodes)
								var frag = can.view.fragment(val, parentNode),
									newNodes = can.makeArray(frag.childNodes),
									oldNodes = can.makeArray(nodes);
								console.log("replacing",val,nodes, newNodes)
								replace(nodes, newNodes)
								frag = can.view.hookup(frag);

								//can.view.frag(val, parentNode),
									// keep a reference to each node

								var	last = oldNodes[oldNodes.length - 1];

								// Insert it in the `document` or `documentFragment`
								if( last.nextSibling ){
									last.parentNode.insertBefore(frag, last.nextSibling)
								} else {
									last.parentNode.appendChild(frag)
								}
								// nodes hasn't been set yet
								console.log("removing", oldNodes)
								can.remove( can.$(oldNodes) );
								/*if( !nodes ) {
									can.remove( can.$(span) );
									nodes = newNodes;
									console.log("register",val)
									register(nodes);
								} else {
									can.remove( can.$(nodes) );
									console.log("replace",nodes, newNodes, val)
									replace(nodes,newNodes);
								}*/
							};
							// nodes are the nodes that any updates will replace
							// at this point, these nodes could be part of a documentFragment
						register(nodes)
						makeAndPut(binding.value);

						setupTeardownOnDestroy( parentNode, nodes );

				}) + "></" +tag+">";
			// In a tag, but not in an attribute
			} else if( status === 1 ) {
				// remember the old attr name
				var attrName = binding.value.replace(/['"]/g, '').split('=')[0];
				pendingHookups.push(function(el) {
					update = function(newVal){
						var parts = (newVal|| "").replace(/['"]/g, '').split('='),
							newAttrName = parts[0];

						// Remove if we have a change and used to have an `attrName`.
						if((newAttrName != attrName) && attrName){
							removeAttr(el,attrName)
						}
						// Set if we have a new `attrName`.
						if(newAttrName){
							setAttr(el, newAttrName, parts[1]);
							attrName = newAttrName;
						}
					}
					setupTeardownOnDestroy(el);
				});

				return binding.value;
			} else { // In an attribute...
				var attributeName = status == 0 ? contentProp : status;
				// if the magic tag is inside the element, like `<option><% TAG %></option>`,
				// we add this hookup to the last element (ex: `option`'s) hookups.
				// Otherwise, the magic tag is in an attribute, just add to the current element's
				// hookups.
				(status === 0  ? lastHookups : pendingHookups ).push(function(el){
					// update will call this attribute's render method
					// and set the attribute accordingly
					update = function(){
						setAttr(el, attributeName, hook.render(), contentProp)
					}

					var wrapped = can.$(el),
						hooks;

					// Get the list of hookups or create one for this element.
					// Hooks is a map of attribute names to hookup `data`s.
					// Each hookup data has:
					// `render` - A `function` to render the value of the attribute.
					// `funcs` - A list of hookup `function`s on that attribute.
					// `batchNum` - The last event `batchNum`, used for performance.
					(hooks = can.data(wrapped,'hooks')) || can.data(wrapped, 'hooks', hooks = {});

					// Get the attribute value.
					var attr = getAttr(el, attributeName, contentProp),
						// Split the attribute value by the template.
						parts = attr.split("__!!__"),
						hook;

					// If we already had a hookup for this attribute...
					if(hooks[attributeName]) {
						// Just add to that attribute's list of `function`s.
						hooks[attributeName].bindings.push(binding);
					}
					else {
						// Create the hookup data.
						hooks[attributeName] = {
							render: function() {
								var i =0,
									newAttr = attr.replace(attributeReplace, function() {
										return contentText( hook.bindings[i++].value );
									});
								return newAttr;
							},
							bindings: [binding],
							batchNum : undefined
						};
					};

					// Save the hook for slightly faster performance.
					hook = hooks[attributeName];

					// Insert the value in parts.
					parts.splice(1,0,binding.value);

					// Set the attribute.
					setAttr(el, attributeName, parts.join(""), contentProp);

					// Bind on change.
					//liveBind(observed, el, binder,oldObserved);
					setupTeardownOnDestroy(el)
				})
				return "__!!__";
			}
		},
		pending: function() {
			// TODO, make this only run for the right tagName
			if(true  || pendingHookups.length) {
				var hooks = pendingHookups.slice(0);
				lastHookups = hooks;
				pendingHookups = [];
				return can.view.hook(function(el){
					can.each(hooks, function(fn){
						fn(el);
					})
				});
			}else {
				return "";
			}
		},
		register: register,
		unregister: unregister,
		replace: replace,
		nodeMap: nodeMap,
		// a mapping of nodeList ids to nodeList
		nodeListMap: nodeListMap
});
	// Start scanning code.
	var tokenReg = new RegExp("(" +[ "<%%", "%%>", "<%==", "<%=",
					"<%#", "<%", "%>", "<", ">", '"', "'"].join("|")+")","g"),
		// Commands for caching.
		startTxt = 'var ___v1ew = [];',
		finishTxt = "return ___v1ew.join('')",
		put_cmd = "___v1ew.push(",
		insert_cmd = put_cmd,
		// Global controls (used by other functions to know where we are).
		//
		// Are we inside a tag?
		htmlTag = null,
		// Are we within a quote within a tag?
		quote = null,
		// What was the text before the current quote? (used to get the `attr` name)
		beforeQuote = null,
		// Used to mark where the element is.
		status = function(){
			// `t` - `1`.
			// `h` - `0`.
			// `q` - String `beforeQuote`.
			return quote ? "'"+beforeQuote.match(attrReg)[1]+"'" : (htmlTag ? 1 : 0)
		},
		pendingHookups = [],
		lastHookups = [],
		scan = function(source, name){
			var tokens = [],
				last = 0;

			source = source.replace(newLine, "\n");
			source.replace(tokenReg, function(whole, part, offset){
				// if the next token starts after the last token ends
				// push what's in between
				if(offset > last){
					tokens.push( source.substring(last, offset) );
				}
				// push the token
				tokens.push(part);
				// update the position of the last part of the last token
				last = offset+part.length;
			})
			// if there's something at the end, add it
			if(last < source.length){
				tokens.push(source.substr(last))
			}

			var content = '',
				buff = [startTxt],
				// Helper `function` for putting stuff in the view concat.
				put = function( content, bonus ) {
					buff.push(put_cmd, '"', clean(content), '"'+(bonus||'')+');');
				},
				// A stack used to keep track of how we should end a bracket
				// `}`.
				// Once we have a `<%= %>` with a `leftBracket`,
				// we store how the file should end here (either `))` or `;`).
				endStack =[],
				// The last token, used to remember which tag we are in.
				lastToken,
				// The corresponding magic tag.
				startTag = null,
				// Was there a magic tag inside an html tag?
				magicInTag = false,
				// The current tag name.
				tagName = '',
				// stack of tagNames
				tagNames = [],
				// Declared here.
				bracketCount,
				i = 0,
				token;

			// Reinitialize the tag state goodness.
			htmlTag = quote = beforeQuote = null;

			for (; (token = tokens[i++]) !== undefined;) {

				if ( startTag === null ) {
					switch ( token ) {
					case '<%':
					case '<%=':
					case '<%==':
						magicInTag = 1;
					case '<%#':
						// A new line -- just add whatever content within a clean.
						// Reset everything.
						startTag = token;
						if ( content.length ) {
							put(content);
						}
						content = '';
						break;

					case '<%%':
						// Replace `<%%` with `<%`.
						content += '<%';
						break;
					case '<':
						// Make sure we are not in a comment.
						if(tokens[i].indexOf("!--") !== 0) {
							htmlTag = 1;
							magicInTag = 0;
						}
						content += token;
						break;
					case '>':
						htmlTag = 0;
						// if there was a magic tag
						// or it's an element that has text content between its tags,
						// but content is not other tags add a hookup
						// TODO: we should only add `can.EJS.pending()` if there's a magic tag
						// within the html tags.
						if(magicInTag || tagToContentPropMap[ tagNames[tagNames.length -1] ]){
							put(content, ",can.EJS.pending(),\">\"");
							content = '';
						} else {
							content += token;
						}
						// if it's a tag like <input/>
						if(lastToken.substr(-1) == "/"){
							// remove the current tag in the stack
							tagNames.pop();
							// set the current tag to the previous parent
							tagName = tagNames[tagNames.length-1];
						}
						break;
					case "'":
					case '"':
						// If we are in an html tag, finding matching quotes.
						if(htmlTag){
							// We have a quote and it matches.
							if(quote && quote === token){
								// We are exiting the quote.
								quote = null;
								// Otherwise we are creating a quote.
								// TODO: does this handle `\`?
							} else if(quote === null){
								quote = token;
								beforeQuote = lastToken;
							}
						}
					default:
						// Track the current tag
						if(lastToken === '<'){
							tagName = token.split(' ')[0];
							// If
							if( tagName.indexOf("/") === 0 && tagNames.pop() === tagName.substr(1) ) {
								tagName = tagNames[tagNames.length-1]|| tagName.substr(1)
							} else {
								tagNames.push(tagName);
							}
						}
						content += token;
						break;
					}
				}
				else {
					// We have a start tag.
					switch ( token ) {
					case '%>':
						// `%>`
						switch ( startTag ) {
						case '<%':
							// `<%`

							// Get the number of `{ minus }`
							bracketCount = bracketNum(content);

							// We are ending a block.
							if (bracketCount == 1) {

								// We are starting on.
								buff.push(insert_cmd, "can.EJS.txt(0,'"+tagName+"'," + status() + ",this,function(){", startTxt, content);

								endStack.push({
									before: "",
									after: finishTxt+"}));\n"
								})
							}
							else {

								// How are we ending this statement?
								var last = // If the stack has value and we are ending a block...
									 endStack.length && bracketCount == -1 ? // Use the last item in the block stack.
									 endStack.pop() : // Or use the default ending.
								{
									after: ";"
								};

								// If we are ending a returning block,
								// add the finish text which returns the result of the
								// block.
								if (last.before) {
									buff.push(last.before)
								}
								// Add the remaining content.
								buff.push(content, ";",last.after);
							}
							break;
						case '<%=':
						case '<%==':
							// We have an extra `{` -> `block`.
							// Get the number of `{ minus }`.
							bracketCount = bracketNum(content);
							// If we have more `{`, it means there is a block.
							if( bracketCount ){
								// When we return to the same # of `{` vs `}` end with a `doubleParent`.
								endStack.push({
									before : finishTxt,
									after: "}));"
								})
							}
							// Check if its a func like `()->`
							if(quickFunc.test(content)){
								var parts = content.match(quickFunc)
								content = "function(__){var "+parts[1]+"=can.$(__);"+parts[2]+"}"
							}

							// If we have `<%== a(function(){ %>` then we want
							// `can.EJS.text(0,this, function(){ return a(function(){ var _v1ew = [];`.
							buff.push(insert_cmd, "can.EJS.txt("+(startTag === '<%=' ? 1 : 0)+",'"+tagName+"'," + status()+",this,function(){ return ", content,
								// If we have a block.
								bracketCount ?
								// Start with startTxt `"var _v1ew = [];"`.
								startTxt :
								// If not, add `doubleParent` to close push and text.
								"}));"
								);
							break;
						}
						startTag = null;
						content = '';
						break;
					case '<%%':
						content += '<%';
						break;
					default:
						content += token;
						break;
					}

				}
				lastToken = token;
			}

			// Put it together...
			if ( content.length ) {
				// Should be `content.dump` in Ruby.
				put(content)
			}
			buff.push(";")

			var template = buff.join(''),
				out = {
					out: 'with(_VIEW) { with (_CONTEXT) {' + template + " "+finishTxt+"}}"
				};
			// Use `eval` instead of creating a function, because it is easier to debug.
			myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
			return out;
		};

	/**
	 * @class can.EJS.Helpers
	 * @parent can.EJS
	 * By adding functions to can.EJS.Helpers.prototype, those functions will be available in the
	 * views.
	 *
	 * The following helper converts a given string to upper case:
	 *
	 * 	can.EJS.Helpers.prototype.toUpper = function(params)
	 * 	{
	 * 		return params.toUpperCase();
	 * 	}
	 *
	 * Use it like this in any EJS template:
	 *
	 * 	<%= toUpper('javascriptmvc') %>
	 *
	 * To access the current DOM element return a function that takes the element as a parameter:
	 *
	 * 	can.EJS.Helpers.prototype.upperHtml = function(params)
	 * 	{
	 * 		return function(el) {
	 * 			$(el).html(params.toUpperCase());
	 * 		}
	 * 	}
	 *
	 * In your EJS view you can then call the helper on an element tag:
	 *
	 * 	<div <%= upperHtml('javascriptmvc') %>>

	 *
	 *
	 * @constructor Creates a view helper.  This function
	 * is called internally.  You should never call it.
	 * @param {Object} data The data passed to the
	 * view.  Helpers have access to it through this._data
	 */
	EJS.Helpers = function( data, extras ) {
		this._data = data;
		this._extras = extras;
		extend(this, extras);
	};
	/**
	 * @prototype
	 */
	EJS.Helpers.prototype = {
		/**
		 * @function list
		 * @hide
		 *
		 * `can.EJS.Helpers.list` iterates over an observable list and
		 * sets up live binding. `list` takes a list of observables and a callback
		 * function with the signature `callback( currentItem, index, itemList )`
		 *
		 * Typically, this will look like:
		 *
		 *     <% list(items, function(item){ %>
		 *
<%= item.attr('name') %>

		 *     <% }) %>
		 *
		 * Whenever the list of observables changes, such as when an item is added or removed,
		 * the EJS view will redraw the list in the DOM.
		 */
		// TODO Deprecated!!
		list : function(list, cb){
			can.each(list, function(item, i){
				cb(item, i, list)
			})
		}
	};

	// Options for `steal`'s build.
	can.view.register({
		suffix: "ejs",
		// returns a `function` that renders the view.
		script: function( id, src ) {
			return "can.EJS(function(_CONTEXT,_VIEW) { " + new EJS({
				text: src,
				name: id
			}).template.out + " })";
		},
		renderer: function( id, text ) {
			return EJS({
				text: text,
				name: id
			});
		}
	});

	return can;
});
<commit_msg>this is old<commit_after><|endoftext|>


<commit_before>
/**
 * this module implements varnish telnet management protocol
 * https://www.varnish-cache.org/trac/wiki/ManagementPort
 */

var net = require('net')

function VarnishClient(host, port, ready_callback) {

    var ready = false;
    var cmd_callback = null;
    var client = null;

    function connect() {
        ready = false;
        client = net.createConnection(port, host);
    }

    connect();

    client.on('data', function(data) {
        data = data.toString();
        lines = data.split('\n', 2);
        if(lines.length == 2) {
            var tk = lines[0].split(' ')
            var code = parseInt(tk[0], 10);
            var body_length = parseInt(tk[1], 10);
            var body = lines[1];
            if(!ready) {
                ready = true;
                ready_callback();
            } else if(cmd_callback) {
                var c = cmd_callback
                cmd_callback = null;
                c(null, code, body);
            }
        }

    });

    // sends the command to the server
    function _send(cmd, callback) {
      cmd_callback = callback;
      client.write(cmd + '\n');
    }

    // run command if there is no peding response
    // fist param of the callback are the error, null
    // if all went ok
    this.run_cmd = function(cmd, callback) {
       if(!cmd_callback) {
         _send(cmd, callback);
       } else {
         callback('response pending');
       }
    }

    // close the connection
    this.close = function() {
       client.end();
       ready = false;
    }

}

function VarnishPool(opts, ready) {
    var resources = [];
    var available = [];

    for(var i = 0; i < opts.pool_size; ++i) {
        var v = new VarnishClient(opts.host, opts.port, function() {
            resources.push(v);
            available.push(v);
        });
    }

    this.run_cmd = function(cmd, callback) {
        var v = available.pop()
        if(v) {
            v.run_cmd(cmd, function(err, status_code, body) {
                callback(err, status_code, body);
                available.push(v);
                ready();
            });
        } else {
            callback('no clients available');
        }
    }

    this.close = function() {
        for(var i = 0; i < resources.length; ++i) {
            resources[i].close();
        }
    }
}

function VarnishQueue(host, port) {

    var queue = [];
    var ready = false;

    var client = new VarnishClient(host, port, function(err) {
        ready = true;
        _send_pending();
    });

    function _send_pending(empty_callback) {
        var c = queue.pop();
        if(!c) return;
        client.run_cmd(c, function() {
            if(queue.length > 0) {
                process.nextTick(_send_pending);
            } else {
                if(empty_callback) {
                    empty_callback();
                }
            }
        });
    }

    this.run_cmd = function(cmd) {
        queue.push(cmd);
        if(ready) {
            _send_pending();
        }
    }

    this.end = function() {
        _send_pending(function() {
            client.close();
        });
    }

}

/*
var queue = new VarnishQueue('localhost', 6082)
setInterval(function() {
    queue.run_cmd('purge obj.http.url == /')
}, 10)
*/
/*
v = new VarnishClient('localhost', 6082, function(err) {
    console.log('connected');
    v.run_cmd('purge obj.http.url == /', function(err, code, body) {
        console.log(code);
        console.log(body);
        v.close();
    });
});

pool = new VarnishPool({
    host: 'locahost',
    port: 6082,
    pool_size: 5
});
/*
v.close();
*/

module.exports = {
    VarnishClient: VarnishClient,
    VarnishQueue: VarnishQueue
}
<commit_msg>some varnish sanity checks<commit_after>
/**
 * this module implements varnish telnet management protocol
 * https://www.varnish-cache.org/trac/wiki/ManagementPort
 */

var net = require('net')

function VarnishClient(host, port, ready_callback) {

    var self = this;
    var ready = false;
    var cmd_callback = null;
    var client = null;

    function connect() {
        ready = false;
        client = net.createConnection(port, host);
    }

    connect();

    client.on('data', function(data) {
        data = data.toString();
        lines = data.split('\n', 2);
        if(lines.length == 2) {
            var tk = lines[0].split(' ')
            var code = parseInt(tk[0], 10);
            var body_length = parseInt(tk[1], 10);
            var body = lines[1];
            if(!ready) {
                ready = true;
                ready_callback();
            } else if(cmd_callback) {
                var c = cmd_callback
                cmd_callback = null;
                c(null, code, body);
            }
        }

    });

    client.on('error', function(err) {
        console.log("[ERROR] some problem in varnish connection");
    });

    client.on('close', function() {
        self.close();
    });

    // sends the command to the server
    function _send(cmd, callback) {
      cmd_callback = callback;
      client.write(cmd + '\n');
    }

    // run command if there is no peding response
    // fist param of the callback are the error, null
    // if all went ok
    this.run_cmd = function(cmd, callback) {
       if(!cmd_callback) {
         _send(cmd, callback);
       } else {
         callback('response pending');
       }
    }

    // close the connection
    this.close = function() {
       client.end();
       ready = false;
    }

}

function VarnishPool(opts, ready) {
    var resources = [];
    var available = [];

    for(var i = 0; i < opts.pool_size; ++i) {
        var v = new VarnishClient(opts.host, opts.port, function() {
            resources.push(v);
            available.push(v);
        });
    }

    this.run_cmd = function(cmd, callback) {
        var v = available.pop()
        if(v) {
            v.run_cmd(cmd, function(err, status_code, body) {
                callback(err, status_code, body);
                available.push(v);
                ready();
            });
        } else {
            callback('no clients available');
        }
    }

    this.close = function() {
        for(var i = 0; i < resources.length; ++i) {
            resources[i].close();
        }
    }
}

function VarnishQueue(host, port) {

    var MAX_QUEUE = 2000;
    var queue = [];
    var ready = false;

    var client = new VarnishClient(host, port, function(err) {
        ready = true;
        _send_pending();
    });

    function _send_pending(empty_callback) {
        var c = queue.pop();
        if(!c) return;
        client.run_cmd(c, function() {
            if(queue.length > 0) {
                process.nextTick(_send_pending);
            } else {
                if(empty_callback) {
                    empty_callback();
                }
            }
        });
    }

    this.run_cmd = function(cmd) {
        queue.push(cmd);
        if(queue.length > MAX_QUEUE) {
            console.log("varnish commando queu too long, removing commands");
            queue.pop();
        }
        if(ready) {
            _send_pending();
        }
    }

    this.end = function() {
        _send_pending(function() {
            client.close();
        });
    }

}

/*
var queue = new VarnishQueue('localhost', 6082)
setInterval(function() {
    queue.run_cmd('purge obj.http.url == /')
}, 10)
*/
/*
v = new VarnishClient('localhost', 6082, function(err) {
    console.log('connected');
    v.run_cmd('purge obj.http.url == /', function(err, code, body) {
        console.log(code);
        console.log(body);
        v.close();
    });
});

pool = new VarnishPool({
    host: 'locahost',
    port: 6082,
    pool_size: 5
});
/*
v.close();
*/

module.exports = {
    VarnishClient: VarnishClient,
    VarnishQueue: VarnishQueue
}
<|endoftext|>