<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let base64 = require("sdk/base64");
let data = require("sdk/self").data;
let events = require("sdk/system/events");
let runtime = require("sdk/system/runtime");
let utils = require("sdk/window/utils");

let { Cc, Ci, Cu } = require("chrome");

let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm", {});
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});

let gDebug = true;
let gPrefix = "SignText: ";
function log(x) {
  if (gDebug) {
    console.log(gPrefix + x);
  }
}

const ERROR_NO_MATCHING_CERT = "error:noMatchingCert";
const ERROR_USER_CANCEL = "error:userCancel";
const ERROR_INTERNAL = "error:internalError";

const CERTCertDBHandle = ctypes.voidptr_t;
const CERTCertificate = ctypes.voidptr_t;
const SECCertUsage = ctypes.int;
const certUsageEmailSigner = 4;
const SECItemType = ctypes.int;
const siBuffer = 0;
const SECItem = ctypes.StructType("SECItem", [
  { "type": SECItemType },
  { "data": ctypes.uint8_t.ptr },
  { "len": ctypes.int }
]);
const SEC_PKCS7ContentInfo = ctypes.voidptr_t;
const SECOidTag = ctypes.int;
const SEC_OID_SHA1 = 4;
const SECStatus = ctypes.int;
const SECSuccess = 0;
const SEC_PKCS7EncoderOutputCallback = ctypes.FunctionType(ctypes.default_abi,
                                                           ctypes.void_t,
                                                           [ctypes.voidptr_t,
                                                            ctypes.char.ptr,
                                                            ctypes.int]);

let nss3 = null;
let smime3 = null;
let nspr4 = null;
let CERT_GetDefaultCertDB = null;
let CERT_FindUserCertByUsage = null;
let CERT_DestroyCertificate = null;
let SECITEM_FreeItem = null;
let SEC_PKCS7CreateSignedData = null;
let SEC_PKCS7IncludeCertChain = null;
let SEC_PKCS7AddSigningTime = null;
let SEC_PKCS7Encode = null;
let SEC_PKCS7DestroyContentInfo = null;
let PR_GetError = null;
let PR_ErrorToString = null;

function platformIsWindows() {
  return runtime.OS == "WINNT";
}

function declareFunction(name, library, args) {
  try {
    args.unshift(ctypes.default_abi);
    args.unshift(name);
    return library.declare.apply(library, args);
  } catch (error) {
    log("couldn't find function '" + name + "' to declare");
    throw error;
  }
}

function loadLibraries() {
  let nss3path = ctypes.libraryName("nss3");
  try {
    nss3 = ctypes.open(nss3path);
  } catch (error) {
    log("opening nss3 failed: " + error);
    throw error;
  }

  if (!platformIsWindows()) {
    let smime3path = ctypes.libraryName("smime3");
    try {
      smime3 = ctypes.open(smime3path);
    } catch (error) {
      log("opening smime3 failed: " + error);
      throw error;
    }

    let nspr4path = ctypes.libraryName("nspr4");
    try {
      nspr4 = ctypes.open(nspr4path);
    } catch (error) {
      log("opening nspr4 failed: " + error);
      throw error;
    }
  } else {
    // On Windows, these libraries aren't separate.
    smime3 = nss3;
    nspr4 = nss3;
  }

  CERT_GetDefaultCertDB = declareFunction("CERT_GetDefaultCertDB", nss3,
                                          [CERTCertDBHandle]);
  CERT_FindUserCertByUsage = declareFunction("CERT_FindUserCertByUsage", nss3,
                                             [CERTCertificate,
                                              CERTCertDBHandle,
                                              ctypes.char.ptr,
                                              SECCertUsage,
                                              ctypes.bool,
                                              ctypes.voidptr_t]);
  CERT_DestroyCertificate = declareFunction("CERT_DestroyCertificate", nss3,
                                            [ctypes.void_t,
                                             CERTCertificate]);
  SECITEM_FreeItem = declareFunction("SECITEM_FreeItem", nss3,
                                     [ctypes.void_t,
                                      SECItem.ptr,
                                      ctypes.bool]);
  SEC_PKCS7CreateSignedData = declareFunction("SEC_PKCS7CreateSignedData",
                                              smime3,
                                              [SEC_PKCS7ContentInfo,
                                               CERTCertificate,
                                               SECCertUsage,
                                               CERTCertDBHandle,
                                               SECOidTag,
                                               SECItem.ptr,
                                               ctypes.voidptr_t,
                                               ctypes.voidptr_t]);
  SEC_PKCS7IncludeCertChain = declareFunction("SEC_PKCS7IncludeCertChain",
                                              smime3,
                                              [SECStatus,
                                               SEC_PKCS7ContentInfo,
                                               ctypes.voidptr_t]);
  SEC_PKCS7AddSigningTime = declareFunction("SEC_PKCS7AddSigningTime", smime3,
                                            [SECStatus,
                                             SEC_PKCS7ContentInfo]);
  SEC_PKCS7Encode = declareFunction("SEC_PKCS7Encode", smime3,
                                        [SECStatus,
                                         SEC_PKCS7ContentInfo,
                                         SEC_PKCS7EncoderOutputCallback.ptr,
                                         ctypes.voidptr_t,
                                         ctypes.voidptr_t,
                                         ctypes.voidptr_t,
                                         ctypes.voidptr_t]);
  SEC_PKCS7DestroyContentInfo = declareFunction("SEC_PKCS7DestroyContentInfo",
                                                smime3,
                                                [ctypes.void_t,
                                                 SEC_PKCS7ContentInfo]);
  PR_GetError = declareFunction("PR_GetError", nspr4, [ctypes.int]);
  PR_ErrorToString = declareFunction("PR_ErrorToString", nspr4,
                                     [ctypes.char.ptr,
                                      ctypes.int,
                                      ctypes.voidptr_t]);
}

function unloadLibraries() {
  if (nss3) {
    nss3.close();
  }
  if (smime3 && !platformIsWindows()) {
    smime3.close();
  }
  if (nspr4 && !platformIsWindows()) {
    nspr4.close();
  }
}

function getUserCerts() {
  let certCache = Cc["@mozilla.org/security/nsscertcache;1"]
                    .createInstance(Ci.nsINSSCertCache);
  certCache.cacheAllCerts();
  let certList = certCache.getX509CachedCerts();
  let userCerts = [];
  let certListEnumerator = certList.getEnumerator();
  while (certListEnumerator.hasMoreElements()) {
    let cert = certListEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
    if (cert.certType & Ci.nsIX509Cert.USER_CERT) {
      userCerts.push(cert);
    }
  }

  return userCerts;
}

function cleanupSignTextResources(cert, contentInfo, encodedItem) {
  try {
    if (cert && !cert.isNull()) {
      CERT_DestroyCertificate(cert);
    }
  } catch (error) {
    log("CERT_DestroyCertificate failed");
    logPRError();
  }

  try {
    if (contentInfo && !contentInfo.isNull()) {
      SEC_PKCS7DestroyContentInfo(contentInfo);
    }
  } catch (error) {
    log("SEC_PKCS7DestroyContentInfo failed");
    logPRError();
  }

  try {
    if (encodedItem && !encodedItem.isNull()) {
      SECITEM_FreeItem(encodedItem, true);
    }
  } catch (error) {
    log("SECITEM_FreeItem failed");
    logPRError();
  }
}

function selectCert(userCerts, text) {
  // We have to create a sandbox with the same origin as this addon's content
  // resources so we can pass data into (and get it back out of) the dialog we
  // open up to let the user select a signing certificate and confirm the
  // signing.
  let sandboxDeclarations = "var domain;\n";
  sandboxDeclarations += "var textToSign;\n";
  sandboxDeclarations += "var certs = {};\n";
  sandboxDeclarations += "var cancelled;\n";
  sandboxDeclarations += "var selectedCert;\n";
  sandboxDeclarations += "function Cert() {};\n";
  let sandbox = Cu.Sandbox(data.url("certChooser.html"));
  Cu.evalInSandbox(sandboxDeclarations, sandbox);
  let domWindow = utils.getMostRecentBrowserWindow();
  sandbox.domain = domWindow.content.location.hostname;
  sandbox.textToSign = text;
  for (let cert of userCerts) {
    sandbox.certs[cert.nickname] = new sandbox.Cert();
    sandbox.certs[cert.nickname].nickname = cert.nickname;
    sandbox.certs[cert.nickname].subject = cert.subjectName;
    sandbox.certs[cert.nickname].serialNumber = cert.serialNumber;
    sandbox.certs[cert.nickname].notBefore = cert.validity.notBeforeLocalTime;
    sandbox.certs[cert.nickname].notAfter = cert.validity.notAfterLocalTime;
    let usages = {};
    cert.getUsagesString(true, {}, usages); // true for local-only verification
    sandbox.certs[cert.nickname].usagesString = usages.value;
    sandbox.certs[cert.nickname].email = cert.emailAddress;
    sandbox.certs[cert.nickname].issuer = cert.issuerName;
    sandbox.certs[cert.nickname].token = cert.tokenName;
  }
  let watcher = Cc["@mozilla.org/embedcomp/window-watcher;1"]
                  .getService(Ci.nsIWindowWatcher);
  let dialog = domWindow.openDialog(data.url("certChooser.html"),
                                    "_blank",
                                    "dialog,centerscreen,chrome,modal",
                                    sandbox);
  if (sandbox.cancelled) {
    return ERROR_USER_CANCEL;
  }
  return sandbox.selectedCert;
}

// charPtr is expected to be null-terminated
function ctypeStringToJSString(charPtr) {
  let jsString = "";
  while (charPtr.contents != 0) {
    jsString += String.fromCharCode(charPtr.contents);
    charPtr = charPtr.increment();
  }
  return jsString;
}

function ctypeBufferToJSString(charPtr, length) {
  let jsString = "";
  for (let i = 0; i < length; i++) {
    jsString += String.fromCharCode(charPtr.contents);
    charPtr = charPtr.increment();
  }
  return jsString;
}

function logPRError() {
  try {
    let error = PR_GetError();
    let errorString = ctypeStringToJSString(PR_ErrorToString(error, null));
    log("NSS error: " + errorString + " (" + error + ")");
  } catch (error) {
    log("logging an NSS error failed");
  }
}

function signText(text) {
  let userCerts = getUserCerts();
  if (userCerts.length < 1) {
    return ERROR_NO_MATCHING_CERT;
  }

  let certName = selectCert(userCerts, text);
  if (certName == ERROR_USER_CANCEL) {
    return ERROR_USER_CANCEL;
  }

  // These are the resources that, if non-null, must be cleaned-up on all code
  // paths in this function.
  let cert = null;
  let contentInfo = null;
  let encodedItem = null;

  try {
    let certDB = CERT_GetDefaultCertDB();
    if (certDB.isNull()) {
      log("CERT_GetDefaultCertDB failed");
      logPRError();
      return ERROR_INTERNAL;
    }
    log("using '" + certName + "'");
    cert = CERT_FindUserCertByUsage(certDB, certName, certUsageEmailSigner,
                                    true, null);
    if (cert.isNull()) {
      log("CERT_FindUserCertByUsage failed");
      logPRError();
      return ERROR_INTERNAL;
    }

    let digestBytes = hash(text);
    let byteArray = ctypes.ArrayType(ctypes.uint8_t);
    let digestBytesBuffer = new byteArray(digestBytes.length);
    for (let i = 0; i < digestBytes.length; i++) {
      digestBytesBuffer[i] = digestBytes.charCodeAt(i);
    }
    let digest = new SECItem;
    digest.type = siBuffer;
    digest.data = digestBytesBuffer;
    digest.len = digestBytes.length;
    contentInfo = SEC_PKCS7CreateSignedData(cert, certUsageEmailSigner, certDB,
                                            SEC_OID_SHA1, digest.address(),
                                            null, null);
    if (contentInfo.isNull()) {
      log("SEC_PKCS7CreateSignedData failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    let status = SEC_PKCS7IncludeCertChain(contentInfo, null);
    if (status != SECSuccess) {
      log("SEC_PKCS7IncludeCertChain failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    status = SEC_PKCS7AddSigningTime(contentInfo);
    if (status != SECSuccess) {
      log("SEC_PKCS7AddSigningTime failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    let output = "";
    let encoderOutputCallback = new SEC_PKCS7EncoderOutputCallback.ptr(
      function(context, data, length) {
        if (data.isNull()) {
          log("data is null in encoderOutputCallback - library failure?");
          logPRError();
          return;
        }
        output += ctypeBufferToJSString(data, length);
      }
    );
    status = SEC_PKCS7Encode(contentInfo, encoderOutputCallback, null, null,
                             null, null);
    if (status != SECSuccess) {
      log("SEC_PKCS7Encode failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }
    cleanupSignTextResources(cert, contentInfo, encodedItem);
    let result = base64.encode(output).replace(/.{64}/g, "$&\n");
    return result;
  } catch (error) {
    log("signText failed: " + error);
    cleanupSignTextResources(cert, contentInfo, encodedItem);
  }

  return ERROR_INTERNAL;
}

// modified from browser/base/content/aboutaccounts/aboutaccounts.js
function hash(string) {
  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                    .createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8"; // XXX TODO: use current document's charset
  // Data is an array of bytes.
  let data = converter.convertToByteArray(string, {});
  let hasher = Cc["@mozilla.org/security/hash;1"]
                 .createInstance(Ci.nsICryptoHash);
  hasher.init(Ci.nsICryptoHash.SHA1);
  hasher.update(data, data.length);

  return hasher.finish(false);
}

function injectSignText(event) {
  // event.subject is an nsIDOMWindow
  // event.data is a string representing the origin
  log("injecting signText for origin " + event.data);
  let domWindow = event.subject;

  // Add signText() to window.crypto
  // Note: If you want to re-add other legacy functions, just repeat
  // these two lines with "defineAs" set to the API name, and the
  // first argument replaced by your implementation function.
  Cu.exportFunction(signText, domWindow.crypto.wrappedJSObject,
                    { defineAs: "signText" });
}

let gInitialized = false;

exports.main = function(options, callbacks) {
  if (!gInitialized &&
      (options.loadReason == "startup" ||
       options.loadReason == "install" ||
       options.loadReason == "enable")) {
    log("initializing");
    try {
      loadLibraries();
      events.on("content-document-global-created", injectSignText);
    } catch (error) {
      log("loadLibraries failed: " + error);
    }
    gInitialized = true;
  }
};

exports.onUnload = function(reason) {
  log("onUnload: " + reason);
  if (gInitialized && (reason == "shutdown" || reason == "disable")) {
    log("deinitializing");
    events.off("content-document-global-created", injectSignText);
    unloadLibraries();
    gInitialized = false;
  }
};
<commit_msg>Fix nss3 loading on OS X.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let base64 = require("sdk/base64");
let data = require("sdk/self").data;
let events = require("sdk/system/events");
let runtime = require("sdk/system/runtime");
let utils = require("sdk/window/utils");

let { Cc, Ci, Cu } = require("chrome");

let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
let { ctypes } = Cu.import("resource://gre/modules/ctypes.jsm", {});
let { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});

let gDebug = true;
let gPrefix = "SignText: ";
function log(x) {
  if (gDebug) {
    console.log(gPrefix + x);
  }
}

const ERROR_NO_MATCHING_CERT = "error:noMatchingCert";
const ERROR_USER_CANCEL = "error:userCancel";
const ERROR_INTERNAL = "error:internalError";

const CERTCertDBHandle = ctypes.voidptr_t;
const CERTCertificate = ctypes.voidptr_t;
const SECCertUsage = ctypes.int;
const certUsageEmailSigner = 4;
const SECItemType = ctypes.int;
const siBuffer = 0;
const SECItem = ctypes.StructType("SECItem", [
  { "type": SECItemType },
  { "data": ctypes.uint8_t.ptr },
  { "len": ctypes.int }
]);
const SEC_PKCS7ContentInfo = ctypes.voidptr_t;
const SECOidTag = ctypes.int;
const SEC_OID_SHA1 = 4;
const SECStatus = ctypes.int;
const SECSuccess = 0;
const SEC_PKCS7EncoderOutputCallback = ctypes.FunctionType(ctypes.default_abi,
                                                           ctypes.void_t,
                                                           [ctypes.voidptr_t,
                                                            ctypes.char.ptr,
                                                            ctypes.int]);

let nss3 = null;
let smime3 = null;
let nspr4 = null;
let CERT_GetDefaultCertDB = null;
let CERT_FindUserCertByUsage = null;
let CERT_DestroyCertificate = null;
let SECITEM_FreeItem = null;
let SEC_PKCS7CreateSignedData = null;
let SEC_PKCS7IncludeCertChain = null;
let SEC_PKCS7AddSigningTime = null;
let SEC_PKCS7Encode = null;
let SEC_PKCS7DestroyContentInfo = null;
let PR_GetError = null;
let PR_ErrorToString = null;

function platformIsOSX() {
  return runtime.OS == "Darwin";
}

function librariesAreFolded() {
  return runtime.OS == "WINNT" || platformIsOSX();
}

function declareFunction(name, library, args) {
  try {
    args.unshift(ctypes.default_abi);
    args.unshift(name);
    return library.declare.apply(library, args);
  } catch (error) {
    log("couldn't find function '" + name + "' to declare");
    throw error;
  }
}

function loadLibraries() {
  let dir = "";
  if (platformIsOSX) {
    dir = require('sdk/system').pathFor("GreBinD") + "/";
  }
  let nss3path = ctypes.libraryName("nss3");
  try {
    nss3 = ctypes.open(dir + nss3path);
  } catch (error) {
    log("opening nss3 failed: " + error);
    throw error;
  }

  if (!librariesAreFolded()) {
    let smime3path = ctypes.libraryName("smime3");
    try {
      smime3 = ctypes.open(smime3path);
    } catch (error) {
      log("opening smime3 failed: " + error);
      throw error;
    }

    let nspr4path = ctypes.libraryName("nspr4");
    try {
      nspr4 = ctypes.open(nspr4path);
    } catch (error) {
      log("opening nspr4 failed: " + error);
      throw error;
    }
  } else {
    // On Windows and OS X, these libraries aren't separate.
    smime3 = nss3;
    nspr4 = nss3;
  }

  CERT_GetDefaultCertDB = declareFunction("CERT_GetDefaultCertDB", nss3,
                                          [CERTCertDBHandle]);
  CERT_FindUserCertByUsage = declareFunction("CERT_FindUserCertByUsage", nss3,
                                             [CERTCertificate,
                                              CERTCertDBHandle,
                                              ctypes.char.ptr,
                                              SECCertUsage,
                                              ctypes.bool,
                                              ctypes.voidptr_t]);
  CERT_DestroyCertificate = declareFunction("CERT_DestroyCertificate", nss3,
                                            [ctypes.void_t,
                                             CERTCertificate]);
  SECITEM_FreeItem = declareFunction("SECITEM_FreeItem", nss3,
                                     [ctypes.void_t,
                                      SECItem.ptr,
                                      ctypes.bool]);
  SEC_PKCS7CreateSignedData = declareFunction("SEC_PKCS7CreateSignedData",
                                              smime3,
                                              [SEC_PKCS7ContentInfo,
                                               CERTCertificate,
                                               SECCertUsage,
                                               CERTCertDBHandle,
                                               SECOidTag,
                                               SECItem.ptr,
                                               ctypes.voidptr_t,
                                               ctypes.voidptr_t]);
  SEC_PKCS7IncludeCertChain = declareFunction("SEC_PKCS7IncludeCertChain",
                                              smime3,
                                              [SECStatus,
                                               SEC_PKCS7ContentInfo,
                                               ctypes.voidptr_t]);
  SEC_PKCS7AddSigningTime = declareFunction("SEC_PKCS7AddSigningTime", smime3,
                                            [SECStatus,
                                             SEC_PKCS7ContentInfo]);
  SEC_PKCS7Encode = declareFunction("SEC_PKCS7Encode", smime3,
                                        [SECStatus,
                                         SEC_PKCS7ContentInfo,
                                         SEC_PKCS7EncoderOutputCallback.ptr,
                                         ctypes.voidptr_t,
                                         ctypes.voidptr_t,
                                         ctypes.voidptr_t,
                                         ctypes.voidptr_t]);
  SEC_PKCS7DestroyContentInfo = declareFunction("SEC_PKCS7DestroyContentInfo",
                                                smime3,
                                                [ctypes.void_t,
                                                 SEC_PKCS7ContentInfo]);
  PR_GetError = declareFunction("PR_GetError", nspr4, [ctypes.int]);
  PR_ErrorToString = declareFunction("PR_ErrorToString", nspr4,
                                     [ctypes.char.ptr,
                                      ctypes.int,
                                      ctypes.voidptr_t]);
}

function unloadLibraries() {
  if (nss3) {
    nss3.close();
  }
  if (smime3 && !librariesAreFolded()) {
    smime3.close();
  }
  if (nspr4 && !librariesAreFolded()) {
    nspr4.close();
  }
}

function getUserCerts() {
  let certCache = Cc["@mozilla.org/security/nsscertcache;1"]
                    .createInstance(Ci.nsINSSCertCache);
  certCache.cacheAllCerts();
  let certList = certCache.getX509CachedCerts();
  let userCerts = [];
  let certListEnumerator = certList.getEnumerator();
  while (certListEnumerator.hasMoreElements()) {
    let cert = certListEnumerator.getNext().QueryInterface(Ci.nsIX509Cert);
    if (cert.certType & Ci.nsIX509Cert.USER_CERT) {
      userCerts.push(cert);
    }
  }

  return userCerts;
}

function cleanupSignTextResources(cert, contentInfo, encodedItem) {
  try {
    if (cert && !cert.isNull()) {
      CERT_DestroyCertificate(cert);
    }
  } catch (error) {
    log("CERT_DestroyCertificate failed");
    logPRError();
  }

  try {
    if (contentInfo && !contentInfo.isNull()) {
      SEC_PKCS7DestroyContentInfo(contentInfo);
    }
  } catch (error) {
    log("SEC_PKCS7DestroyContentInfo failed");
    logPRError();
  }

  try {
    if (encodedItem && !encodedItem.isNull()) {
      SECITEM_FreeItem(encodedItem, true);
    }
  } catch (error) {
    log("SECITEM_FreeItem failed");
    logPRError();
  }
}

function selectCert(userCerts, text) {
  // We have to create a sandbox with the same origin as this addon's content
  // resources so we can pass data into (and get it back out of) the dialog we
  // open up to let the user select a signing certificate and confirm the
  // signing.
  let sandboxDeclarations = "var domain;\n";
  sandboxDeclarations += "var textToSign;\n";
  sandboxDeclarations += "var certs = {};\n";
  sandboxDeclarations += "var cancelled;\n";
  sandboxDeclarations += "var selectedCert;\n";
  sandboxDeclarations += "function Cert() {};\n";
  let sandbox = Cu.Sandbox(data.url("certChooser.html"));
  Cu.evalInSandbox(sandboxDeclarations, sandbox);
  let domWindow = utils.getMostRecentBrowserWindow();
  sandbox.domain = domWindow.content.location.hostname;
  sandbox.textToSign = text;
  for (let cert of userCerts) {
    sandbox.certs[cert.nickname] = new sandbox.Cert();
    sandbox.certs[cert.nickname].nickname = cert.nickname;
    sandbox.certs[cert.nickname].subject = cert.subjectName;
    sandbox.certs[cert.nickname].serialNumber = cert.serialNumber;
    sandbox.certs[cert.nickname].notBefore = cert.validity.notBeforeLocalTime;
    sandbox.certs[cert.nickname].notAfter = cert.validity.notAfterLocalTime;
    let usages = {};
    cert.getUsagesString(true, {}, usages); // true for local-only verification
    sandbox.certs[cert.nickname].usagesString = usages.value;
    sandbox.certs[cert.nickname].email = cert.emailAddress;
    sandbox.certs[cert.nickname].issuer = cert.issuerName;
    sandbox.certs[cert.nickname].token = cert.tokenName;
  }
  let watcher = Cc["@mozilla.org/embedcomp/window-watcher;1"]
                  .getService(Ci.nsIWindowWatcher);
  let dialog = domWindow.openDialog(data.url("certChooser.html"),
                                    "_blank",
                                    "dialog,centerscreen,chrome,modal",
                                    sandbox);
  if (sandbox.cancelled) {
    return ERROR_USER_CANCEL;
  }
  return sandbox.selectedCert;
}

// charPtr is expected to be null-terminated
function ctypeStringToJSString(charPtr) {
  let jsString = "";
  while (charPtr.contents != 0) {
    jsString += String.fromCharCode(charPtr.contents);
    charPtr = charPtr.increment();
  }
  return jsString;
}

function ctypeBufferToJSString(charPtr, length) {
  let jsString = "";
  for (let i = 0; i < length; i++) {
    jsString += String.fromCharCode(charPtr.contents);
    charPtr = charPtr.increment();
  }
  return jsString;
}

function logPRError() {
  try {
    let error = PR_GetError();
    let errorString = ctypeStringToJSString(PR_ErrorToString(error, null));
    log("NSS error: " + errorString + " (" + error + ")");
  } catch (error) {
    log("logging an NSS error failed");
  }
}

function signText(text) {
  let userCerts = getUserCerts();
  if (userCerts.length < 1) {
    return ERROR_NO_MATCHING_CERT;
  }

  let certName = selectCert(userCerts, text);
  if (certName == ERROR_USER_CANCEL) {
    return ERROR_USER_CANCEL;
  }

  // These are the resources that, if non-null, must be cleaned-up on all code
  // paths in this function.
  let cert = null;
  let contentInfo = null;
  let encodedItem = null;

  try {
    let certDB = CERT_GetDefaultCertDB();
    if (certDB.isNull()) {
      log("CERT_GetDefaultCertDB failed");
      logPRError();
      return ERROR_INTERNAL;
    }
    log("using '" + certName + "'");
    cert = CERT_FindUserCertByUsage(certDB, certName, certUsageEmailSigner,
                                    true, null);
    if (cert.isNull()) {
      log("CERT_FindUserCertByUsage failed");
      logPRError();
      return ERROR_INTERNAL;
    }

    let digestBytes = hash(text);
    let byteArray = ctypes.ArrayType(ctypes.uint8_t);
    let digestBytesBuffer = new byteArray(digestBytes.length);
    for (let i = 0; i < digestBytes.length; i++) {
      digestBytesBuffer[i] = digestBytes.charCodeAt(i);
    }
    let digest = new SECItem;
    digest.type = siBuffer;
    digest.data = digestBytesBuffer;
    digest.len = digestBytes.length;
    contentInfo = SEC_PKCS7CreateSignedData(cert, certUsageEmailSigner, certDB,
                                            SEC_OID_SHA1, digest.address(),
                                            null, null);
    if (contentInfo.isNull()) {
      log("SEC_PKCS7CreateSignedData failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    let status = SEC_PKCS7IncludeCertChain(contentInfo, null);
    if (status != SECSuccess) {
      log("SEC_PKCS7IncludeCertChain failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    status = SEC_PKCS7AddSigningTime(contentInfo);
    if (status != SECSuccess) {
      log("SEC_PKCS7AddSigningTime failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }

    let output = "";
    let encoderOutputCallback = new SEC_PKCS7EncoderOutputCallback.ptr(
      function(context, data, length) {
        if (data.isNull()) {
          log("data is null in encoderOutputCallback - library failure?");
          logPRError();
          return;
        }
        output += ctypeBufferToJSString(data, length);
      }
    );
    status = SEC_PKCS7Encode(contentInfo, encoderOutputCallback, null, null,
                             null, null);
    if (status != SECSuccess) {
      log("SEC_PKCS7Encode failed");
      logPRError();
      cleanupSignTextResources(cert, contentInfo, encodedItem);
      return ERROR_INTERNAL;
    }
    cleanupSignTextResources(cert, contentInfo, encodedItem);
    let result = base64.encode(output).replace(/.{64}/g, "$&\n");
    return result;
  } catch (error) {
    log("signText failed: " + error);
    cleanupSignTextResources(cert, contentInfo, encodedItem);
  }

  return ERROR_INTERNAL;
}

// modified from browser/base/content/aboutaccounts/aboutaccounts.js
function hash(string) {
  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                    .createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8"; // XXX TODO: use current document's charset
  // Data is an array of bytes.
  let data = converter.convertToByteArray(string, {});
  let hasher = Cc["@mozilla.org/security/hash;1"]
                 .createInstance(Ci.nsICryptoHash);
  hasher.init(Ci.nsICryptoHash.SHA1);
  hasher.update(data, data.length);

  return hasher.finish(false);
}

function injectSignText(event) {
  // event.subject is an nsIDOMWindow
  // event.data is a string representing the origin
  log("injecting signText for origin " + event.data);
  let domWindow = event.subject;

  // Add signText() to window.crypto
  // Note: If you want to re-add other legacy functions, just repeat
  // these two lines with "defineAs" set to the API name, and the
  // first argument replaced by your implementation function.
  Cu.exportFunction(signText, domWindow.crypto.wrappedJSObject,
                    { defineAs: "signText" });
}

let gInitialized = false;

exports.main = function(options, callbacks) {
  if (!gInitialized &&
      (options.loadReason == "startup" ||
       options.loadReason == "install" ||
       options.loadReason == "enable")) {
    log("initializing");
    try {
      loadLibraries();
      events.on("content-document-global-created", injectSignText);
    } catch (error) {
      log("loadLibraries failed: " + error);
    }
    gInitialized = true;
  }
};

exports.onUnload = function(reason) {
  log("onUnload: " + reason);
  if (gInitialized && (reason == "shutdown" || reason == "disable")) {
    log("deinitializing");
    events.off("content-document-global-created", injectSignText);
    unloadLibraries();
    gInitialized = false;
  }
};
<|endoftext|>


<commit_before>jQuery(function($){
	var editor = {};
	var cssLab = new aTemplate.View({
		templates:[
			"css_preview",
			"css_edit",
			"css_search_result",
			"css_search",
			"css_setting",
			"css_new",
			"css_about",
			"css_io"
		],
		data:{
			index:0,
			components:[],
			name:"",
			newName:"",
			css:"",
			html:"",
			newCategory:"",
			category:"",
			search:"",
			about:"",
			editMode:"css",
			searchStatus:"inactive",
			searchResults:function(){
				var search = this.data.search;
				var components = this.data.components;
				return components.filter(function(comp){
					return comp.name.indexOf(search) >= 0;
				});
			}
		},
		method:{
			initialize:function(){
				this.setData(defs);
				this.loadData("css_lab");
				this.update();
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
			},
			showAlert:function(msg){
				var $alert = $("
"+msg+"
");
				$("body").append($alert);
				$alert.delay(1).queue(function(next){
				  $(this).addClass("active");
				  next();
				}).delay(700).queue(function(next){
				  $(this).removeClass('active');
				  next();
				}).delay(200).queue(function(next){
				  $(this).remove();
				  next();
				});
			},
			changeStyle:function(target){
				this.update("html","css_preview");
				this.saveData("css_lab");
			},
			editComp:function(i){
				var data = this.data;
				var components = this.getComputedProp("searchResults");
				var comp = components[i];
				this.data.css = comp.css;
				this.data.name = comp.name;
				this.data.html = comp.html;
				this.data.category = comp.category;
				this.data.about = comp.about;
				this.data.id = comp.id;
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					editor.destroy();
				}
				this.update("html","css_search_result");
				this.update("html","css_edit");
				this.update("html","css_preview");
				componentHandler.upgradeDom();
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
			},
			deleteComp:function(i){
				var data = this.data;
				var components = this.getComputedProp("searchResults");
				var comp = components[i];
				var name = comp.name;
				var comps = this.data.components;
				for(var i = 0,n = comps.length; i < n; i++){
					if(comps[i].name == name){
						comps.splice(i,1);
						break;
					}
				}
				this.update("html","css_search_result");
				this.update("html","css_edit");
				this.update("html","css_preview");
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
				this.saveData("css_lab");
				componentHandler.upgradeDom();
			},
			outputComp:function(){
				var zip = new JSZip();
				zip.file("setting.json", JSON.stringify(this.data));
				var content = zip.generate({type:"blob"});
		  		saveAs(content, "css-lab.zip");
			},
			searchComponents:function(){
				this.data.searchStatus = "active";
				this.update("html","css_search_result");
				componentHandler.upgradeDom();
			},
			convertCompToHtml:function(word){
				var data = this.data.components;
				var html = "";
				for(var i = 0,n = data.length; i < n; i++){
					if(data[i].name == word){
						html = ""+data[i].html;
					}
				}
				return html;
			},
			getUniqueId:function(){
				var components = this.data.components;
				var id = this.getRandText(15);
				var flag = false;
				while(1){
					for(var i = 0,n = components.length; i < n; i++){
						if(components[i].id == id){
							flag = true;
						}
					}
					if(flag === false){
						break;
					}
				}
				return id;
			},
			saveComponent:function(e){
				var data = this.data;
				var components = this.data.components;
				var flag = false;
				for(var i = 0,n = components.length; i < n; i++){
					var comp = components[i];
					if(comp.id == data.id){
						comp.html = data.html;
						comp.css = data.css;
						comp.name = data.name;
						comp.id = data.id;
						comp.category = data.category;
						comp.about = data.about;
					}
				}
				this.applyMethod("showAlert","コンポーネントを保存しました。");
				this.update("html","css_search_result");
				componentHandler.upgradeDom();
				this.saveData("css_lab");
			},
			addComponent:function(){
				var data = this.data;
				var id = this.applyMethod("getUniqueId");
				var obj = {html:"",css:"",name:data.newName,id:id,category:data.newCategory};
				var components = this.data.components;
				var flag = false;
				var dialog = document.querySelector(".js-new-dialog");
				this.data.components.push(obj);
				this.data.newName = "";
				this.applyMethod("showAlert","コンポーネントを追加しました。");
				this.update("html","css_search_result");
				componentHandler.upgradeDom();
				this.saveData("css_lab");
				dialog.close();
			},
			readSetting:function(){
				var evt = this.e;
				var files = evt.target.files;
				var that = this;
				for (var i = 0, f; f = files[i]; i++) {
					var reader = new FileReader();
					reader.onload = (function(theFile) {
						var json = theFile.target.result.replace("data:application/json;base64,","");
						json = atob(json);
						json = JSON.parse(json);
						that.setData(json);
						that.update();
					});
					reader.readAsDataURL(f);
				}
			},
			changeMode:function(mode){
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					editor.destroy();
				}
			  	this.data.editMode = mode;
				this.update();
				componentHandler.upgradeDom();
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
			},
			openDialog:function(){
				var dialog = document.querySelector(".js-new-dialog");
				dialog.showModal();
			},
			closeDialog:function(){
				var dialog = document.querySelector(".js-new-dialog");
				dialog.close();
			},
			openEditDialog:function(){
				var dialog = document.querySelector(".js-edit-dialog");
				dialog.showModal();
			},
			closeEditDialog:function(){
				var dialog = document.querySelector(".js-edit-dialog");
				dialog.close();
			},
			doneEditDialog:function(){
				this.applyMethod("closeEditDialog");
				this.applyMethod("saveComponent");
				this.update("html","css_edit");
			},
			clearEditor:function(){
				this.removeData(['name','html','css']);
				this.saveData("css_lab");
				this.update();
				if(this.data.editMode != "preview"){
					this.applyMethod("runEditor",this.data.editMode);
				}
				componentHandler.upgradeDom();
			},
			runEditor:function(name){
				var that = this;
				editor = ace.edit("js-"+name);
				editor.setTheme("ace/theme/monokai");
				editor.getSession().setMode("ace/mode/"+name);
				editor.setOptions({
					enableBasicAutocompletion: true,
					enableSnippets: true,
					enableLiveAutocompletion: true
				});
				editor.commands.addCommand({
					name:"reload",
					bindKey:{win:"Ctrl+R",mac:"Command-R"},
					exec: function(){

					},
				});
				editor.commands.addCommand({
					name:"save",
					bindKey:{win:"Ctrl+S",mac:"Command-S"},
					exec: function(){
						that.data[name] = editor.getSession().getValue();
						that.applyMethod("saveComponent");
					}
				});
				editor.commands.on("exec",function(e){
					if(e.command && (e.command.name == "reload" || e.command.name == "save")){
						e.preventDefault();
						e.command.exec();
					}
				});
			}
		},
		convert:{
			preview:function(text){
				//textからpreview取得
				var preview = parser.getPreview(text);
				//previewからコメント文取得
				while(1){
					var comment = parser.getComment(preview);
					if(!comment){
						break;
					}
					//commentからコンポーネント名取得
					var name = parser.getComponentName(comment);
					var components = this.data.components;
					var flag = false;
					for(var i = 0,n = components.length; i < n; i++){
						var comp = components[i];
						if(name == comp.name){
							var template = parser.getTemplate(comp.html);
							var html = parser.getInnerHtmlFromTemplate(template);
							var defs = parser.getVarsFromTemplate(template);
							var overrides = parser.getVars(comment);
							html = parser.getRendered(html,defs,overrides);
							preview = preview.replace(comment,html);
							preview += ""
							break;
						}
					}
					preview = preview.replace(comment,"");
				}
				//todo delete
				return parser.removeScript(preview);
			},
			deleteScriptTag:function(data){
				return parser.removeScript(data);
			},
			markdown:function(data){
				return marked(data);
			}
		}
	}).applyMethod("initialize");
});
<commit_msg>restriction<commit_after>jQuery(function($){
	var editor = {};
	var cssLab = new aTemplate.View({
		templates:[
			"css_preview",
			"css_edit",
			"css_search_result",
			"css_search",
			"css_setting",
			"css_new",
			"css_about",
			"css_io"
		],
		data:{
			index:0,
			components:[],
			name:"",
			newName:"",
			css:"",
			html:"",
			newCategory:"",
			category:"",
			search:"",
			about:"",
			editMode:"css",
			searchStatus:"inactive",
			searchResults:function(){
				var search = this.data.search;
				var components = this.data.components;
				return components.filter(function(comp){
					return comp.name.indexOf(search) >= 0;
				});
			}
		},
		method:{
			initialize:function(){
				this.setData(defs);
				this.loadData("css_lab");
				this.update();
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
			},
			showAlert:function(msg){
				var $alert = $("
"+msg+"
");
				$("body").append($alert);
				$alert.delay(1).queue(function(next){
				  $(this).addClass("active");
				  next();
				}).delay(700).queue(function(next){
				  $(this).removeClass('active');
				  next();
				}).delay(200).queue(function(next){
				  $(this).remove();
				  next();
				});
			},
			changeStyle:function(target){
				this.update("html","css_preview");
				this.saveData("css_lab");
			},
			editComp:function(i){
				var data = this.data;
				var components = this.getComputedProp("searchResults");
				var comp = components[i];
				this.data.css = comp.css;
				this.data.name = comp.name;
				this.data.html = comp.html;
				this.data.category = comp.category;
				this.data.about = comp.about;
				this.data.id = comp.id;
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					editor.destroy();
				}
				this.update("html","css_search_result");
				this.update("html","css_edit");
				this.update("html","css_preview");
				componentHandler.upgradeDom();
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
			},
			deleteComp:function(i){
				var data = this.data;
				var components = this.getComputedProp("searchResults");
				var comp = components[i];
				var name = comp.name;
				var comps = this.data.components;
				for(var i = 0,n = comps.length; i < n; i++){
					if(comps[i].name == name){
						comps.splice(i,1);
						break;
					}
				}
				this.update("html","css_search_result");
				this.update("html","css_edit");
				this.update("html","css_preview");
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
				this.saveData("css_lab");
				componentHandler.upgradeDom();
			},
			outputComp:function(){
				var zip = new JSZip();
				zip.file("setting.json", JSON.stringify(this.data));
				var content = zip.generate({type:"blob"});
		  		saveAs(content, "css-lab.zip");
			},
			searchComponents:function(){
				this.data.searchStatus = "active";
				this.update("html","css_search_result");
				componentHandler.upgradeDom();
			},
			convertCompToHtml:function(word){
				var data = this.data.components;
				var html = "";
				for(var i = 0,n = data.length; i < n; i++){
					if(data[i].name == word){
						html = ""+data[i].html;
					}
				}
				return html;
			},
			getUniqueId:function(){
				var components = this.data.components;
				var id = this.getRandText(15);
				var flag = false;
				while(1){
					for(var i = 0,n = components.length; i < n; i++){
						if(components[i].id == id){
							flag = true;
						}
					}
					if(flag === false){
						break;
					}
				}
				return id;
			},
			saveComponent:function(e){
				var data = this.data;
				var components = this.data.components;
				var flag = false;
				for(var i = 0,n = components.length; i < n; i++){
					var comp = components[i];
					if(comp.id == data.id){
						comp.html = data.html;
						comp.css = data.css;
						comp.name = data.name;
						comp.id = data.id;
						comp.category = data.category;
						comp.about = data.about;
					}
				}
				this.applyMethod("showAlert","コンポーネントを保存しました。");
				this.update("html","css_search_result");
				componentHandler.upgradeDom();
				this.saveData("css_lab");
			},
			addComponent:function(){
				var data = this.data;
				var id = this.applyMethod("getUniqueId");
				var obj = {html:"",css:"",name:data.newName,id:id,category:data.newCategory};
				var components = this.data.components;
				var flag = false;
				var dialog = document.querySelector(".js-new-dialog");
				this.data.components.push(obj);
				this.data.newName = "";
				this.applyMethod("showAlert","コンポーネントを追加しました。");
				this.update("html","css_search_result");
				componentHandler.upgradeDom();
				this.saveData("css_lab");
				dialog.close();
			},
			readSetting:function(){
				var evt = this.e;
				var files = evt.target.files;
				var that = this;
				for (var i = 0, f; f = files[i]; i++) {
					var reader = new FileReader();
					reader.onload = (function(theFile) {
						var json = theFile.target.result.replace("data:application/json;base64,","");
						json = atob(json);
						json = JSON.parse(json);
						that.setData(json);
						that.update();
					});
					reader.readAsDataURL(f);
				}
			},
			changeMode:function(mode){
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					editor.destroy();
				}
			  	this.data.editMode = mode;
				this.update();
				componentHandler.upgradeDom();
				if(this.data.editMode != "preview" && this.data.editMode != "about"){
					this.applyMethod("runEditor",this.data.editMode);
				}
			},
			openDialog:function(){
				var dialog = document.querySelector(".js-new-dialog");
				dialog.showModal();
			},
			closeDialog:function(){
				var dialog = document.querySelector(".js-new-dialog");
				dialog.close();
			},
			openEditDialog:function(){
				var dialog = document.querySelector(".js-edit-dialog");
				dialog.showModal();
			},
			closeEditDialog:function(){
				var dialog = document.querySelector(".js-edit-dialog");
				dialog.close();
			},
			doneEditDialog:function(){
				this.applyMethod("closeEditDialog");
				this.applyMethod("saveComponent");
				this.update("html","css_edit");
			},
			clearEditor:function(){
				this.removeData(['name','html','css']);
				this.saveData("css_lab");
				this.update();
				if(this.data.editMode != "preview"){
					this.applyMethod("runEditor",this.data.editMode);
				}
				componentHandler.upgradeDom();
			},
			isGreaterThan:function(text){
				var cat = this.data.category;
				var category = {
					atom:0,
					molucule:1,
					organism:2,
					template:3
				}
				if(category[cat] > category[text]){
					return true;
				}else{
					return false;
				}
			},
			runEditor:function(name){
				var that = this;
				editor = ace.edit("js-"+name);
				editor.setTheme("ace/theme/monokai");
				editor.getSession().setMode("ace/mode/"+name);
				editor.setOptions({
					enableBasicAutocompletion: true,
					enableSnippets: true,
					enableLiveAutocompletion: true
				});
				editor.commands.addCommand({
					name:"reload",
					bindKey:{win:"Ctrl+R",mac:"Command-R"},
					exec: function(){

					},
				});
				editor.commands.addCommand({
					name:"save",
					bindKey:{win:"Ctrl+S",mac:"Command-S"},
					exec: function(){
						that.data[name] = editor.getSession().getValue();
						that.applyMethod("saveComponent");
					}
				});
				editor.commands.on("exec",function(e){
					if(e.command && (e.command.name == "reload" || e.command.name == "save")){
						e.preventDefault();
						e.command.exec();
					}
				});
			}
		},
		convert:{
			preview:function(text){
				//textからpreview取得
				var preview = parser.getPreview(text);
				//previewからコメント文取得
				while(1){
					var comment = parser.getComment(preview);
					if(!comment){
						break;
					}
					//commentからコンポーネント名取得
					var name = parser.getComponentName(comment);
					var components = this.data.components;
					var flag = false;
					for(var i = 0,n = components.length; i < n; i++){
						var comp = components[i];
						if(name == comp.name){
							//例えば、atomはmoluculeをincludeできない
							if(this.data.id != comp.id && !this.applyMethod("isGreaterThan",comp.category)){
								break;
							}
							var template = parser.getTemplate(comp.html);
							var html = parser.getInnerHtmlFromTemplate(template);
							var defs = parser.getVarsFromTemplate(template);
							var overrides = parser.getVars(comment);
							html = parser.getRendered(html,defs,overrides);
							preview = preview.replace(comment,html);
							preview += ""
							break;
						}
					}
					preview = preview.replace(comment,"");
				}
				//todo delete
				return parser.removeScript(preview);
			},
			deleteScriptTag:function(data){
				return parser.removeScript(data);
			},
			markdown:function(data){
				return marked(data);
			}
		}
	}).applyMethod("initialize");
});
<|endoftext|>


<commit_before>'use babel';

export default {
	event1: null,
	event2: null,
	timer1: null,

	activate(state) {
		this.event1 = atom.workspace.observeTextEditors(this.callback.bind(this));
		this.event2 = atom.grammars.onDidAddGrammar(this.callback_grammar_added.bind(this));
	},

	callback(editor) {
		if (editor.getGrammar() != atom.grammars.nullGrammar) {
			return;
		}

		line = editor.lineTextForBufferRow(0);
		if (! line.startsWith('#!')) {
			return;
		}

		if (! line.endsWith('python')) {
			return;
		}

		grammar = atom.grammars.grammarForScopeName('source.python');
		if (grammar == null) {
			return;
		}

		editor.setGrammar(grammar);
	},

	callback_grammar_added(grammar) {
		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}

		var self = this;
		this.timer1 = setTimeout(function(){self.callback_all_grammars_loaded();}, 500);
	},

	callback_all_grammars_loaded() {
		this.timer1 = null;
		var editors = atom.workspace.getTextEditors();
		for (i = 0; i < editors.length; i++) {
			this.callback(editors[i]);
		}
	},

	deactivate() {
		if (this.event1 != null) {
			this.event.dispose();
		}

		if (this.event2 != null) {
			this.event2.dispose();
		}

		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}
	}
};
<commit_msg>Refactoring: use a CompositeDisposable instead of saving each event in a variable<commit_after>'use babel';

import { CompositeDisposable } from 'atom';

export default {
	events: null,
	timer1: null,

	activate(state) {
		this.subscriptions = new CompositeDisposable();
		this.subscriptions.add(
			atom.workspace.observeTextEditors(this.callback.bind(this))
		);
		this.subscriptions.add(
			atom.grammars.onDidAddGrammar(this.callback_grammar_added.bind(this))
		);
	},

	callback(editor) {
		if (editor.getGrammar() != atom.grammars.nullGrammar) {
			return;
		}

		line = editor.lineTextForBufferRow(0);
		if (! line.startsWith('#!')) {
			return;
		}

		if (! line.endsWith('python')) {
			return;
		}

		grammar = atom.grammars.grammarForScopeName('source.python');
		if (grammar == null) {
			return;
		}

		editor.setGrammar(grammar);
	},

	callback_grammar_added(grammar) {
		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}

		var self = this;
		this.timer1 = setTimeout(function(){self.callback_all_grammars_loaded();}, 500);
	},

	callback_all_grammars_loaded() {
		this.timer1 = null;
		var editors = atom.workspace.getTextEditors();
		for (i = 0; i < editors.length; i++) {
			this.callback(editors[i]);
		}
	},

	deactivate() {
		if (this.events != null) {
			this.events.dispose();
			this.events = null;
		}

		if (this.timer1 != null) {
			clearTimeout(this.timer1);
			this.timer1 = null;
		}
	}
};
<|endoftext|>


<commit_before>var _ = require('lodash');
var fs = require('fs');
var util = require('../../core/util');
var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + 'log');

var ENV = util.gekkoEnv();
var mode = util.gekkoMode();
var startTime = util.getStartTime();

var talib = require(dirs.core + 'talib');
if(talib == null) {
  log.warn('TALIB indicators could not be loaded, they will be unavailable.');
}

var tulind = require(dirs.core + 'tulind');
if(tulind == null) {
  log.warn('TULIP indicators could not be loaded, they will be unavailable.');
}

var indicatorsPath = dirs.methods + 'indicators/';
var indicatorFiles = fs.readdirSync(indicatorsPath);
var Indicators = {};

_.each(indicatorFiles, function(indicator) {
  const indicatorName = indicator.split(".")[0];
  if (indicatorName[0] != "_")
    try {
      Indicators[indicatorName] = require(indicatorsPath + indicator);
    } catch (e) {
      log.error("Failed to load indicator", indicatorName);
    }
});

var allowedIndicators = _.keys(Indicators);
var allowedTalibIndicators = _.keys(talib);
var allowedTulipIndicators = _.keys(tulind);

var Base = function(settings) {
  _.bindAll(this);

  // properties
  this.age = 0;
  this.processedTicks = 0;
  this.setup = false;
  this.settings = settings;
  this.tradingAdvisor = config.tradingAdvisor;
  // defaults
  this.requiredHistory = 0;
  this.priceValue = 'close';
  this.indicators = {};
  this.talibIndicators = {};
  this.tulipIndicators = {};
  this.asyncTick = false;
  this.candlePropsCacheSize = 1000;
  this.deferredTicks = [];

  this._prevAdvice;

  this.candleProps = {
    open: [],
    high: [],
    low: [],
    close: [],
    volume: [],
    vwp: [],
    trades: []
  };

  // make sure we have all methods
  _.each(['init', 'check'], function(fn) {
    if(!this[fn])
      util.die('No ' + fn + ' function in this trading method found.')
  }, this);

  if(!this.update)
    this.update = function() {};

  if(!this.end)
    this.end = function() {};

  // let's run the implemented starting point
  this.init();

  if(!config.debug || !this.log)
    this.log = function() {};

  this.setup = true;

  if(_.size(this.talibIndicators) || _.size(this.tulipIndicators))
    this.asyncTick = true;

  if(_.size(this.indicators))
    this.hasSyncIndicators = true;
}

// teach our base trading method events
util.makeEventEmitter(Base);

Base.prototype.tick = function(candle) {

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.age !== this.processedTicks
  ) {
    // Gekko will call talib and run strat
    // functions when talib is done, but by
    // this time the sync indicators might be
    // updated with future candles.
    //
    // See @link: https://github.com/askmike/gekko/issues/837#issuecomment-316549691
    return this.deferredTicks.push(candle);
  }

  this.age++;

  if(this.asyncTick) {
    this.candleProps.open.push(candle.open);
    this.candleProps.high.push(candle.high);
    this.candleProps.low.push(candle.low);
    this.candleProps.close.push(candle.close);
    this.candleProps.volume.push(candle.volume);
    this.candleProps.vwp.push(candle.vwp);
    this.candleProps.trades.push(candle.trades);

    if(this.age > this.candlePropsCacheSize) {
      this.candleProps.open.shift();
      this.candleProps.high.shift();
      this.candleProps.low.shift();
      this.candleProps.close.shift();
      this.candleProps.volume.shift();
      this.candleProps.vwp.shift();
      this.candleProps.trades.shift();
    }
  }

  // update all indicators
  var price = candle[this.priceValue];
  _.each(this.indicators, function(i) {
    if(i.input === 'price')
      i.update(price);
    if(i.input === 'candle')
      i.update(candle);
  },this);

  // update the trading method
  if(!this.asyncTick) {
    this.propogateTick(candle);
  } else {

    var next = _.after(
      _.size(this.talibIndicators) + _.size(this.tulipIndicators),
      () => this.propogateTick(candle)
    );

    var basectx = this;

    // handle result from talib
    var talibResultHander = function(err, result) {
      if(err)
        util.die('TALIB ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from talib
    _.each(
      this.talibIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        talibResultHander.bind(indicator)
      )
    );

    // handle result from tulip
    var tulindResultHander = function(err, result) {
      if(err)
        util.die('TULIP ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from tulip indicators
    _.each(
      this.tulipIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        tulindResultHander.bind(indicator)
      )
    );
  }

  this.propogateCustomCandle(candle);
}

// if this is a child process the parent might
// be interested in the custom candle.
if(ENV !== 'child-process') {
  Base.prototype.propogateCustomCandle = _.noop;
} else {
  Base.prototype.propogateCustomCandle = function(candle) {
    process.send({
      type: 'candle',
      candle: candle
    });
  }
}

Base.prototype.propogateTick = function(candle) {
  this.candle = candle;

  this.update(candle);

  var isAllowedToCheck = this.requiredHistory <= this.age;

  // in live mode we might receive more candles
  // than minimally needed. In that case check
  // whether candle start time is > startTime
  var isPremature;

  if(mode === 'realtime'){
    // Subtract number of minutes in current candle for instant start
    let startTimeMinusCandleSize = startTime.clone();
    startTimeMinusCandleSize.subtract(this.tradingAdvisor.candleSize, "minutes");

    isPremature = candle.start < startTimeMinusCandleSize;
  }
  else{
    isPremature = false;
  }

  if(isAllowedToCheck && !isPremature) {
    this.log(candle);
    this.check(candle);
  }
  this.processedTicks++;

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.deferredTicks.length
  ) {
    return this.tick(this.deferredTicks.shift())
  }

  // are we totally finished?
  var done = this.age === this.processedTicks;
  if(done && this.finishCb)
    this.finishCb();
}

Base.prototype.addTalibIndicator = function(name, type, parameters) {
  if(!talib)
    util.die('Talib is not enabled');

  if(!_.contains(allowedTalibIndicators, type))
    util.die('I do not know the talib indicator ' + type);

  if(this.setup)
    util.die('Can only add talib indicators in the init method!');

  var basectx = this;

  this.talibIndicators[name] = {
    run: talib[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addTulipIndicator = function(name, type, parameters) {
  if(!tulind)
  util.die('Tulip indicators is not enabled');

  if(!_.contains(allowedTulipIndicators, type))
    util.die('I do not know the tulip indicator ' + type);

  if(this.setup)
    util.die('Can only add tulip indicators in the init method!');

  var basectx = this;

  this.tulipIndicators[name] = {
    run: tulind[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addIndicator = function(name, type, parameters) {
  if(!_.contains(allowedIndicators, type))
    util.die('I do not know the indicator ' + type);

  if(this.setup)
    util.die('Can only add indicators in the init method!');

  this.indicators[name] = new Indicators[type](parameters);

  // some indicators need a price stream, others need full candles
}

Base.prototype.advice = function(newPosition, _candle) {
  // ignore soft advice coming from legacy
  // strategies.
  if(!newPosition)
    return;

  // ignore if advice equals previous advice
  if(newPosition === this._prevAdvice)
    return;

  // cache the candle this advice is based on
  if(_candle)
    var candle = _candle;
  else
    var candle = this.candle;

  this._prevAdvice = newPosition;

  _.defer(function() {
    this.emit('advice', {
      recommendation: newPosition,
      portfolio: 1,
      candle
    });
  }.bind(this));
}

// Because the trading method might be async we need
// to be sure we only stop after all candles are
// processed.
Base.prototype.finish = function(done) {
  if(!this.asyncTick) {
    this.end();
    return done();
  }

  if(this.age === this.processedTicks) {
    this.end();
    return done();
  }

  // we are not done, register cb
  // and call after we are..
  this.finishCb = done;
}

module.exports = Base;
<commit_msg>base trading method update<commit_after>var _ = require('lodash');
var fs = require('fs');
var util = require('../../core/util');
var config = util.getConfig();
var dirs = util.dirs();
var log = require(dirs.core + 'log');

var ENV = util.gekkoEnv();
var mode = util.gekkoMode();
var startTime = util.getStartTime();

var talib = require(dirs.core + 'talib');
if(talib == null) {
  log.warn('TALIB indicators could not be loaded, they will be unavailable.');
}

var tulind = require(dirs.core + 'tulind');
if(tulind == null) {
  log.warn('TULIP indicators could not be loaded, they will be unavailable.');
}

var indicatorsPath = dirs.methods + 'indicators/';
var indicatorFiles = fs.readdirSync(indicatorsPath);
var Indicators = {};

_.each(indicatorFiles, function(indicator) {
  const indicatorName = indicator.split(".")[0];
  if (indicatorName[0] != "_")
    try {
      Indicators[indicatorName] = require(indicatorsPath + indicator);
    } catch (e) {
      log.error("Failed to load indicator", indicatorName);
    }
});

var allowedIndicators = _.keys(Indicators);
var allowedTalibIndicators = _.keys(talib);
var allowedTulipIndicators = _.keys(tulind);

var Base = function(settings) {
  _.bindAll(this);

  // properties
  this.age = 0;
  this.processedTicks = 0;
  this.setup = false;
  this.settings = settings;
  this.tradingAdvisor = config.tradingAdvisor;
  // defaults
  this.requiredHistory = 0;
  this.priceValue = 'close';
  this.indicators = {};
  this.talibIndicators = {};
  this.tulipIndicators = {};
  this.asyncTick = false;
  this.candlePropsCacheSize = 1000;
  this.deferredTicks = [];

  this._prevAdvice;
  this._position; // store the current position from the last successful trade

  this.candleProps = {
    open: [],
    high: [],
    low: [],
    close: [],
    volume: [],
    vwp: [],
    trades: []
  };

  // make sure we have all methods
  _.each(['init', 'check'], function(fn) {
    if(!this[fn])
      util.die('No ' + fn + ' function in this trading method found.')
  }, this);

  if(!this.update)
    this.update = function() {};

  if(!this.end)
    this.end = function() {};

  if(!this.onTrade)
    this.onTrade = function() {};

  // let's run the implemented starting point
  this.init();

  if(!config.debug || !this.log)
    this.log = function() {};

  this.setup = true;

  if(_.size(this.talibIndicators) || _.size(this.tulipIndicators))
    this.asyncTick = true;

  if(_.size(this.indicators))
    this.hasSyncIndicators = true;
}

// teach our base trading method events
util.makeEventEmitter(Base);

Base.prototype.tick = function(candle) {

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.age !== this.processedTicks
  ) {
    // Gekko will call talib and run strat
    // functions when talib is done, but by
    // this time the sync indicators might be
    // updated with future candles.
    //
    // See @link: https://github.com/askmike/gekko/issues/837#issuecomment-316549691
    return this.deferredTicks.push(candle);
  }

  this.age++;

  if(this.asyncTick) {
    this.candleProps.open.push(candle.open);
    this.candleProps.high.push(candle.high);
    this.candleProps.low.push(candle.low);
    this.candleProps.close.push(candle.close);
    this.candleProps.volume.push(candle.volume);
    this.candleProps.vwp.push(candle.vwp);
    this.candleProps.trades.push(candle.trades);

    if(this.age > this.candlePropsCacheSize) {
      this.candleProps.open.shift();
      this.candleProps.high.shift();
      this.candleProps.low.shift();
      this.candleProps.close.shift();
      this.candleProps.volume.shift();
      this.candleProps.vwp.shift();
      this.candleProps.trades.shift();
    }
  }

  // update all indicators
  var price = candle[this.priceValue];
  _.each(this.indicators, function(i) {
    if(i.input === 'price')
      i.update(price);
    if(i.input === 'candle')
      i.update(candle);
  },this);

  // update the trading method
  if(!this.asyncTick) {
    this.propogateTick(candle);
  } else {

    var next = _.after(
      _.size(this.talibIndicators) + _.size(this.tulipIndicators),
      () => this.propogateTick(candle)
    );

    var basectx = this;

    // handle result from talib
    var talibResultHander = function(err, result) {
      if(err)
        util.die('TALIB ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from talib
    _.each(
      this.talibIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        talibResultHander.bind(indicator)
      )
    );

    // handle result from tulip
    var tulindResultHander = function(err, result) {
      if(err)
        util.die('TULIP ERROR:', err);

      // fn is bound to indicator
      this.result = _.mapValues(result, v => _.last(v));
      next(candle);
    }

    // handle result from tulip indicators
    _.each(
      this.tulipIndicators,
      indicator => indicator.run(
        basectx.candleProps,
        tulindResultHander.bind(indicator)
      )
    );
  }

  this.propogateCustomCandle(candle);
}

// if this is a child process the parent might
// be interested in the custom candle.
if(ENV !== 'child-process') {
  Base.prototype.propogateCustomCandle = _.noop;
} else {
  Base.prototype.propogateCustomCandle = function(candle) {
    process.send({
      type: 'candle',
      candle: candle
    });
  }
}

Base.prototype.propogateTick = function(candle) {
  this.candle = candle;

  this.update(candle);

  var isAllowedToCheck = this.requiredHistory <= this.age;

  // in live mode we might receive more candles
  // than minimally needed. In that case check
  // whether candle start time is > startTime
  var isPremature;

  if(mode === 'realtime'){
    // Subtract number of minutes in current candle for instant start
    let startTimeMinusCandleSize = startTime.clone();
    startTimeMinusCandleSize.subtract(this.tradingAdvisor.candleSize, "minutes");

    isPremature = candle.start < startTimeMinusCandleSize;
  }
  else{
    isPremature = false;
  }

  if(isAllowedToCheck && !isPremature) {
    this.log(candle);
    this.check(candle);
  }
  this.processedTicks++;

  if(
    this.asyncTick &&
    this.hasSyncIndicators &&
    this.deferredTicks.length
  ) {
    return this.tick(this.deferredTicks.shift())
  }

  // are we totally finished?
  var done = this.age === this.processedTicks;
  if(done && this.finishCb)
    this.finishCb();
}

Base.prototype.processTrade = function(trade) {
    this.onTrade(trade);
    this._position = (trade.action === 'buy' && _.isNumber(trade.price)) ? 'long' : 'short';
}

Base.prototype.addTalibIndicator = function(name, type, parameters) {
  if(!talib)
    util.die('Talib is not enabled');

  if(!_.contains(allowedTalibIndicators, type))
    util.die('I do not know the talib indicator ' + type);

  if(this.setup)
    util.die('Can only add talib indicators in the init method!');

  var basectx = this;

  this.talibIndicators[name] = {
    run: talib[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addTulipIndicator = function(name, type, parameters) {
  if(!tulind)
  util.die('Tulip indicators is not enabled');

  if(!_.contains(allowedTulipIndicators, type))
    util.die('I do not know the tulip indicator ' + type);

  if(this.setup)
    util.die('Can only add tulip indicators in the init method!');

  var basectx = this;

  this.tulipIndicators[name] = {
    run: tulind[type].create(parameters),
    result: NaN
  }
}

Base.prototype.addIndicator = function(name, type, parameters) {
  if(!_.contains(allowedIndicators, type))
    util.die('I do not know the indicator ' + type);

  if(this.setup)
    util.die('Can only add indicators in the init method!');

  this.indicators[name] = new Indicators[type](parameters);

  // some indicators need a price stream, others need full candles
}

Base.prototype.advice = function(newPosition, _candle) {
  // ignore soft advice coming from legacy
  // strategies.
  if(!newPosition)
    return;

  // ignore if advice equals previous advice
  // updated to take the current actual position into account in case a previous advice resulted in a cancelled/failed trade
  // ** still need to consider a way to deal with an enhancement to not use the full balance on every trade
  // ** making multiple instances of the same advice in succession desirable
  if(newPosition === this._prevAdvice && newPosition === this._position) {
    log.debug('Advice to go [' + newPosition.toUpperCase() + '] is the same as the previous advice and current market position, ignoring.');
    return;
  }

  // cache the candle this advice is based on
  if(_candle)
    var candle = _candle;
  else
    var candle = this.candle;

  this._prevAdvice = newPosition;

  _.defer(function() {
    this.emit('advice', {
      recommendation: newPosition,
      portfolio: 1,
      candle
    });
  }.bind(this));
}

// Because the trading method might be async we need
// to be sure we only stop after all candles are
// processed.
Base.prototype.finish = function(done) {
  if(!this.asyncTick) {
    this.end();
    return done();
  }

  if(this.age === this.processedTicks) {
    this.end();
    return done();
  }

  // we are not done, register cb
  // and call after we are..
  this.finishCb = done;
}

module.exports = Base;
<|endoftext|>


<commit_before>(function() {

var stage;
var tikis;
var selectedTiki;
var hasSelected;
var tikiWidth = 0, tikiHeight = 0;
var tikiColor = ['blue', 'green', 'yellow', 'orange', 'red'];

$(document).ready(function() {
        startCanvas();
        createCards();
    });

function startCanvas() {

    // Scale canvas with its actually width and height
    var gameCanvas = $('#game-canvas');
    var canvasWidth = gameCanvas.width();
    var canvasHeight = gameCanvas.height();
    gameCanvas.attr('width', canvasWidth)
            .attr('height', canvasHeight);

    // Initial stage by referencing canvas
    stage = new createjs.Stage(gameCanvas[0]);
    stage.enableMouseOver(20);

    tikis = new Array();
    tikiHeight = canvasHeight / tikiColor.length - 10;
    tikiWidth = tikiHeight * 2 / 3;
    var tikiX = canvasWidth / 2 - tikiWidth / 2;
    var tikiY = 10;

    var circleMark = new createjs.Graphics();
        circleMark.setStrokeStyle(1).beginFill('#FFFFFF').drawCircle(0, 0, 3);
    var selectMark = new createjs.Shape(circleMark);

    selectedTiki = null;
    hasSelected = false;
    for (var i = 0; i < tikiColor.length; ++i) {
        var tiki = new createjs.Shape();
        tiki.graphics.beginFill(tikiColor[i])
            .drawRoundRect(0, 0, tikiWidth, tikiHeight, 5);
        tiki.x = tikiX;
        tiki.y = tikiY;
        tikiY += tikiHeight + 5;

        tiki.addEventListener('mouseover', function(evt) {
            //evt.target.alpha = 0.6;
            //console.log(!hasSelected);
            if (!hasSelected) {
                selectMark.x = evt.target.x + 10;
                selectMark.y = evt.target.y + 10;
                stage.addChild(selectMark);
                stage.update();
            }
        });
        tiki.addEventListener('mouseout', function(evt) {
            //evt.target.alpha = 1;
            if (!hasSelected) {
                stage.removeChild(selectMark);
                stage.update();
            }
        });
        tiki.addEventListener('click', function(evt) {
            if (null === selectedTiki) {
                selectedTiki = evt.target;
                hasSelected = true;
            }
            else {
                stage.removeChild(selectMark);
                if (evt.target === selectedTiki) {
                    selectedTiki = null;
                    hasSelected = false;
                }
                else {
                    selectedTiki = evt.target;
                    selectMark.x = evt.target.x + 10;
                    selectMark.y = evt.target.y + 10;
                    stage.addChild(selectMark);
                }
            }
            stage.update();
        });

        tikis.push(tiki);
        stage.addChild(tiki);
    }

    stage.update();
}

function createCards() {
    $('#card-wrapper li').append('
Card
');
    $('#card-wrapper li div').on('click', function() {

        //var targetIdx = Math.floor(Math.random() * (tikis.length-1)) + 1;
        if (null === selectedTiki) {
            return;
        }

        var upper, lower;
        var targetIdx = tikis.indexOf(selectedTiki);
        if (-1 === targetIdx) {
            console.log("selected tiki not in tiki list!");
            return;
        }
        else if (0 === targetIdx) {
            alert('Cannot move the first Tiki!');
            return;
        }
        else {
            upper = tikis[targetIdx - 1];
            lower = selectedTiki;
        }
        /*var upper = tikis[targetIdx - 1];
        var lower = tikis[targetIdx];*/
        tikis[targetIdx - 1] = lower;
        tikis[targetIdx] = upper;
        console.log(upper);
        console.log(lower);

        var upperX = upper.x, upperY = upper.y;
        var lowerX = lower.x, lowerY = lower.y;
        var animStep = 0;

        stage.removeChildAt(5);
        hasSelected = false;
        selectedTiki = null;

        createjs.Ticker.addEventListener("tick", tick);
        createjs.Ticker.setInterval(60);
        createjs.Ticker.setFPS(60);

        function tick(event) {
            if (animStep === 0) {
                if (lowerX - lower.x > tikiWidth / 2 + 1) {
                    animStep = 1;
                }
                else {
                    upper.x = upper.x + tikiWidth / 5;
                    lower.x = lower.x - tikiWidth / 5;
                }
            }
            if (animStep === 1) {
                if (lower.y === upperY) {
                    animStep = 2;
                }
                else {
                    upper.y = upper.y + (lowerY - upperY) / 10;
                    lower.y = lower.y - (lowerY - upperY) / 10;
                }
            }
            if (animStep === 2) {
                if (lower.x === upperX) {
                    animStep = 3;
                }
                else {
                    upper.x = upper.x - tikiWidth / 5;
                    lower.x = lower.x + tikiWidth / 5;
                }
            }
            if (animStep === 3) {
                createjs.Ticker.removeEventListener('tick', tick);
            }

            stage.update(event);
        }
    });
}

})();<commit_msg>Modify boundaries of the direction-changing of tiki animation<commit_after>(function() {

var stage;
var tikis;
var selectedTiki;
var hasSelected;
var tikiWidth = 0, tikiHeight = 0;
var tikiColor = ['blue', 'green', 'yellow', 'orange', 'red'];

$(document).ready(function() {
        startCanvas();
        createCards();
    });

function startCanvas() {

    // Scale canvas with its actually width and height
    var gameCanvas = $('#game-canvas');
    var canvasWidth = gameCanvas.width();
    var canvasHeight = gameCanvas.height();
    gameCanvas.attr('width', canvasWidth)
            .attr('height', canvasHeight);

    // Initial stage by referencing canvas
    stage = new createjs.Stage(gameCanvas[0]);
    stage.enableMouseOver(20);

    tikis = new Array();
    tikiHeight = canvasHeight / tikiColor.length - 10;
    tikiWidth = tikiHeight * 2 / 3;
    var tikiX = canvasWidth / 2 - tikiWidth / 2;
    var tikiY = 10;

    var circleMark = new createjs.Graphics();
        circleMark.setStrokeStyle(1).beginFill('#FFFFFF').drawCircle(0, 0, 3);
    var selectMark = new createjs.Shape(circleMark);

    selectedTiki = null;
    hasSelected = false;
    for (var i = 0; i < tikiColor.length; ++i) {
        var tiki = new createjs.Shape();
        tiki.graphics.beginFill(tikiColor[i])
            .drawRoundRect(0, 0, tikiWidth, tikiHeight, 5);
        tiki.x = tikiX;
        tiki.y = tikiY;
        tikiY += tikiHeight + 5;

        tiki.addEventListener('mouseover', function(evt) {
            //evt.target.alpha = 0.6;
            //console.log(!hasSelected);
            if (!hasSelected) {
                selectMark.x = evt.target.x + 10;
                selectMark.y = evt.target.y + 10;
                stage.addChild(selectMark);
                stage.update();
            }
        });
        tiki.addEventListener('mouseout', function(evt) {
            //evt.target.alpha = 1;
            if (!hasSelected) {
                stage.removeChild(selectMark);
                stage.update();
            }
        });
        tiki.addEventListener('click', function(evt) {
            if (null === selectedTiki) {
                selectedTiki = evt.target;
                hasSelected = true;
            }
            else {
                stage.removeChild(selectMark);
                if (evt.target === selectedTiki) {
                    selectedTiki = null;
                    hasSelected = false;
                }
                else {
                    selectedTiki = evt.target;
                    selectMark.x = evt.target.x + 10;
                    selectMark.y = evt.target.y + 10;
                    stage.addChild(selectMark);
                }
            }
            stage.update();
        });

        tikis.push(tiki);
        stage.addChild(tiki);
    }

    stage.update();
}

function createCards() {
    $('#card-wrapper li').append('
Card
');
    $('#card-wrapper li div').on('click', function() {

        //var targetIdx = Math.floor(Math.random() * (tikis.length-1)) + 1;
        if (null === selectedTiki) {
            return;
        }

        var upper, lower;
        var targetIdx = tikis.indexOf(selectedTiki);
        if (-1 === targetIdx) {
            console.log("selected tiki not in tiki list!");
            return;
        }
        else if (0 === targetIdx) {
            alert('Cannot move the first Tiki!');
            return;
        }
        else {
            upper = tikis[targetIdx - 1];
            lower = selectedTiki;
        }
        /*var upper = tikis[targetIdx - 1];
        var lower = tikis[targetIdx];*/
        tikis[targetIdx - 1] = lower;
        tikis[targetIdx] = upper;
        console.log(upper);
        console.log(lower);

        var upperX = upper.x, upperY = upper.y;
        var lowerX = lower.x, lowerY = lower.y;
        var animStep = 0;

        stage.removeChildAt(5);
        hasSelected = false;
        selectedTiki = null;

        createjs.Ticker.addEventListener("tick", tick);
        createjs.Ticker.setInterval(60);
        createjs.Ticker.setFPS(60);

        function tick(event) {
            if (animStep === 0) {
                if (lowerX - lower.x > tikiWidth / 2 + 1) {
                    animStep = 1;
                }
                else {
                    upper.x = upper.x + tikiWidth / 5;
                    lower.x = lower.x - tikiWidth / 5;
                }
            }
            if (animStep === 1) {
                if (lower.y <= upperY) {
                    lower.y = upperY;
                    upper.y = lowerY;
                    animStep = 2;
                }
                else {
                    upper.y = upper.y + (lowerY - upperY) / 10;
                    lower.y = lower.y - (lowerY - upperY) / 10;
                }
            }
            if (animStep === 2) {
                if (lower.x >= upperX) {
                    lower.x = upperX;
                    upper.x = lowerX;
                    animStep = 3;
                }
                else {
                    upper.x = upper.x - tikiWidth / 5;
                    lower.x = lower.x + tikiWidth / 5;
                }
            }
            if (animStep === 3) {
                createjs.Ticker.removeEventListener('tick', tick);
            }

            stage.update(event);
        }
    });
}

})();<|endoftext|>


<commit_before>(function (global, undefined) {
    'use strict';

    console.log('start main.js');

    var Server = global.Server;
    var Face = global.Face;

    const MBED_URL = 'http://127.0.0.1:5000';
    const PORT = 3000;

    function initializeServer() {
        var server = new Server();
        global.server = server;

        server.setPort(PORT);

        // set mbed related things
        server.addEventListener('mbedRequested', function (req, res, oncomplete) {
            var path = req.path + '?' + req.queryString;
            var xhr = new XMLHttpRequest();
            xhr.open('GET', MBED_URL + path, true);
            try {
                xhr.send();
            } catch (e) {
                console.error(e);
            }
            res._headers = {
                'Access-Control-Allow-Origin' : '*'
            };
            oncomplete();
        });

        // set face related things
        server.addEventListener('faceRequested', function (req, res, oncomplete) {
            res._headers = {
                'Access-Control-Allow-Origin' : '*'
            };
            oncomplete();
        });

        if (server.start()) {
            console.log('Started server on port ' + server.getPort());
        }
    }

    function initializeFace() {
        var face = new Face(document.getElementById('face'));
        global.face = face;
    }

    window.addEventListener('load', function () {
        console.log('window onload');

        screen.mozLockOrientation('landscape-primary');
        initializeServer();
        initializeFace();
    });
}(this));
<commit_msg>Fix HTTP header handling<commit_after>(function (global, undefined) {
    'use strict';

    console.log('start main.js');

    var Server = global.Server;
    var Face = global.Face;

    const MBED_URL = 'http://127.0.0.1:5000';
    const PORT = 3000;

    function initializeServer() {
        var server = new Server();
        global.server = server;

        server.setPort(PORT);

        // set mbed related things
        server.addEventListener('mbedRequested', function (req, res, oncomplete) {
            var path = req.path + '?' + req.queryString;
            var xhr = new XMLHttpRequest();
            xhr.open('GET', MBED_URL + path, true);
            try {
                xhr.send();
            } catch (e) {
                console.error(e);
            }
            res.setHeader('Access-Control-Allow-Origin', '*');
            oncomplete();
        });

        // set face related things
        server.addEventListener('faceRequested', function (req, res, oncomplete) {
            res.setHeader('Access-Control-Allow-Origin', '*');
            oncomplete();
        });

        if (server.start()) {
            console.log('Started server on port ' + server.getPort());
        }
    }

    function initializeFace() {
        var face = new Face(document.getElementById('face'));
        global.face = face;
    }

    window.addEventListener('load', function () {
        console.log('window onload');

        screen.mozLockOrientation('landscape-primary');
        initializeServer();
        initializeFace();
    });
}(this));
<|endoftext|>