<commit_before>const os = require('os');
const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const utils = require('../utils.js');
const KiteError = require('../kite-error');

const {STATES} = require('../constants');

const KEY_BAT = `"${path.join(__dirname, 'read-key.bat')}"`;
const ARCH_BAT = `"${path.join(__dirname, 'read-arch.bat')}"`;

let COMPUTED_INSTALL_PATH;

const WindowsSupport = {
  RELEASE_URL: 'https://alpha.kite.com/release/dls/windows/current',
  KITE_INSTALLER_PATH: path.join(os.tmpdir(), 'KiteSetup.exe'),
  SESSION_FILE_PATH: path.join(process.env.LOCALAPPDATA, 'Kite', 'session.json'),
  // We're only setting the install path in tests
  set KITE_EXE_PATH(path) {
    COMPUTED_INSTALL_PATH = path;
  },
  get KITE_EXE_PATH() {
    if (!COMPUTED_INSTALL_PATH) {
      let installDir, fallbackInstallDir;

      if (process.env.ProgramW6432) {
        fallbackInstallDir = path.join(process.env.ProgramW6432, 'Kite');
      } else {
        // TODO: report that even the fallback needed a fallback
        fallbackInstallDir = 'C:\\Program Files\\Kite';
      }

      try {
        const registryDir = String(child_process.execSync(KEY_BAT)).trim();

        installDir = registryDir !== 'not found'
          ? registryDir
          : fallbackInstallDir;
      } catch (err) {
        installDir = fallbackInstallDir;
      }
      COMPUTED_INSTALL_PATH = path.join(installDir, 'kited.exe');
    }

    return COMPUTED_INSTALL_PATH;
  },

  get releaseURL() {
    return this.RELEASE_URL;
  },

  get downloadPath() {
    return this.KITE_INSTALLER_PATH;
  },

  get installPath() {
    return this.KITE_EXE_PATH;
  },

  get enterpriseInstallPath() {
    return null;
  },

  get allInstallPath() {
    return [this.installPath];
  },

  get allEnterpriseInstallPath() {
    return [];
  },

  get sessionFilePath() {
    return this.SESSION_FILE_PATH;
  },

  isAdmin() {
    return true;
    // try {
    //   child_process.execSync('net session');
    //   return true;
    // } catch (e) {
    //   return false;
    // }
  },

  arch() {
    if (this.cachedArch) { return this.cachedArch; }
    try {
      this.cachedArch = String(child_process.execSync(ARCH_BAT)).trim();
      return this.cachedArch;
    } catch (err) {
      return '';
    }
  },

  isOSSupported() {
    return true;
  },

  isOSVersionSupported() {
    return parseFloat(os.release()) >= 6.1 &&
           this.arch() === '64bit';
  },

  isKiteSupported() {
    return this.isOSVersionSupported();
  },

  isKiteInstalled() {
    return fs.existsSync(this.KITE_EXE_PATH)
      ? Promise.resolve()
      : Promise.reject(new KiteError('bad_state', STATES.UNINSTALLED));
  },

  isKiteEnterpriseInstalled() {
    return Promise.reject(new KiteError('bad_state', STATES.UNSUPPORTED));
  },

  hasManyKiteInstallation() {
    return this.allInstallPath.length > 1;
  },

  hasManyKiteEnterpriseInstallation() {
    return this.allEnterpriseInstallPath.length > 1;
  },

  hasBothKiteInstalled() {
    return Promise.all([
      this.isKiteInstalled(),
      this.isKiteEnterpriseInstalled(),
    ]);
  },

  installKite(opts) {
    opts = opts || {};
    var env = Object.create(process.env);
    env.KITE_SKIP_ONBOARDING = '1';

    utils.guardCall(opts.onInstallStart);
    return utils.execPromise(this.KITE_INSTALLER_PATH + ' --skip-onboarding --plugin-launch', {env: env})
    .then(() => utils.guardCall(opts.onCopy))
    .then(() => fs.unlinkSync(this.KITE_INSTALLER_PATH))
    .then(() => utils.guardCall(opts.onRemove));
  },

  isKiteRunning() {
    return utils.spawnPromise('tasklist', 'tasklist_error')
    .then(stdout => {
      const procs = stdout.split('\n');
      if (procs.every(l => l.indexOf('kited.exe') === -1)) {
        throw new KiteError('bad_state', STATES.INSTALLED);
      }
    });
  },

  runKite() {
    var env = Object.create(process.env);
    env.KITE_SKIP_ONBOARDING = '1';
    child_process.spawn(this.KITE_EXE_PATH, {detached: true, env: env});
    // child_process.spawn(`${this.KITE_EXE_PATH} --plugin-launch`, {detached: true, env: env});
    return Promise.resolve();
  },

  isKiteEnterpriseRunning() {
    return Promise.reject(new KiteError('bad_state', STATES.UNSUPPORTED));
  },

  runKiteEnterprise() {
    return Promise.reject(new KiteError('bad_state', STATES.UNSUPPORTED));
  },
};

module.exports = WindowsSupport;
<commit_msg>:fire: remaining windows support file<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

(function() {

  // Declares the public namespace. This is used to make items publically
  // visible.
  var tachyfontprelude = {};

  /** @type {!Object} Indicates which fonts were loaded */
  tachyfontprelude['loaded'] = {};

  /** @type {!Object} The URL for the fonts which were loaded */
  tachyfontprelude['urls'] = {};

  /** @type {boolean} Indicates a DOM mutation has occured. */
  tachyfontprelude['DomMutationObserved'] = false;

  // Create a DOM mutation observer.
  var observer = new MutationObserver(function(mutations) {
    tachyfontprelude['DomMutationObserved'] = true;
  });
  // Watch for these mutations.
  var config = /** @type {!MutationObserverInit} */ ({ 'childList': true,
    'subtree': true, 'characterData': true });
  observer.observe(document.documentElement, config);

  /** @type {boolean} Indicates the DOM content is fully loaded. */
  tachyfontprelude['DomContentLoaded'] = false;

  // Check the DOM when it reports loading the page is done.
  document.addEventListener("DOMContentLoaded", function(event) {
    tachyfontprelude['DOMContentLoaded'] = true;
  });

  /** @const {string} The database name prefix. */
  var DB_NAME_PREFIX = 'incrfonts';

  /** @const {string} The db store name for the font base. */
  var COMPACT_FONT = 'compact_font';

  /** @const {string} The db store name for the metadata. */
  var COMPACT_META = 'compact_metadata';

  /** @const {string} The db store name for the metadata. */
  var CREATED_METADATA_TIME = 'created_metadata_time';

  /**
   * The persistence 'stable' time in milliseconds.
   * If the data was only recently created then it could be the first time the
   * client has ever used TachyFont. This should be fairly infrequent. However,
   * it is also possible that the data was recently created because the client
   * has an auto clean feature that is automatically deleting the data. This is
   * the sum of the global and per font stable times.
   * @type {number}
   */
  var STABLE_DATA_TIME = 24 * 60 * 60 * 1000;

  /** @const {string} The Style Sheet ID. */
  var STYLESHEET_ID = 'Incremental\u00A0Font\u00A0Utils';

  /** @const {string} Failed to open IndexedDB error. */
  var ERROR_PRELUDE_INDEXEDDB_OPEN = '01';

  /** @const {string} IndexedDB missing the base field error. */
  var ERROR_PRELUDE_MISSING_BASE = '02';

  /** @const {string} IndexedDB missing the metadata field error. */
  var ERROR_PRELUDE_MISSING_METADATA = '06';

  /**
   * Indicates the data is younger than the 'stable' time.
   * @const {string}
   */
  var ERROR_PRELUDE_BELOW_STABLE_TIME = '07';

  /**
   * The errors encounter while loading the Tachyfont preludes.
   * These will be reported by the TachyFont library.
   *
   * @type {!Array<!Array<string|number>>}
   */
  var reports = [];

  /** @const {number} TachyFontPrelude start time. */
  var START_TIME = (new Date()).getTime();

  /**
   * The information needed to load a font.
   * @param {string} fontFamily The font name.
   * @param {string} weight The font weight.
   * @param {boolean} priority Whether this is a priority font.
   * @param {boolean} isTtf True is TTF and false if OTF.
   * @constructor
   */
  function FontInfo(fontFamily, weight, priority, isTtf) {
    /** @type {string} The font name. */
    this.fontFamily = fontFamily;

    /** @type {string} The font weight. */
    this.weight = weight;

    /**
     * True if this is a priority font that should be prioritized ahead of other
     * fonts.
     * @type {boolean}
     */
    this.priority = priority;

    /** @type {boolean} isTtf True is TTF and false if OTF. */
    this.isTtf = isTtf;
  }

  /**
   * Loads a file using XHR.
   * @param {string} url The URL to the file.
   * @param {string} responseType The requested response type.
   * @return {!Promise<*>} A promise for the url data.
   */
  function loadUrl(url, responseType) {
    return new Promise(function(resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.responseType = responseType;
      xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
          if (xhr.status == 200) {
            resolve(xhr.response);
          } else {
            reject(xhr.statusText);
          }
        }
      };
      xhr.open('GET', url, true);
      xhr.send();
    });
  }

  /**
   * Loads the TachyFonts from persistent store if available.
   * @param {string} cssFontFamily The CSS font-family name.
   * @param {!Array<!FontInfo>} fontInfos The list of fonts to load.
   * @return {!Promise<boolean>} Resolves true if all the fonts were persisted.
   */
  function loadFont(cssFontFamily, fontInfos) {
    var style = document.getElementById(STYLESHEET_ID);
    if (!style) {
      style = document.createElement('style');
      style.id = STYLESHEET_ID;
      document.head.appendChild(style);
    }
    var sheet = style.sheet;

    var fontInfos1;
    var promises;
    return newResolvedPromise()
        .then(function() {
          // Prevent the browser creating ransom note effects by picking glyphs
          // from other weights in the family by specifying a placeholder font.
          fontInfos1 = fontInfos.slice();
          promises = [];
          for (var i = 0; i < fontInfos1.length; i++) {
            promises.push(
                setFontNoFlash(sheet, cssFontFamily, null, fontInfos1[i]));
          }
          return Promise.all(promises);
        })
        .then(function() {
          // Now load whatever fonts are already persisted.
          fontInfos1 = fontInfos.slice();
          promises = [];
          for (var i = 0; i < fontInfos1.length; i++) {
            promises.push(useFont(sheet, cssFontFamily, fontInfos1[i]));
          }
          return Promise.all(promises).then(function(loadedFonts) {
            var allLoaded = true;
            for (var i = 0; i < loadedFonts.length; i++) {
              allLoaded &= loadedFonts[i];
            }
            return allLoaded;
          });
        });
  }

  /**
   * Uses a TachyFont from persistent store if available.
   * @param {!FontInfo} fontInfo The info on the font to use.
   * @return {!Promise} This promise resolves when the font is used.
   */
  function openIDB(fontInfo) {
    return new Promise(function(resolve, reject) {
      var request = window.indexedDB.open(
          DB_NAME_PREFIX + '/' + fontInfo.fontFamily + '/' + fontInfo.weight);

      request.onerror = function(event) {
        reject(ERROR_PRELUDE_INDEXEDDB_OPEN);
      };

      request.onsuccess = function(event) {
        resolve(event.target.result);
      };
    });
  }

  /**
   * Get data from the database.
   * @param {!IDBDatabase} db The database handle.
   * @param {string} name The store name.
   * @return {!Promise<!ArrayBuffer,?>}
   */
  function getData(db, name) {
    return new Promise(function(resolve, reject) {
      var trans;
      try {
        trans = db.transaction([name], 'readonly');
      } catch (e) {
        reject();
        return;
      }
      var store = trans.objectStore(name);
      var request = store.get(0);
      request.onsuccess = function(e) {
        if (e.target.result != undefined) {
          resolve(e.target.result);
        } else {
          reject();
        }
      };
      request.onerror = function(e) { reject(); };
    });
  }

  /**
   * Get the font data from the indexedDB.
   * @param {!FontInfo} fontInfo Info about this font.
   * @return {!Promise} If success promise resolves the header+font ArrayBuffer.
   */
  function getFontData(fontInfo) {
    return openIDB(fontInfo)
        .then(function(db) {
          return getData(db, COMPACT_META).then(
              function(metadata) {
                // Check metadata age.
                var age = START_TIME -
                    (metadata[CREATED_METADATA_TIME] || START_TIME);
                if (age < STABLE_DATA_TIME) {
                  return newRejectedPromise(ERROR_PRELUDE_BELOW_STABLE_TIME);
                }
                return db;
              },
              function() {
                return newRejectedPromise(ERROR_PRELUDE_MISSING_METADATA);
              });
        })
        .then(function(db) {
          return getData(db, COMPACT_FONT).then(undefined, function() {
            return newRejectedPromise(ERROR_PRELUDE_MISSING_BASE);
          });
        });
  }

  /**
   * Set the CSS \@font-face rule.
   *
   * @param {!CSSStyleSheet} sheet The style sheet.
   * @param {string} fontFamily The fontFamily.
   * @param {string} weight The weight.
   * @param {string} srcStr The src string for the \@font-face
   */
  function setCssFontRule(sheet, fontFamily, weight, srcStr) {
    var rule_str = '@font-face{' +
        'font-family: ' + fontFamily + ';' +
        'font-weight: ' + weight + ';' +
        'src: ' + srcStr + '' +
        '}\n';
    sheet.insertRule(rule_str, sheet.cssRules.length);
  }

  /**
   * A function that does nothing. Useful for places that need a function but
   * that function does not need to do anything.
   */
  function nullFunction() {}

  /**
   * @param {!CSSStyleSheet} sheet The style sheet.
   * @param {string} cssFontFamily The CSS font-family name.
   * @param {?DataView} fontDataView The font data.
   * @param {!FontInfo} fontInfo Info about this font.
   * @return {!Promise} The promise resolves when the glyphs are displaying.
   */
  function setFontNoFlash(sheet, cssFontFamily, fontDataView, fontInfo) {
    var weight = fontInfo.weight;

    var mimeType;
    var format;
    var srcStr;
    if (fontDataView) {
      if (fontInfo.isTtf) {
        mimeType = 'font/ttf';  // 'application/x-font-ttf';
        format = 'truetype';
      } else {
        mimeType = 'font/otf';  // 'application/font-sfnt';
        format = 'opentype';
      }
      var blob = new Blob([fontDataView], {type: mimeType});
      var blobUrl = window.URL.createObjectURL(blob);
      srcStr = 'url("' + blobUrl + '") ' + 'format("' + format + '");';
    } else {
      srcStr = 'local("sans-serif");';
    }

    // Load the font data under a CSS style sheet that is not getting used.
    var tmpFontFamily = 'tmp-' + weight + '-' + cssFontFamily;
    setCssFontRule(sheet, tmpFontFamily, weight, srcStr);
    var fontStr = '400 20px ' + tmpFontFamily;
    return document.fonts.load(fontStr)
        .then(nullFunction, nullFunction)  // Ignore loading errors.
        .then(function(value) {
          setCssFontRule(sheet, cssFontFamily, weight, srcStr);
          if (!fontDataView) {
            return;
          }
          // Record the font ready time.
          reports.push(['l', (new Date()).getTime() - START_TIME, weight]);
          var oldBlobUrl = tachyfontprelude[weight];
          if (oldBlobUrl) {
            URL.revokeObjectURL(oldBlobUrl);
          }
          tachyfontprelude['urls'][weight] = blobUrl;
          tachyfontprelude['loaded'][weight] = true;
        }, nullFunction);
  }

  /**
   * Uses a TachyFont from persistent store if available.
   * @param {string} cssFontFamily The CSS font-family name.
   * @param {!FontInfo} fontInfo The info on the font to use.
   * @return {!Promise} This promise resolves when the font is used.
   */
  function useFont(sheet, cssFontFamily, fontInfo) {
    return getFontData(fontInfo)
        .then(function(fontDataView) {
          return setFontNoFlash(sheet, cssFontFamily, fontDataView, fontInfo)
              .then(function() {
                return true;
              });
        })
        .then(undefined, function(errorNumber) {
          // Report the error.
          reports.push(['e', errorNumber, fontInfo.weight]);
          return newResolvedPromise(false);
        });
  }

  /**
   * Gets a resolved promise.
   * @param {*=} opt_value The value the promise resolves to.
   * @return {!Promise}
   */
  function newResolvedPromise(opt_value) {
    return new Promise(function(resolve) {
      resolve(opt_value);
    });
  }

  /**
   * Gets a rejected promise.
   * @param {*=} opt_value The value the promise rejects with.
   * @return {!Promise}
   */
  function newRejectedPromise(opt_value) {
    return new Promise(function(resolve, reject) { reject(opt_value); });
  }

  /**
   * Export the objects and functions. Data exports may be done elsewhere in
   * this file.
   */
  window['tachyfontprelude'] = tachyfontprelude;
  tachyfontprelude['reports'] = reports;
  tachyfontprelude['FontInfo'] = FontInfo;
  tachyfontprelude['loadFont'] = loadFont;
  tachyfontprelude['loadUrl'] = loadUrl;
})();
<commit_msg>Close the db handles in the prelude code.<commit_after>'use strict';

/**
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

(function() {

  // Declares the public namespace. This is used to make items publically
  // visible.
  var tachyfontprelude = {};

  /** @type {!Object} Indicates which fonts were loaded */
  tachyfontprelude['loaded'] = {};

  /** @type {!Object} The URL for the fonts which were loaded */
  tachyfontprelude['urls'] = {};

  /** @type {boolean} Indicates a DOM mutation has occured. */
  tachyfontprelude['DomMutationObserved'] = false;

  // Create a DOM mutation observer.
  var observer = new MutationObserver(function(mutations) {
    tachyfontprelude['DomMutationObserved'] = true;
  });
  // Watch for these mutations.
  var config = /** @type {!MutationObserverInit} */ ({ 'childList': true,
    'subtree': true, 'characterData': true });
  observer.observe(document.documentElement, config);

  /** @type {boolean} Indicates the DOM content is fully loaded. */
  tachyfontprelude['DomContentLoaded'] = false;

  // Check the DOM when it reports loading the page is done.
  document.addEventListener("DOMContentLoaded", function(event) {
    tachyfontprelude['DOMContentLoaded'] = true;
  });

  /** @const {string} The database name prefix. */
  var DB_NAME_PREFIX = 'incrfonts';

  /** @const {string} The db store name for the font base. */
  var COMPACT_FONT = 'compact_font';

  /** @const {string} The db store name for the metadata. */
  var COMPACT_META = 'compact_metadata';

  /** @const {string} The db store name for the metadata. */
  var CREATED_METADATA_TIME = 'created_metadata_time';

  /**
   * The persistence 'stable' time in milliseconds.
   * If the data was only recently created then it could be the first time the
   * client has ever used TachyFont. This should be fairly infrequent. However,
   * it is also possible that the data was recently created because the client
   * has an auto clean feature that is automatically deleting the data. This is
   * the sum of the global and per font stable times.
   * @type {number}
   */
  var STABLE_DATA_TIME = 24 * 60 * 60 * 1000;

  /** @const {string} The Style Sheet ID. */
  var STYLESHEET_ID = 'Incremental\u00A0Font\u00A0Utils';

  /** @const {string} Failed to open IndexedDB error. */
  var ERROR_PRELUDE_INDEXEDDB_OPEN = '01';

  /** @const {string} IndexedDB missing the base field error. */
  var ERROR_PRELUDE_MISSING_BASE = '02';

  /** @const {string} IndexedDB missing the metadata field error. */
  var ERROR_PRELUDE_MISSING_METADATA = '06';

  /**
   * Indicates the data is younger than the 'stable' time.
   * @const {string}
   */
  var ERROR_PRELUDE_BELOW_STABLE_TIME = '07';

  /**
   * The errors encounter while loading the Tachyfont preludes.
   * These will be reported by the TachyFont library.
   *
   * @type {!Array<!Array<string|number>>}
   */
  var reports = [];

  /** @const {number} TachyFontPrelude start time. */
  var START_TIME = (new Date()).getTime();

  /**
   * The information needed to load a font.
   * @param {string} fontFamily The font name.
   * @param {string} weight The font weight.
   * @param {boolean} priority Whether this is a priority font.
   * @param {boolean} isTtf True is TTF and false if OTF.
   * @constructor
   */
  function FontInfo(fontFamily, weight, priority, isTtf) {
    /** @type {string} The font name. */
    this.fontFamily = fontFamily;

    /** @type {string} The font weight. */
    this.weight = weight;

    /**
     * True if this is a priority font that should be prioritized ahead of other
     * fonts.
     * @type {boolean}
     */
    this.priority = priority;

    /** @type {boolean} isTtf True is TTF and false if OTF. */
    this.isTtf = isTtf;
  }

  /**
   * Loads a file using XHR.
   * @param {string} url The URL to the file.
   * @param {string} responseType The requested response type.
   * @return {!Promise<*>} A promise for the url data.
   */
  function loadUrl(url, responseType) {
    return new Promise(function(resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.responseType = responseType;
      xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
          if (xhr.status == 200) {
            resolve(xhr.response);
          } else {
            reject(xhr.statusText);
          }
        }
      };
      xhr.open('GET', url, true);
      xhr.send();
    });
  }

  /**
   * Loads the TachyFonts from persistent store if available.
   * @param {string} cssFontFamily The CSS font-family name.
   * @param {!Array<!FontInfo>} fontInfos The list of fonts to load.
   * @return {!Promise<boolean>} Resolves true if all the fonts were persisted.
   */
  function loadFont(cssFontFamily, fontInfos) {
    var style = document.getElementById(STYLESHEET_ID);
    if (!style) {
      style = document.createElement('style');
      style.id = STYLESHEET_ID;
      document.head.appendChild(style);
    }
    var sheet = style.sheet;

    var fontInfos1;
    var promises;
    return newResolvedPromise()
        .then(function() {
          // Prevent the browser creating ransom note effects by picking glyphs
          // from other weights in the family by specifying a placeholder font.
          fontInfos1 = fontInfos.slice();
          promises = [];
          for (var i = 0; i < fontInfos1.length; i++) {
            promises.push(
                setFontNoFlash(sheet, cssFontFamily, null, fontInfos1[i]));
          }
          return Promise.all(promises);
        })
        .then(function() {
          // Now load whatever fonts are already persisted.
          fontInfos1 = fontInfos.slice();
          promises = [];
          for (var i = 0; i < fontInfos1.length; i++) {
            promises.push(useFont(sheet, cssFontFamily, fontInfos1[i]));
          }
          return Promise.all(promises).then(function(loadedFonts) {
            var allLoaded = true;
            for (var i = 0; i < loadedFonts.length; i++) {
              allLoaded &= loadedFonts[i];
            }
            return allLoaded;
          });
        });
  }

  /**
   * Uses a TachyFont from persistent store if available.
   * @param {!FontInfo} fontInfo The info on the font to use.
   * @return {!Promise} This promise resolves when the font is used.
   */
  function openIDB(fontInfo) {
    return new Promise(function(resolve, reject) {
      var request = window.indexedDB.open(
          DB_NAME_PREFIX + '/' + fontInfo.fontFamily + '/' + fontInfo.weight);

      request.onerror = function(event) {
        reject(ERROR_PRELUDE_INDEXEDDB_OPEN);
      };

      request.onsuccess = function(event) {
        resolve(event.target.result);
      };
    });
  }

  /**
   * Get data from the database.
   * @param {!IDBDatabase} db The database handle.
   * @param {string} name The store name.
   * @return {!Promise<!ArrayBuffer,?>}
   */
  function getData(db, name) {
    return new Promise(function(resolve, reject) {
      var trans;
      try {
        trans = db.transaction([name], 'readonly');
      } catch (e) {
        reject();
        return;
      }
      var store = trans.objectStore(name);
      var request = store.get(0);
      request.onsuccess = function(e) {
        if (e.target.result != undefined) {
          resolve(e.target.result);
        } else {
          reject();
        }
      };
      request.onerror = function(e) { reject(); };
    });
  }

  /**
   * Get the font data from the indexedDB.
   * @param {!FontInfo} fontInfo Info about this font.
   * @return {!Promise} If success promise resolves the header+font ArrayBuffer.
   */
  function getFontData(fontInfo) {
    return openIDB(fontInfo)
        .then(function(db) {
          return getData(db, COMPACT_META)
              .then(
                  function(metadata) {
                    // Check metadata age.
                    var age = START_TIME -
                        (metadata[CREATED_METADATA_TIME] || START_TIME);
                    if (age < STABLE_DATA_TIME) {
                      db.close();
                      return newRejectedPromise(
                          ERROR_PRELUDE_BELOW_STABLE_TIME);
                    }
                    return db;
                  },
                  function() {
                    db.close();
                    return newRejectedPromise(ERROR_PRELUDE_MISSING_METADATA);
                  });
        })
        .then(function(db) {
          return getData(db, COMPACT_FONT)
              .then(
                  function(data) {
                    db.close();
                    return data;
                  },
                  function() {
                    db.close();
                    return newRejectedPromise(ERROR_PRELUDE_MISSING_BASE);
                  });
        });
  }

  /**
   * Set the CSS \@font-face rule.
   *
   * @param {!CSSStyleSheet} sheet The style sheet.
   * @param {string} fontFamily The fontFamily.
   * @param {string} weight The weight.
   * @param {string} srcStr The src string for the \@font-face
   */
  function setCssFontRule(sheet, fontFamily, weight, srcStr) {
    var rule_str = '@font-face{' +
        'font-family: ' + fontFamily + ';' +
        'font-weight: ' + weight + ';' +
        'src: ' + srcStr + '' +
        '}\n';
    sheet.insertRule(rule_str, sheet.cssRules.length);
  }

  /**
   * A function that does nothing. Useful for places that need a function but
   * that function does not need to do anything.
   */
  function nullFunction() {}

  /**
   * @param {!CSSStyleSheet} sheet The style sheet.
   * @param {string} cssFontFamily The CSS font-family name.
   * @param {?DataView} fontDataView The font data.
   * @param {!FontInfo} fontInfo Info about this font.
   * @return {!Promise} The promise resolves when the glyphs are displaying.
   */
  function setFontNoFlash(sheet, cssFontFamily, fontDataView, fontInfo) {
    var weight = fontInfo.weight;

    var mimeType;
    var format;
    var srcStr;
    if (fontDataView) {
      if (fontInfo.isTtf) {
        mimeType = 'font/ttf';  // 'application/x-font-ttf';
        format = 'truetype';
      } else {
        mimeType = 'font/otf';  // 'application/font-sfnt';
        format = 'opentype';
      }
      var blob = new Blob([fontDataView], {type: mimeType});
      var blobUrl = window.URL.createObjectURL(blob);
      srcStr = 'url("' + blobUrl + '") ' + 'format("' + format + '");';
    } else {
      srcStr = 'local("sans-serif");';
    }

    // Load the font data under a CSS style sheet that is not getting used.
    var tmpFontFamily = 'tmp-' + weight + '-' + cssFontFamily;
    setCssFontRule(sheet, tmpFontFamily, weight, srcStr);
    var fontStr = '400 20px ' + tmpFontFamily;
    return document.fonts.load(fontStr)
        .then(nullFunction, nullFunction)  // Ignore loading errors.
        .then(function(value) {
          setCssFontRule(sheet, cssFontFamily, weight, srcStr);
          if (!fontDataView) {
            return;
          }
          // Record the font ready time.
          reports.push(['l', (new Date()).getTime() - START_TIME, weight]);
          var oldBlobUrl = tachyfontprelude[weight];
          if (oldBlobUrl) {
            URL.revokeObjectURL(oldBlobUrl);
          }
          tachyfontprelude['urls'][weight] = blobUrl;
          tachyfontprelude['loaded'][weight] = true;
        }, nullFunction);
  }

  /**
   * Uses a TachyFont from persistent store if available.
   * @param {string} cssFontFamily The CSS font-family name.
   * @param {!FontInfo} fontInfo The info on the font to use.
   * @return {!Promise} This promise resolves when the font is used.
   */
  function useFont(sheet, cssFontFamily, fontInfo) {
    return getFontData(fontInfo)
        .then(function(fontDataView) {
          return setFontNoFlash(sheet, cssFontFamily, fontDataView, fontInfo)
              .then(function() {
                return true;
              });
        })
        .then(undefined, function(errorNumber) {
          // Report the error.
          reports.push(['e', errorNumber, fontInfo.weight]);
          return newResolvedPromise(false);
        });
  }

  /**
   * Gets a resolved promise.
   * @param {*=} opt_value The value the promise resolves to.
   * @return {!Promise}
   */
  function newResolvedPromise(opt_value) {
    return new Promise(function(resolve) {
      resolve(opt_value);
    });
  }

  /**
   * Gets a rejected promise.
   * @param {*=} opt_value The value the promise rejects with.
   * @return {!Promise}
   */
  function newRejectedPromise(opt_value) {
    return new Promise(function(resolve, reject) { reject(opt_value); });
  }

  /**
   * Export the objects and functions. Data exports may be done elsewhere in
   * this file.
   */
  window['tachyfontprelude'] = tachyfontprelude;
  tachyfontprelude['reports'] = reports;
  tachyfontprelude['FontInfo'] = FontInfo;
  tachyfontprelude['loadFont'] = loadFont;
  tachyfontprelude['loadUrl'] = loadUrl;
})();
<|endoftext|>


<commit_before>var log  = require('npmlog');
var http = require('http');
var Buffer  = require("buffer").Buffer;

var RemoteRoom  = require("matrix-appservice-bridge").RemoteRoom;
var MatrixRoom  = require("matrix-appservice-bridge").MatrixRoom;

var TimelineHandler = function (bridge, twitter) {
  this._bridge = bridge;
  this.twitter = twitter;
}

TimelineHandler.prototype.processInvite = function (event, request, context) {
  log.info("Handler.Timeline","Got invite for a timeline");
  if (event.state_key.startsWith("@twitter_"))
  { //We should prolly use a regex
      if (event.membership == "invite")
      {
          var intent = this._bridge.getIntent(event.state_key);
          intent.join(event.room_id);

          //Set the avatar based on the 'owners' avatar.
          intent.getClient().getProfileInfo(event.state_key, 'avatar_url').then((content) =>
          {

              if (typeof content.avatar_url != "string")
              {
                  log.error("Handler.Timeline","User", event.state_key, "does not have an avatar set. This is unexpected.");
                  console.log(content);
                  return;
              }

              log.info("Handler.Timeline","Set Room Avatar:", content.avatar_url);
              intent.sendStateEvent(event.room_id, "m.room.avatar", "",
              {
                  "url": content.avatar_url
              });
          });

          if (context.rooms.remote != null)
          {
              this.twitter.add_timeline(event.state_key, context.rooms.matrix, context.rooms.remote);
              return;
          }
          log.warn("Handler.Timeline","Couldn't find the remote room for this timeline.");
      }
      else if(event.membership == "leave"){
        log.warn("Handler.Timeline", event.sender + " left " + event.room_id);

        //var users = getRoomMembers(event.room_id);
        //for(var user of users){
        //  console.log(user);
        //}
      }
    }
}

TimelineHandler.prototype.processMessage = function (event, request, context) {
  this.twitter.send_matrix_event_as_tweet(event,context.senders.matrix,context.rooms.remote);
}

TimelineHandler.prototype.processAliasQuery = function(alias){
  //Create the room
  log.info("Handler.TimelineHandler","Looking up " + alias);
  return this.twitter.get_user(alias).then((tuser) => {
      if (tuser != null) {
          if (!tuser.protected) {
              return this._constructTimelineRoom(tuser, alias);
          }
          log.info("Handler.Timeline",tuser.screen_name + " is a protected account, so we can't read from it.");
      }
      log.info("Handler.Timeline",tuser.screen_name + " was not found.");

  });
}
/*
  This will create a stream room for one user's timeline.
  Users who are not authenticated via OAuth will receive the default power of 0
  Users who are authenticated via Oauth will receive a power level of 10
  The owner of this stream will receive a 75
  The bot will have 100
*/

TimelineHandler.prototype._constructTimelineRoom = function(user, alias) {
    console.log(user);
    var botID = this._bridge.getBot().getUserId();

    var roomOwner = "@twitter_" + user.id_str + ":" + this._bridge.opts.domain;
    var users = {};
    users[botID] = 100;
    users[roomOwner] = 75;
    var powers = roomPowers(users);
    var remote = new RemoteRoom("timeline_" + user.id_str);
    remote.set("twitter_type", "timeline");
    remote.set("twitter_user", roomOwner);
    this._bridge.getRoomStore().setRemoteRoom(remote);

    opts = {
        visibility: "public",
        room_alias_name: "twitter_@"+alias,
        name: "[Twitter] " + user.name,
        topic: user.description,
        invite: [roomOwner],
        initial_state: [
            powers, {
                "type": "m.room.join_rules",
                "content": {
                    "join_rule": "public"
                },
                "state_key": ""
            }, {
                "type": "org.matrix.twitter.data",
                "content": user,
                "state_key": ""
            }
        ]
    };
    return {
        creationOpts: opts,
        remote: remote
    };
}

//Download an image and return it's data.
TimelineHandler.prototype._downloadImage = function(url){
  return new Promise((resolve, reject) => {
      http.get((url), (res) => {
          var size = parseInt(res.headers["content-length_downloadImage"]);
          var buffer = Buffer.alloc(size);
          var bsize = 0;
          res.on('data', (d) => {
              d.copy(buffer, bsize);
              bsize += d.length;
          });
          res.on('error', () => {
              reject("Failed to download.");
          });
          res.on('end', () => {
              resolve(buffer);
          });
      })
  });
}

function roomPowers(users) {
    return {
        "content": {
            "ban": 50,
            "events": {
                "m.room.name": 100,
                "m.room.power_levels": 100,
                "m.room.topic": 100,
                "m.room.join_rules": 100,
                "m.room.avatar": 100,
                "m.room.aliases": 75,
                "m.room.canonical_alias": 75
            },
            "events_default": 10,
            "kick": 75,
            "redact": 75,
            "state_default": 0,
            "users": users,
            "users_default": 10
        },
        "state_key": "",
        "type": "m.room.power_levels"
    };
}

module.exports = {
    TimelineHandler: TimelineHandler
}
<commit_msg>Removed `_downloadImage` from TimelineHandler<commit_after>var log  = require('npmlog');
var Buffer  = require("buffer").Buffer;

var RemoteRoom  = require("matrix-appservice-bridge").RemoteRoom;
var MatrixRoom  = require("matrix-appservice-bridge").MatrixRoom;

var TimelineHandler = function (bridge, twitter) {
  this._bridge = bridge;
  this.twitter = twitter;
}

TimelineHandler.prototype.processInvite = function (event, request, context) {
  log.info("Handler.Timeline","Got invite for a timeline");
  if (event.state_key.startsWith("@twitter_"))
  { //We should prolly use a regex
      if (event.membership == "invite")
      {
          var intent = this._bridge.getIntent(event.state_key);
          intent.join(event.room_id);

          //Set the avatar based on the 'owners' avatar.
          intent.getClient().getProfileInfo(event.state_key, 'avatar_url').then((content) =>
          {

              if (typeof content.avatar_url != "string")
              {
                  log.error("Handler.Timeline","User", event.state_key, "does not have an avatar set. This is unexpected.");
                  console.log(content);
                  return;
              }

              log.info("Handler.Timeline","Set Room Avatar:", content.avatar_url);
              intent.sendStateEvent(event.room_id, "m.room.avatar", "",
              {
                  "url": content.avatar_url
              });
          });

          if (context.rooms.remote != null)
          {
              this.twitter.add_timeline(event.state_key, context.rooms.matrix, context.rooms.remote);
              return;
          }
          log.warn("Handler.Timeline","Couldn't find the remote room for this timeline.");
      }
      else if(event.membership == "leave"){
        log.warn("Handler.Timeline", event.sender + " left " + event.room_id);

        //var users = getRoomMembers(event.room_id);
        //for(var user of users){
        //  console.log(user);
        //}
      }
    }
}

TimelineHandler.prototype.processMessage = function (event, request, context) {
  this.twitter.send_matrix_event_as_tweet(event,context.senders.matrix,context.rooms.remote);
}

TimelineHandler.prototype.processAliasQuery = function(alias){
  //Create the room
  log.info("Handler.TimelineHandler","Looking up " + alias);
  return this.twitter.get_user(alias).then((tuser) => {
      if (tuser != null) {
          if (!tuser.protected) {
              return this._constructTimelineRoom(tuser, alias);
          }
          log.warn("Handler.Timeline",tuser.screen_name + " is a protected account, so we can't read from it.");
      }
      log.warn("Handler.Timeline",tuser.screen_name + " was not found.");

  }).catch(reason =>{
    log.error("Twitter","Couldn't create timeline room: ",reason);
  });
}
/*
  This will create a stream room for one user's timeline.
  Users who are not authenticated via OAuth will receive the default power of 0
  Users who are authenticated via Oauth will receive a power level of 10
  The owner of this stream will receive a 75
  The bot will have 100
*/

TimelineHandler.prototype._constructTimelineRoom = function(user, alias) {
    console.log(user);
    var botID = this._bridge.getBot().getUserId();

    var roomOwner = "@twitter_" + user.id_str + ":" + this._bridge.opts.domain;
    var users = {};
    users[botID] = 100;
    users[roomOwner] = 75;
    var powers = roomPowers(users);
    var remote = new RemoteRoom("timeline_" + user.id_str);
    remote.set("twitter_type", "timeline");
    remote.set("twitter_user", roomOwner);
    this._bridge.getRoomStore().setRemoteRoom(remote);

    opts = {
        visibility: "public",
        room_alias_name: "twitter_@"+alias,
        name: "[Twitter] " + user.name,
        topic: user.description,
        invite: [roomOwner],
        initial_state: [
            powers, {
                "type": "m.room.join_rules",
                "content": {
                    "join_rule": "public"
                },
                "state_key": ""
            }, {
                "type": "org.matrix.twitter.data",
                "content": user,
                "state_key": ""
            }
        ]
    };
    return {
        creationOpts: opts,
        remote: remote
    };
}

function roomPowers(users) {
    return {
        "content": {
            "ban": 50,
            "events": {
                "m.room.name": 100,
                "m.room.power_levels": 100,
                "m.room.topic": 100,
                "m.room.join_rules": 100,
                "m.room.avatar": 100,
                "m.room.aliases": 75,
                "m.room.canonical_alias": 75
            },
            "events_default": 10,
            "kick": 75,
            "redact": 75,
            "state_default": 0,
            "users": users,
            "users_default": 10
        },
        "state_key": "",
        "type": "m.room.power_levels"
    };
}

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


<commit_before>"use strict";

var changing = require('best-globals').changing;
var coalesce = require('best-globals').coalesce;
var typeStore = require('type-store');

const escapeStringRegexp = require('escape-string-regexp');

function completeEditablesProperties(def,defaultValue,other){
    var other = other || {};
    ['insert','delete','update','select'].forEach(function(actionName){
        if(!(actionName in def.allow)){
            def.allow[actionName] = (
                actionName!=='select' && 'editable' in def ?
                !!def.editable :
                (actionName in other?other[actionName]:defaultValue)
            );
        }
    });
    def.allow.filter = coalesce(def.allow.filter,true);
    def.allow.import = coalesce(def.allow.import,def.allow.export && def.allow.update,false);
}

function tableDefAdapt(tableDef, context){
    var be = this;
    var resultTableDef = changing({
        title: tableDef.name,
        field: {},
        allow: {select: true, orientation: false, "vertical-edit": true, export:true, import:true},
        actionNamesList: [],
        foreignKeys: [],
        filterColumns: [],
        detailTables: [],
        sql:{
            fields:{}
        },
        layout:{
            vertical:false,
        }
    },tableDef);
    completeEditablesProperties(resultTableDef, false);
    var prefixRegExp;
    if(resultTableDef.prefix){
        prefixRegExp=new RegExp('^'+escapeStringRegexp(resultTableDef.prefix)+'_');
    }else{
        prefixRegExp=/^/;
    }
    resultTableDef.nameFields = [];
    resultTableDef.fields = resultTableDef.fields.map(function(fieldDef){
        var resultFieldDef = changing({
            allow:{},
            title: fieldDef.name.replace(prefixRegExp,'').replace(/_/g, ' '),
        }, fieldDef);
        typeStore.completeTypeInfo(resultFieldDef);
        resultFieldDef.label = resultFieldDef.label||resultFieldDef.title;
        completeEditablesProperties(resultFieldDef, !!resultTableDef.editableFieldDef, resultTableDef.allow);
        if(resultTableDef.allow.insert){
            resultFieldDef.allow.insert=true;
        }
        if(fieldDef.typeName==='enum'){
            resultFieldDef.options = resultFieldDef.options.map(function(option){
                if(typeof option !== 'object'){
                    return {option:option, label:option};
                }else{
                    return option;
                }
            });
        }
        return resultFieldDef;
    }).filter(function(fieldDef){
        return fieldDef.allow.select;
    }).map(function(fieldDef){
        resultTableDef.field[fieldDef.name]=fieldDef;
        if(fieldDef.isName){
            resultTableDef.nameFields.push(fieldDef.name);
        }
        return fieldDef;
    });
    (resultTableDef.primaryKey||[]).forEach(function(fieldName, i){
        resultTableDef.field[fieldName].isPk=i+1;
    });
    resultTableDef.foreignKeys.map(function(fk){
        fk.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.detailTables.map(function(detail){
        detail.label=detail.label||detail.table;
        detail.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.alias = resultTableDef.alias || resultTableDef.name;
    resultTableDef.sql.select = resultTableDef.sql.select || resultTableDef.fields.map(function(fieldDef){
        var resultName = be.db.quoteObject(fieldDef.name)
        var exprAs='';
        if(fieldDef.clientSide && !fieldDef.inTable){
            exprAs="null::text as ";
        }else if((resultTableDef.sql.fields[fieldDef.name]||{}).expr){
            exprAs=resultTableDef.sql.fields[fieldDef.name].expr+' as ';
        }else if(resultTableDef.foreignKeys.length){
            resultName=be.db.quoteObject(resultTableDef.alias)+'.'+resultName;
        }
        return exprAs+resultName;
    });
    resultTableDef.sql.isTable = coalesce(resultTableDef.sql.isTable, !resultTableDef.sql.from);
    //console.log('xxxxxxxxxxxxxxxxxxxx');
    //console.log(be.db.quoteObject(resultTableDef.alias));
    //console.log(resultTableDef.sql.from);
    resultTableDef.sql.from = (resultTableDef.sql.from || be.db.quoteObject(resultTableDef.name));
    if(resultTableDef.sql.from.endsWith(' x')){
        console.log('hay que quitar el " as x"')
        throw Error('tableDefAdapt from ends with x "'+resultTableDef.name+'"');
    }
    if(be.db.quoteObject(resultTableDef.alias)!==resultTableDef.sql.from){
        resultTableDef.sql.from += ' as ' + be.db.quoteObject(resultTableDef.alias);
    }
    if(context && !context.plainStructure){
        resultTableDef.foreignKeys.forEach(function(fkDef){
            var fkTableDef = be.tableStructures[fkDef.references];
            if(!fkTableDef){
                throw new Error('tableDefAdapt: table "'+fkDef.references+'" must be declared before "'+resultTableDef.name+'"');
            }
            fkTableDef=fkTableDef(changing(context,{plainStructure: true}));
            var lastSourceField=null;
            resultTableDef.sql.from += '\n    left join ' + fkTableDef.sql.from + ' on ' +
                fkDef.fields.map(function(pair){
                    lastSourceField = pair.source;
                    return be.db.quoteObject(resultTableDef.alias)+'.'+be.db.quoteObject(pair.source)
                        + ' = ' + be.db.quoteObject(fkTableDef.alias)+'.'+be.db.quoteObject(pair.target);
                }).join(' AND ');
            var iLastSourceField = resultTableDef.fields.findIndex(function(fieldDef){ return fieldDef.name == lastSourceField });
            fkTableDef.nameFields.forEach(function(fieldName){
                var fieldNameAdded=fkTableDef.alias+'_'+fieldName;
                var fieldDefAdded=changing(fkTableDef.field[fieldName], {
                    name:fieldNameAdded,
                    editable:false,
                    allow:{update:false, insert:false},
                    references:fkDef.references
                });
                resultTableDef.field[fieldNameAdded]=fieldDefAdded;
                iLastSourceField++;
                resultTableDef.fields.splice(iLastSourceField,0,fieldDefAdded);
                resultTableDef.sql.select.push(
                    be.db.quoteObject(fkTableDef.alias)+'.'+be.db.quoteObject(fieldName)+' as '+
                    be.db.quoteObject(fkTableDef.alias+'_'+fieldName)
                );
            });
        });
    }else{
        if(resultTableDef.foreignKeys.length){
            console.log("ATENCIÓN. Conviene pasar context como segundo parámetro a tableDefAdapt para que tome las FK en tabla "+resultTableDef.name)
        }
    }
    resultTableDef.sql.postCreateSqls = resultTableDef.sql.postCreateSqls || '';
    return resultTableDef;
}

module.exports = tableDefAdapt;<commit_msg>Permito definir subclass<commit_after>"use strict";

var changing = require('best-globals').changing;
var coalesce = require('best-globals').coalesce;
var typeStore = require('type-store');

const escapeStringRegexp = require('escape-string-regexp');

function completeEditablesProperties(def,defaultValue,other){
    var other = other || {};
    ['insert','delete','update','select'].forEach(function(actionName){
        if(!(actionName in def.allow)){
            def.allow[actionName] = (
                actionName!=='select' && 'editable' in def ?
                !!def.editable :
                (actionName in other?other[actionName]:defaultValue)
            );
        }
    });
    def.allow.filter = coalesce(def.allow.filter,true);
    def.allow.import = coalesce(def.allow.import,def.allow.export && def.allow.update,false);
}

function tableDefAdapt(tableDef, context){
    var be = this;
    var resultTableDef = changing({
        title: tableDef.name,
        field: {},
        allow: {select: true, orientation: false, "vertical-edit": true, export:true, import:true},
        actionNamesList: [],
        foreignKeys: [],
        filterColumns: [],
        detailTables: [],
        sql:{
            fields:{}
        },
        layout:{
            vertical:false,
        }
    },tableDef);
    completeEditablesProperties(resultTableDef, false);
    var prefixRegExp;
    if(resultTableDef.prefix){
        prefixRegExp=new RegExp('^'+escapeStringRegexp(resultTableDef.prefix)+'_');
    }else{
        prefixRegExp=/^/;
    }
    resultTableDef.nameFields = [];
    resultTableDef.fields = resultTableDef.fields.map(function(fieldDef){
        var resultFieldDef = changing({
            allow:{},
            title: fieldDef.name.replace(prefixRegExp,'').replace(/_/g, ' '),
        }, fieldDef);
        typeStore.completeTypeInfo(resultFieldDef);
        resultFieldDef.label = resultFieldDef.label||resultFieldDef.title;
        completeEditablesProperties(resultFieldDef, !!resultTableDef.editableFieldDef, resultTableDef.allow);
        if(resultTableDef.allow.insert){
            resultFieldDef.allow.insert=true;
        }
        if(fieldDef.typeName==='enum'){
            resultFieldDef.options = resultFieldDef.options.map(function(option){
                if(typeof option !== 'object'){
                    return {option:option, label:option};
                }else{
                    return option;
                }
            });
        }
        return resultFieldDef;
    }).filter(function(fieldDef){
        return fieldDef.allow.select;
    }).map(function(fieldDef){
        resultTableDef.field[fieldDef.name]=fieldDef;
        if(fieldDef.isName){
            resultTableDef.nameFields.push(fieldDef.name);
        }
        return fieldDef;
    });
    (resultTableDef.primaryKey||[]).forEach(function(fieldName, i){
        resultTableDef.field[fieldName].isPk=i+1;
    });
    resultTableDef.foreignKeys.map(function(fk){
        fk.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.detailTables.map(function(detail){
        detail.label=detail.label||detail.table;
        detail.fields.forEach(function(pair, i, pairs){
            if(typeof pair === "string"){
                pairs[i] = {source: pair, target: pair};
            }
        });
    });
    resultTableDef.alias = resultTableDef.alias || resultTableDef.name;
    resultTableDef.sql.select = resultTableDef.sql.select || resultTableDef.fields.map(function(fieldDef){
        var resultName = be.db.quoteObject(fieldDef.name)
        var exprAs='';
        if(fieldDef.clientSide && !fieldDef.inTable){
            exprAs="null::text as ";
        }else if((resultTableDef.sql.fields[fieldDef.name]||{}).expr){
            exprAs=resultTableDef.sql.fields[fieldDef.name].expr+' as ';
        }else if(resultTableDef.foreignKeys.length){
            resultName=be.db.quoteObject(resultTableDef.alias)+'.'+resultName;
        }
        return exprAs+resultName;
    });
    resultTableDef.sql.isTable = coalesce(resultTableDef.sql.isTable, !resultTableDef.sql.from);
    //console.log('xxxxxxxxxxxxxxxxxxxx');
    //console.log(be.db.quoteObject(resultTableDef.alias));
    //console.log(resultTableDef.sql.from);
    resultTableDef.sql.from = (resultTableDef.sql.from || be.db.quoteObject(resultTableDef.name));
    if(resultTableDef.sql.from.endsWith(' x')){
        console.log('hay que quitar el " as x"')
        throw Error('tableDefAdapt from ends with x "'+resultTableDef.name+'"');
    }
    if(be.db.quoteObject(resultTableDef.alias)!==resultTableDef.sql.from){
        resultTableDef.sql.from += ' as ' + be.db.quoteObject(resultTableDef.alias);
    }
    if(context && !context.plainStructure){
        resultTableDef.foreignKeys.forEach(function(fkDef){
            if(fkDef.definingSubclass){
                return;
            }
            var fkTableDef = be.tableStructures[fkDef.references];
            if(!fkTableDef){
                throw new Error('tableDefAdapt: table "'+fkDef.references+'" must be declared before "'+resultTableDef.name+'"');
            }
            fkTableDef=fkTableDef(changing(context,{plainStructure: true}));
            var lastSourceField=null;
            resultTableDef.sql.from += '\n    left join ' + fkTableDef.sql.from + ' on ' +
                fkDef.fields.map(function(pair){
                    lastSourceField = pair.source;
                    return be.db.quoteObject(resultTableDef.alias)+'.'+be.db.quoteObject(pair.source)
                        + ' = ' + be.db.quoteObject(fkTableDef.alias)+'.'+be.db.quoteObject(pair.target);
                }).join(' AND ');
            var iLastSourceField = resultTableDef.fields.findIndex(function(fieldDef){ return fieldDef.name == lastSourceField });
            fkTableDef.nameFields.forEach(function(fieldName){
                var fieldNameAdded=fkTableDef.alias+'_'+fieldName;
                var fieldDefAdded=changing(fkTableDef.field[fieldName], {
                    name:fieldNameAdded,
                    editable:false,
                    allow:{update:false, insert:false},
                    references:fkDef.references
                });
                resultTableDef.field[fieldNameAdded]=fieldDefAdded;
                iLastSourceField++;
                resultTableDef.fields.splice(iLastSourceField,0,fieldDefAdded);
                resultTableDef.sql.select.push(
                    be.db.quoteObject(fkTableDef.alias)+'.'+be.db.quoteObject(fieldName)+' as '+
                    be.db.quoteObject(fkTableDef.alias+'_'+fieldName)
                );
            });
        });
    }else{
        if(resultTableDef.foreignKeys.length){
            console.log("ATENCIÓN. Conviene pasar context como segundo parámetro a tableDefAdapt para que tome las FK en tabla "+resultTableDef.name)
        }
    }
    resultTableDef.sql.postCreateSqls = resultTableDef.sql.postCreateSqls || '';
    return resultTableDef;
}

module.exports = tableDefAdapt;<|endoftext|>


<commit_before>function urlContainsSignature(input) {
  var potentialSignature = input.substring(input.lastIndexOf('/') + 1);
  potentialSignature = potentialSignature.toLowerCase();
  return potentialSignature.includes('se') && potentialSignature.includes('st') && potentialSignature.includes('sp') && potentialSignature.includes.('sv') && potentialSignature.includes('sr') && potentialSignature.includes('sig');
}

function separateSignatureAndURL(url) {
  var potentialSignature = input.substring(input.lastIndexOf('/') + 1);
  var potentialURL = input.substring(0, input.lastIndexOf('/'));
  return {potentialURL, potentialSignature};
}

function convertToDexecureURL(url) {
  var listOfJoyDomains = {'dev-media.withjoy.com':'dev-media-joy.dexecure.net',
                          'rehearsal-media.withjoy.com': 'rehearsal-joy.dexecure.net',
                          'dev.withjoy.com': 'dev-joy.dexecure.net',
                          'rehearsal.withjoy.com': 'rehearsal-joy.dexecure.net',
                          'ceremony-media.withjoy.com': 'ceremony-media-joy.dexecure.net',
                          'withjoy.com': 'joy.dexecure.net'
                          };
  var parsedURL = new URL(url);
  for (i in listOfJoyDomains) {
    if (i.toLowerCase() == parsedURL.host.toLowerCase()) {
      return url.replace(i, listOfJoyDomains[i]);
    }
  }
  return url;
}

function isFirstPartyDomain(url) {
  var parsedURL =  new URL(url);
  var firstPartyDomain = dexecure.firstPartyDomain;
  for (var i = firstPartyDomain.length - 1; i >= 0; i--) {
    if (firstPartyDomain[i].toLowerCase() == parsedURL.host.toLowerCase()) {
      return true;
    }
  }
  return false;
}

if (dexecure.optimisationsEnabled) {
  self.addEventListener('install', function(event) {
    event.waitUntil(self.skipWaiting());
  });
  self.addEventListener('activate', function(event) {
    event.waitUntil(self.clients.claim());
  });

  self.addEventListener('fetch', function(event) {
    var headersToSendJS = {};
    if (event.request.headers.has('Accept')) {
      headersToSendJS['Accept'] = event.request.headers.get('Accept');
    }

    var imageMatchRegex = new RegExp(dexecure.imageMatchRegex, "i");
    if (imageMatchRegex.test(event.request.url.toLowerCase()) && isFirstPartyDomain(event.request.url)) {
     var dexecureURL = convertToDexecureURL(event.request.url);
     var signatureSeparator = '';
     var signatureComponent = '';
     dexecureURL = decodeURIComponent(dexecureURL);
     if (urlContainsSignature(dexecureURL)) {
      var separatedComponents = separateSignatureAndURL(dexecureURL);
      dexecureURL = separatedComponents[0];
      signatureComponent = separatedComponents[1];
     }

     if (signatureComponent.length > 0) {
      headersToSendJS['signaturepresent'] = 'true';
      headersToSendJS['signatureseparator'] = '/';
      headersToSendJS['signature'] = signatureComponent;
     }

     var headersToSend = new Headers(headersToSendJS);
     event.respondWith(fetch(dexecureURL, {mode: 'cors', headers: headersToSend})
      .then(response => {
        if (response.ok) {
          return response;
        } else {
          if (dexecure.debugMode)
            console.log('Responding with original image as optimiser was not reachable ', event.request.url);
          throw new Error('Unable to fetch optimised image');
        }
      })
      .catch(err => {
        if (dexecure.debugMode) {
          console.log('Sending original image as an error occured when trying to optimise ', event.request.url);
          console.log('The error was ', err);
        }
        return fetch(event.request);
      }));
   }
  })
}<commit_msg>remove stray dot and change url to localhost for temp testing<commit_after>function urlContainsSignature(input) {
  var potentialSignature = input.substring(input.lastIndexOf('/') + 1);
  potentialSignature = potentialSignature.toLowerCase();
  return potentialSignature.includes('se') && potentialSignature.includes('st') && potentialSignature.includes('sp') && potentialSignature.includes('sv') && potentialSignature.includes('sr') && potentialSignature.includes('sig');
}

function separateSignatureAndURL(url) {
  var potentialSignature = input.substring(input.lastIndexOf('/') + 1);
  var potentialURL = input.substring(0, input.lastIndexOf('/'));
  return {potentialURL, potentialSignature};
}

function convertToDexecureURL(url) {
  var listOfJoyDomains = {'dev-media.withjoy.com':'dev-media-joy.dexecure.net',
                          'rehearsal-media.withjoy.com': 'rehearsal-joy.dexecure.net',
                          'dev.withjoy.com': 'dev-joy.dexecure.net',
                          'rehearsal.withjoy.com': 'rehearsal-joy.dexecure.net',
                          'ceremony-media.withjoy.com': 'localhost:44444',
                          'withjoy.com': 'joy.dexecure.net'
                          };
  var parsedURL = new URL(url);
  for (i in listOfJoyDomains) {
    if (i.toLowerCase() == parsedURL.host.toLowerCase()) {
      return url.replace(i, listOfJoyDomains[i]);
    }
  }
  return url;
}

function isFirstPartyDomain(url) {
  var parsedURL =  new URL(url);
  var firstPartyDomain = dexecure.firstPartyDomain;
  for (var i = firstPartyDomain.length - 1; i >= 0; i--) {
    if (firstPartyDomain[i].toLowerCase() == parsedURL.host.toLowerCase()) {
      return true;
    }
  }
  return false;
}

if (dexecure.optimisationsEnabled) {
  self.addEventListener('install', function(event) {
    event.waitUntil(self.skipWaiting());
  });
  self.addEventListener('activate', function(event) {
    event.waitUntil(self.clients.claim());
  });

  self.addEventListener('fetch', function(event) {
    var headersToSendJS = {};
    if (event.request.headers.has('Accept')) {
      headersToSendJS['Accept'] = event.request.headers.get('Accept');
    }

    var imageMatchRegex = new RegExp(dexecure.imageMatchRegex, "i");
    if (imageMatchRegex.test(event.request.url.toLowerCase()) && isFirstPartyDomain(event.request.url)) {
     var dexecureURL = convertToDexecureURL(event.request.url);
     var signatureSeparator = '';
     var signatureComponent = '';
     dexecureURL = decodeURIComponent(dexecureURL);
     if (urlContainsSignature(dexecureURL)) {
      var separatedComponents = separateSignatureAndURL(dexecureURL);
      dexecureURL = separatedComponents[0];
      signatureComponent = separatedComponents[1];
     }

     if (signatureComponent.length > 0) {
      headersToSendJS['signaturepresent'] = 'true';
      headersToSendJS['signatureseparator'] = '/';
      headersToSendJS['signature'] = signatureComponent;
     }

     var headersToSend = new Headers(headersToSendJS);
     event.respondWith(fetch(dexecureURL, {mode: 'cors', headers: headersToSend})
      .then(response => {
        if (response.ok) {
          return response;
        } else {
          if (dexecure.debugMode)
            console.log('Responding with original image as optimiser was not reachable ', event.request.url);
          throw new Error('Unable to fetch optimised image');
        }
      })
      .catch(err => {
        if (dexecure.debugMode) {
          console.log('Sending original image as an error occured when trying to optimise ', event.request.url);
          console.log('The error was ', err);
        }
        return fetch(event.request);
      }));
   }
  })
}<|endoftext|>


<commit_before>FaultedEarth = Ext.extend(gxp.Viewer, {

    legendTabTitle: "Legend",
	summaryId: null,

    constructor: function(config) {

        Ext.Window.prototype.shadow = false;

        // property names for FeatureEditor and FeatureGrid
        var propertyNames = {
            // custom fied names for the fault summary table
            "fault_name": "Fault Name",
            "sec_name": "Fault Section Name",
            "episodi_is": "Episodic behaviour (yes/no)",
            "episodi_ac": "Episodic behaviour (active/inactive)",
            "length": "Length (km, pref, min, max)",
            "u_sm_d_min": "Upper seismogenic depth min (km)",
            "u_sm_d_max": "Upper seismogenic depth max (km)",
            "u_sm_d_pre": "Upper seismogenic depth pref (km)",
            "u_sm_d_com": "Upper seismogenic completeness (1, 2, 3, or 4)",
            "low_d_min": "Lower seismogenic depth min (km)",
            "low_d_max": "Lower seismogenic depth max (km)",
            "low_d_pref": "Lower seismogenic depth pref (km)",
            "low_d_com": "Lower seismogenic completeness (1, 2, 3, or 4)",
            "strike": "Strike (...\u00B0)",
            "dip_min": "Dip min (...\u00B0)",
            "dip_max": "Dip max (...\u00B0)",
            "dip_pref": "Dip pref (...\u00B0)",
            "dip_com": "Dip data completeness factor (1, 2, 3, or 4)",
            "dip_dir": "Dip direction (...\u00B0)",
            "down_thro": "Downthrown side (N, S, W, E or NW etc.)",
            "slip_typ": "Slip type (Reverse etc.)",
            "slip_com": "Slip type completeness (1, 2, 3, or 4)",
            "slip_r_min": "Slip rate min (mm/yr)",
            "slip_r_max": "Slip rate max (mm/yr)",
            "slip_r_pre": "Slip rate pref (mm/yr)",
            "slip_r_com": "Slip rate completeness (1, 2, 3, or 4)",
            "aseis_slip": "Aseismic-slip factor (0-1)",
            "aseis_com": "Aseismic-slip completeness (1, 2, 3, or 4)",
            "dis_min": "Displacement min (m)",
            "dis_max": "Displacement max (m)",
            "dis_pref": "Displacement pref (m)",
            "re_int_min": "Recurrence interval min (yr)",
            "re_int_max": "Recurrence interval max (yr)",
            "re_int_pre": "Recurrence interval pref (yr)",
            "re_int_com": "Recurrence interval common (yr)",
            "mov_min": "Age of last movement min (yr BP)",
            "mov_max": "Age of last movement max (yr BP)",
            "mov_pref": "Age of last movement pref (yr BP)",
            "all_com": "Overall data completeness (1, 2, 3, or 4)",
            "created": "Date created (date)",
            "compiler": "Compiled by (name)",
            "contrib": "Contributed by (name)",
            // custom fied names for the observations table
            "observationType": "Observation Type",
            "slipType": "Slip Type",
            "hv_ratio": "H:V Ratio",
            "rake": "Rake (deg)",
            "net_slip_rate_min": "Net Slip Rate Min (mm/yr)",
            "net_slip_rate_max": "Net Slip Rate Max (mm/yr)",
            "net_slip_rate_pref": "Net Slip Rate Pref (mm/yr)",
            "dip_slip_rate_min": "Dip Slip Rate Min (mm/yr)",
            "dip_slip_rate_max": "Dip Slip Rate Max (mm/yr)",
            "dip_slip_rate_pref": "Dip Slip Rate Pref (mm/yr)",
            "marker_age": "Marker Age (yrs BP)",
            "slip_rate_category": "Slip Rate Category",
            "strike_slip_rate_min": "Strike Slip Rate Min (mm/yr)",
            "strike_slip_rate_max": "Strike Slip Rate Max (mm/yr)",
            "strike_slip_rate_pref": "Strike Slip Rate Pref (mm/yr)",
            "vertical_slip_rate_min": "Vertical Slip Rate Min (mm/yr)",
            "vertical_slip_rate_max": "Vertical Slip Rate Max (mm/yr)",
            "vertical_slip_rate_pref": "Vertical Slip Rate Pref (mm/yr)",
            "site": "Site",
            "notes": "Notes",
            "summary_id": "Fault Section Summary ID",
            // custom fied names for fault trace form
            "section_name": "Fault Section Name",
            "loc_meth": "Location Method",
            "scale": "Scale",
            "accuracy": "Accuracy",
            "geomor_exp": "Geomorphic Expression",
            "notes": "Notes",
            "fault_section_id": "Fault Section Id",
            "t_feature": "Trace Feature",
	        "s_feature": "Site Feature",
	        // custom field names for fault source form
    	    "source_nm": "Fault Source Name",
    	    "width": "Width",
    	    "area": "Area",
    	    "rake_min": "Rake Min",
    	    "rake_max": "Rake Max",
    	    "rake_pref": "Rake Pref",
    	    "rake_com": "Rake Common",
    	    "magnitude": "Magnitude",
    	    "length_min": "Length Min",
    	    "length_max": "Length Max",
    	    "length_pre": "Length Pref"
        };

        Ext.applyIf(config, {
            proxy: "/proxy?url=",

            mapItems: [{
                xtype: "gx_zoomslider",
                vertical: true,
                height: 100
            }, {
                xtype: "gxp_scaleoverlay"
            }],
            portalItems: [{
                region: "center",
                layout: "border",
                tbar: {
                    id: "paneltbar",
                    items: ["-"]
                },
                items: [{
                    id: "west",
                    region: "west",
                    layout: "accordion",
                    width: 280,
                    split: true,
                    collapsible: true,
                    collapseMode: "mini",
                    header: false,
                    border: false,
                    defaults: {
                       hideBorders: true,
                       autoScroll: true

                    },
                    items: [{
                        id: "tree",
                        title: "Layers"
                    }, {
                        id: 'trace',
                        title: "Trace Form",
                        padding: 10
                    }, {
                		id: 'summary',
                		title: "Neotectonic Section Summary",
                		padding: 10
                	}, {
                        id: 'site',
                        title: "Site Observation Form",
                        padding: 10
                    },/* {
                        id: 'observations',
                        title: "Observations",
                        layout: "fit",
                        autoScroll: false
                    },*/ {
                		id: "fault",
                		title: "Neotectonic Fault Form"
                    }, {
                		id: "simple_geometry",
                		title: "Simple Fault Geometry"
                    }, {
                    	id: "source",
                    	title: "Fault Source"
                    }]
                },
		"map", {
                    id: "featuregrid",
                    layout: "fit",
                    region: "south",
                    border: false,
                    height: 200,
                    split: true,
                    collapseMode: "mini",
                }]
            }],

            tools: [{
                actionTarget: {target: "paneltbar", index: 0},
                outputAction: 0,
                outputConfig: {
                    title: "Login",
                    width: 900,
                    height: 500,
                    modal: true,
                    bodyCfg: {
                        tag: "iframe",
                        src: "http://178.79.185.190/",
                        style: {border: 0}
                    }
                },
                actions: [{
                    iconCls: "icon-geoexplorer",
                    text: "Login",
                }]
            },{
                ptype: "gxp_layertree",
                outputTarget: "tree",
            }, {
                ptype: "gxp_featuremanager",
                id: "featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "trace_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "summary_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "site_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "fault_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "simpleFault_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "source_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuregrid",
                alwaysDisplayOnMap: true,
                selectOnMap: true,
                displayMode: "selected",
                featureManager: "featuremanager",
                outputTarget: "featuregrid",
                outputConfig: {
                    id: "grid",
                    propertyNames: propertyNames
                },
                controlOptions: {
                    multiple: true,
                }
            }, {
                ptype: "app_traceform",
                id: "traceform",
                featureManager: "trace_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "trace"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "trace_featuremanager",
                actionTarget: "traceform_tooltarget",
                autoLoadFeatures: true,
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_summaryform",
                id: "summaryform",
                featureManager: "summary_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "summary"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "summary_featuremanager",
                modifyOnly: true,
                actionTarget: "summaryform_tooltarget",
                //createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
				snappingAgent: "snapping-agent",
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_siteform",
                id: "siteform",
                featureManager: "site_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "site"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "site_featuremanager",
                actionTarget: "siteform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
                snappingAgent: "snapping-agent",
                autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_faultform",
                id: "faultform",
                featureManager: "fault_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "fault"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "fault_featuremanager",
                actionTarget: "faultform_tooltarget",
                modifyOnly: true,
                //createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
		        autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_simplegeometryform",
                id: "simplegeometryform",
                featureManager: "simpleFault_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "simple_geometry"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "simpleFault_featuremanager",
                actionTarget: "simplegeometryform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
		        autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_sourceform",
                id: "sourceform",
                featureManager: "source_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "source"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "source_featuremanager",
                actionTarget: "sourceform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
		        autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
		         ptype: "gxp_wmsgetfeatureinfo",
	             outputConfig: {
	                 width: 400
	                 }
	         },/*{
        		ptype: "app_observations",
        		featureManager: "featuremanager",
        		outputTarget: "observations"
        	    },*/{
        		ptype: "gxp_legend",
        		outputTarget: "west",
        		outputConfig: {
        		    title: this.legendTabTitle,
        		    autoScroll: true
        		}
        	}, {
            	ptype: "gxp_measure",
            	actionTarget: {target: "map.tbar", index: 6},
            	toggleGroup: "main"
            }, {
            	ptype: "gxp_zoomtoextent",
            	actionTarget: "map.tbar"
            }, {
            	ptype: "gxp_zoom",
            	actionTarget: "map.tbar"
            }, {
            	ptype: "gxp_navigationhistory",
            	actionTarget: "map.tbar"
            }, {
                ptype: "gxp_zoomtoselectedfeatures",
                featureManager: "featuremanager",
                actionTarget: "map.tbar",
                tooltip: "Zoom to selected closure"
            }, {
                ptype: "gxp_selectedfeatureactions",
                featureManager: "featuremanager",
                actions: [{
                    menuText: "Feature context demo",
                    text: "Feature context demo",
                    urlTemplate: "/geoserver/wms/reflect?layers={layer}&width=377&height=328&format=application/openlayers&featureid={fid}"
                }],
                actionTarget: ["grid.contextMenu", "grid.bbar"],
                outputConfig: {
                    width: 410,
                    height: 410
                }
            },{
                ptype: "gxp_googlegeocoder",
                outputTarget: "paneltbar",
                outputConfig: {
                    emptyText: "Search for a location ..."
                }
            }, {
            	ptype: "gxp_snappingagent",
            	id: "snapping-agent",
            	targets: [{
            		source: "local",
            		name: "geonode:trace"
            	}]
    	     }]
        });

        FaultedEarth.superclass.constructor.apply(this, arguments);
    }

});
<commit_msg>testing that each new FM needs its own feature editor<commit_after>FaultedEarth = Ext.extend(gxp.Viewer, {

    legendTabTitle: "Legend",
	summaryId: null,

    constructor: function(config) {

        Ext.Window.prototype.shadow = false;

        // property names for FeatureEditor and FeatureGrid
        var propertyNames = {
            // custom fied names for the fault summary table
            "fault_name": "Fault Name",
            "sec_name": "Fault Section Name",
            "episodi_is": "Episodic behaviour (yes/no)",
            "episodi_ac": "Episodic behaviour (active/inactive)",
            "length": "Length (km, pref, min, max)",
            "u_sm_d_min": "Upper seismogenic depth min (km)",
            "u_sm_d_max": "Upper seismogenic depth max (km)",
            "u_sm_d_pre": "Upper seismogenic depth pref (km)",
            "u_sm_d_com": "Upper seismogenic completeness (1, 2, 3, or 4)",
            "low_d_min": "Lower seismogenic depth min (km)",
            "low_d_max": "Lower seismogenic depth max (km)",
            "low_d_pref": "Lower seismogenic depth pref (km)",
            "low_d_com": "Lower seismogenic completeness (1, 2, 3, or 4)",
            "strike": "Strike (...\u00B0)",
            "dip_min": "Dip min (...\u00B0)",
            "dip_max": "Dip max (...\u00B0)",
            "dip_pref": "Dip pref (...\u00B0)",
            "dip_com": "Dip data completeness factor (1, 2, 3, or 4)",
            "dip_dir": "Dip direction (...\u00B0)",
            "down_thro": "Downthrown side (N, S, W, E or NW etc.)",
            "slip_typ": "Slip type (Reverse etc.)",
            "slip_com": "Slip type completeness (1, 2, 3, or 4)",
            "slip_r_min": "Slip rate min (mm/yr)",
            "slip_r_max": "Slip rate max (mm/yr)",
            "slip_r_pre": "Slip rate pref (mm/yr)",
            "slip_r_com": "Slip rate completeness (1, 2, 3, or 4)",
            "aseis_slip": "Aseismic-slip factor (0-1)",
            "aseis_com": "Aseismic-slip completeness (1, 2, 3, or 4)",
            "dis_min": "Displacement min (m)",
            "dis_max": "Displacement max (m)",
            "dis_pref": "Displacement pref (m)",
            "re_int_min": "Recurrence interval min (yr)",
            "re_int_max": "Recurrence interval max (yr)",
            "re_int_pre": "Recurrence interval pref (yr)",
            "re_int_com": "Recurrence interval common (yr)",
            "mov_min": "Age of last movement min (yr BP)",
            "mov_max": "Age of last movement max (yr BP)",
            "mov_pref": "Age of last movement pref (yr BP)",
            "all_com": "Overall data completeness (1, 2, 3, or 4)",
            "created": "Date created (date)",
            "compiler": "Compiled by (name)",
            "contrib": "Contributed by (name)",
            // custom fied names for the observations table
            "observationType": "Observation Type",
            "slipType": "Slip Type",
            "hv_ratio": "H:V Ratio",
            "rake": "Rake (deg)",
            "net_slip_rate_min": "Net Slip Rate Min (mm/yr)",
            "net_slip_rate_max": "Net Slip Rate Max (mm/yr)",
            "net_slip_rate_pref": "Net Slip Rate Pref (mm/yr)",
            "dip_slip_rate_min": "Dip Slip Rate Min (mm/yr)",
            "dip_slip_rate_max": "Dip Slip Rate Max (mm/yr)",
            "dip_slip_rate_pref": "Dip Slip Rate Pref (mm/yr)",
            "marker_age": "Marker Age (yrs BP)",
            "slip_rate_category": "Slip Rate Category",
            "strike_slip_rate_min": "Strike Slip Rate Min (mm/yr)",
            "strike_slip_rate_max": "Strike Slip Rate Max (mm/yr)",
            "strike_slip_rate_pref": "Strike Slip Rate Pref (mm/yr)",
            "vertical_slip_rate_min": "Vertical Slip Rate Min (mm/yr)",
            "vertical_slip_rate_max": "Vertical Slip Rate Max (mm/yr)",
            "vertical_slip_rate_pref": "Vertical Slip Rate Pref (mm/yr)",
            "site": "Site",
            "notes": "Notes",
            "summary_id": "Fault Section Summary ID",
            // custom fied names for fault trace form
            "section_name": "Fault Section Name",
            "loc_meth": "Location Method",
            "scale": "Scale",
            "accuracy": "Accuracy",
            "geomor_exp": "Geomorphic Expression",
            "notes": "Notes",
            "fault_section_id": "Fault Section Id",
            "t_feature": "Trace Feature",
	        "s_feature": "Site Feature",
	        // custom field names for fault source form
    	    "source_nm": "Fault Source Name",
    	    "width": "Width",
    	    "area": "Area",
    	    "rake_min": "Rake Min",
    	    "rake_max": "Rake Max",
    	    "rake_pref": "Rake Pref",
    	    "rake_com": "Rake Common",
    	    "magnitude": "Magnitude",
    	    "length_min": "Length Min",
    	    "length_max": "Length Max",
    	    "length_pre": "Length Pref"
        };

        Ext.applyIf(config, {
            proxy: "/proxy?url=",

            mapItems: [{
                xtype: "gx_zoomslider",
                vertical: true,
                height: 100
            }, {
                xtype: "gxp_scaleoverlay"
            }],
            portalItems: [{
                region: "center",
                layout: "border",
                tbar: {
                    id: "paneltbar",
                    items: ["-"]
                },
                items: [{
                    id: "west",
                    region: "west",
                    layout: "accordion",
                    width: 280,
                    split: true,
                    collapsible: true,
                    collapseMode: "mini",
                    header: false,
                    border: false,
                    defaults: {
                       hideBorders: true,
                       autoScroll: true

                    },
                    items: [{
                        id: "tree",
                        title: "Layers"
                    }, {
                        id: 'trace',
                        title: "Trace Form",
                        padding: 10
                    }, {
                		id: 'summary',
                		title: "Neotectonic Section Summary",
                		padding: 10
                	}, {
                        id: 'site',
                        title: "Site Observation Form",
                        padding: 10
                    },/* {
                        id: 'observations',
                        title: "Observations",
                        layout: "fit",
                        autoScroll: false
                    },*/ {
                		id: "fault",
                		title: "Neotectonic Fault Form"
                    }, {
                		id: "simple_geometry",
                		title: "Simple Fault Geometry"
                    }, {
                    	id: "source",
                    	title: "Fault Source"
                    }]
                },
		"map", {
                    id: "featuregrid",
                    layout: "fit",
                    region: "south",
                    border: false,
                    height: 200,
                    split: true,
                    collapseMode: "mini",
                }]
            }],

            tools: [{
                actionTarget: {target: "paneltbar", index: 0},
                outputAction: 0,
                outputConfig: {
                    title: "Login",
                    width: 900,
                    height: 500,
                    modal: true,
                    bodyCfg: {
                        tag: "iframe",
                        src: "http://178.79.185.190/",
                        style: {border: 0}
                    }
                },
                actions: [{
                    iconCls: "icon-geoexplorer",
                    text: "Login",
                }]
            },{
                ptype: "gxp_layertree",
                outputTarget: "tree",
            }, {
                ptype: "gxp_featuremanager",
                id: "featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "trace_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuregrid",
                alwaysDisplayOnMap: true,
                selectOnMap: true,
                displayMode: "selected",
                featureManager: "trace_featuremanager",
                outputTarget: "featuregrid",
                outputConfig: {
                    id: "grid",
                    propertyNames: propertyNames
                },
                controlOptions: {
                    multiple: true,
                }
            }, {
                ptype: "gxp_featuremanager",
                id: "summary_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "site_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "fault_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "simpleFault_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuremanager",
                id: "source_featuremanager",
                autoLoadFeatures: true,
                autoSetLayer: false,
                paging: false,
                maxFeatures: 100
            }, {
                ptype: "gxp_featuregrid",
                alwaysDisplayOnMap: true,
                selectOnMap: true,
                displayMode: "selected",
                featureManager: "featuremanager",
                outputTarget: "featuregrid",
                outputConfig: {
                    id: "grid",
                    propertyNames: propertyNames
                },
                controlOptions: {
                    multiple: true,
                }
            }, {
                ptype: "app_traceform",
                id: "traceform",
                featureManager: "trace_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "trace"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "trace_featuremanager",
                actionTarget: "traceform_tooltarget",
                autoLoadFeatures: true,
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_summaryform",
                id: "summaryform",
                featureManager: "summary_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "summary"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "summary_featuremanager",
                modifyOnly: true,
                actionTarget: "summaryform_tooltarget",
                //createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
				snappingAgent: "snapping-agent",
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_siteform",
                id: "siteform",
                featureManager: "site_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "site"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "site_featuremanager",
                actionTarget: "siteform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
                snappingAgent: "snapping-agent",
                autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_faultform",
                id: "faultform",
                featureManager: "fault_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "fault"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "fault_featuremanager",
                actionTarget: "faultform_tooltarget",
                modifyOnly: true,
                //createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
		        autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_simplegeometryform",
                id: "simplegeometryform",
                featureManager: "simpleFault_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "simple_geometry"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "simpleFault_featuremanager",
                actionTarget: "simplegeometryform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
		        autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
                ptype: "app_sourceform",
                id: "sourceform",
                featureManager: "source_featuremanager",
                featureEditor: "featureeditor",
                outputTarget: "source"
            }, {
                ptype: "gxp_featureeditor",
                id: "featureeditor",
                featureManager: "source_featuremanager",
                actionTarget: "sourceform_tooltarget",
                createFeatureActionText: "Draw",
                editFeatureActionText: "Modify",
		        snappingAgent: "snapping-agent",
		        autoLoadFeatures: true,
                outputConfig: {
                    propertyNames: propertyNames
                }
            }, {
		         ptype: "gxp_wmsgetfeatureinfo",
	             outputConfig: {
	                 width: 400
	                 }
	         },/*{
        		ptype: "app_observations",
        		featureManager: "featuremanager",
        		outputTarget: "observations"
        	    },*/{
        		ptype: "gxp_legend",
        		outputTarget: "west",
        		outputConfig: {
        		    title: this.legendTabTitle,
        		    autoScroll: true
        		}
        	}, {
            	ptype: "gxp_measure",
            	actionTarget: {target: "map.tbar", index: 6},
            	toggleGroup: "main"
            }, {
            	ptype: "gxp_zoomtoextent",
            	actionTarget: "map.tbar"
            }, {
            	ptype: "gxp_zoom",
            	actionTarget: "map.tbar"
            }, {
            	ptype: "gxp_navigationhistory",
            	actionTarget: "map.tbar"
            }, {
                ptype: "gxp_zoomtoselectedfeatures",
                featureManager: "featuremanager",
                actionTarget: "map.tbar",
                tooltip: "Zoom to selected closure"
            }, {
                ptype: "gxp_selectedfeatureactions",
                featureManager: "featuremanager",
                actions: [{
                    menuText: "Feature context demo",
                    text: "Feature context demo",
                    urlTemplate: "/geoserver/wms/reflect?layers={layer}&width=377&height=328&format=application/openlayers&featureid={fid}"
                }],
                actionTarget: ["grid.contextMenu", "grid.bbar"],
                outputConfig: {
                    width: 410,
                    height: 410
                }
            },{
                ptype: "gxp_googlegeocoder",
                outputTarget: "paneltbar",
                outputConfig: {
                    emptyText: "Search for a location ..."
                }
            }, {
            	ptype: "gxp_snappingagent",
            	id: "snapping-agent",
            	targets: [{
            		source: "local",
            		name: "geonode:trace"
            	}]
    	     }]
        });

        FaultedEarth.superclass.constructor.apply(this, arguments);
    }

});
<|endoftext|>