<commit_before>const fs = require('fs')
const http = require('http')
const path = require('path')
const url = require('url')

var pathIsInside = require('path-is-inside')
const httpShutdown = require('http-shutdown')

/**
 * A HTTP server for a `Host`
 */
class HostHttpServer {

  constructor (host, port = 2000) {
    this._host = host
    this._address = '127.0.0.1'
    this._port = port
    this._server = null
  }

  /**
   * Get the URL of this server
   *
   * @return {string} - Server's URL, `null` if not serving
   */
  url () {
    return this._server ? ('http://' + this._address + ':' + this._port) : null
  }

  /**
   * Start this server
   *
   * @return {Promise}
   */
  start () {
    return new Promise((resolve, reject) => {
      if (!this._server) {
        let server = http.createServer(this.handle.bind(this))
        server = httpShutdown(server)
        server.on('error', function (error) {
          if (error.code === 'EADDRINUSE') {
            this._port += 10
            server.close()
            server.listen(this._port, this._address, 511, () => {
              resolve()
            })
            return
          }
          reject(error)
        }.bind(this))
        server.listen(this._port, this._address, 511, () => {
          resolve()
        })
        this._server = server
      }
      resolve()
    })
  }

  /**
   * Stop this server
   *
   * @return {Promise}
   */
  stop () {
    return new Promise((resolve) => {
      if (this._server) {
        this._server.shutdown()
        this._server = null
      }
      resolve()
    })
  }

  /**
   * Handle a HTTP request
   */
  handle (request, response) {
    let endpoint = this.route(request.method, request.url)
    if (endpoint) {
      let method = endpoint[0]
      let args = endpoint.slice(1)
      return method.call(this, request, response, ...args)
                   .catch(error => this.error500(request, response, error))
    } else {
      this.error400(request, response)
      return Promise.resolve()
    }
  }

  /**
   * Route a HTTP request
   *
   * @param {string} verb - The request's HTTP verb (aka. "method") eg. GET
   * @param {string} path - The requested path
   * @return {array} - An array with first element being the method to call,
   *                   and subsequent elements being the call arguments
   */
  route (verb, path) {
    if (path === '/') return [this.home]
    if (path === '/favicon.ico') return [this.statico, 'favicon.ico']
    if (path.substring(0, 8) === '/static/') return [this.statico, path.substring(8)]

    let matches = path.match(/^\/(.+?)(!(.+))?$/)
    if (matches) {
      let id = matches[1]
      let method = matches[3]
      if (verb === 'POST' && id) return [this.post, id]
      else if (verb === 'GET' && id) return [this.get, id]
      else if (verb === 'PUT' && id && method) return [this.put, id, method]
      else if (verb === 'DELETE' && id) return [this.delete, id]
    }

    return null
  }

  home (request, response) {
    if (!acceptsJson(request)) {
      return this.statico(request, response, 'index.html')
    } else {
      return this._host.options()
        .then(options => {
          response.setHeader('Content-Type', 'application/json')
          response.end(JSON.stringify(options))
        })
    }
  }

  statico (request, response, path_) {
    return new Promise((resolve) => {
      let staticPath = path.join(__dirname, '../../static')
      let requestedPath = path.join(staticPath, url.parse(path_).pathname)
      if (!pathIsInside(requestedPath, staticPath)) {
        this.error403(request, response, path_)
        resolve()
      } else {
        fs.readFile(requestedPath, (error, content) => {
          if (error) {
            if (error.code === 'ENOENT') {
              this.error404(request, response, path_)
            } else {
              this.error500(request, response, error)
            }
            response.end()
          } else {
            let contentType = {
              '.html': 'text/html',
              '.js': 'text/javascript',
              '.css': 'text/css',
              '.json': 'application/json',

              '.png': 'image/png',
              '.jpg': 'image/jpg',
              '.gif': 'image/gif',
              '.svg': 'image/svg+xml',

              '.woff': 'application/font-woff',
              '.ttf': 'application/font-ttf',
              '.eot': 'application/vnd.ms-fontobject',
              '.otf': 'application/font-otf'
            }[String(path.extname(path_)).toLowerCase()] || 'application/octect-stream'
            response.setHeader('Content-Type', contentType)
            response.end(content, 'utf-8')
          }
          resolve()
        })
      }
    })
  }

  post (request, response, type) {
    return bodify(request)
      .then(body => {
        let options = body ? JSON.parse(body) : {}
        return this._host.post(type, options)
          .then(id => {
            response.setHeader('Content-Type', 'application/json')
            response.end(id)
          })
      })
  }

  get (request, response, id) {
    return this._host.get(id)
      .then(instance => {
        response.setHeader('Content-Type', 'application/json')
        response.end(JSON.stringify(instance))
      })
  }

  put (request, response, id, method) {
    return bodify(request)
      .then(body => {
        // Ensure arguments are an array
        let args = []
        if (body) {
          let value = JSON.parse(body)
          if (value instanceof Array) {
            args = value
          } else if (value instanceof Object) {
            args = Object.keys(value).map(key => value[key])
          } else {
            args = [value]
          }
        }
        return args
      })
      .then(args => {
        return this._host.put(id, method, args)
          .then(result => {
            response.setHeader('Content-Type', 'application/json')
            response.end(JSON.stringify(result))
          })
      })
  }

  delete (request, response, id) {
    return this._host.delete(id)
      .then(() => {
        response.end()
      })
  }

  error (request, response, status, error) {
    response.statusCode = status
    let content = JSON.stringify(error)
    if (acceptsJson(request)) response.setHeader('Content-Type', 'application/json')
    response.end(content)
  }

  error400 (request, response) {
    this.error(request, response, 400, {error: 'Bad request', details: request.method + ' ' + request.url})
  }

  error403 (request, response, details) {
    this.error(request, response, 403, {error: 'Access denied', details: details})
  }

  error404 (request, response, details) {
    this.error(request, response, 404, {error: 'Not found', details: details})
  }

  error500 (request, response, error) {
    // Ignore this for code coverage it's difficult to test
    /* istanbul ignore next */
    (function () {
      response.statusCode = 500
      let content
      let what = error ? error.stack : ''
      if (acceptsJson(request)) {
        content = JSON.stringify({error: 'Internal error', what: what})
        response.setHeader('Content-Type', 'application/json')
      } else {
        content = 'Internal error' + (what ? (': ' + what) : '')
      }
      response.end(content)
    }())
  }

}

function acceptsJson (request) {
  let accept = request.headers['accept'] || ''
  return accept.match(/application\/json/)
}

function bodify (request) {
  return new Promise((resolve) => {
    // Check if in tests and using a mock request
    if (request._setBody) resolve(request.body)
    else {
      // Ignore this for code coverage it's difficult to test
      /* istanbul ignore next */
      (function () {
        var body = []
        request.on('data', function (chunk) {
          body.push(chunk)
        }).on('end', function () {
          body = Buffer.concat(body).toString() // eslint-disable-line no-undef
          resolve(body)
        })
      }())
    }
  })
}

module.exports = HostHttpServer
<commit_msg>Do not serve when root<commit_after>const fs = require('fs')
const http = require('http')
const path = require('path')
const url = require('url')

var pathIsInside = require('path-is-inside')
const httpShutdown = require('http-shutdown')

/**
 * A HTTP server for a `Host`
 */
class HostHttpServer {

  constructor (host, port = 2000) {
    this._host = host
    this._address = '127.0.0.1'
    this._port = port
    this._server = null
  }

  /**
   * Get the URL of this server
   *
   * @return {string} - Server's URL, `null` if not serving
   */
  url () {
    return this._server ? ('http://' + this._address + ':' + this._port) : null
  }

  /**
   * Start this server
   *
   * @return {Promise}
   */
  start () {
    return new Promise((resolve, reject) => {
      if (!this._server) {
        if (process.getuid && process.getuid() === 0) {
          return reject(new Error('Serving host while root user is dangerous and is not allowed'))
        }

        let server = http.createServer(this.handle.bind(this))
        server = httpShutdown(server)
        server.on('error', function (error) {
          if (error.code === 'EADDRINUSE') {
            this._port += 10
            server.close()
            server.listen(this._port, this._address, 511, () => {
              resolve()
            })
            return
          }
          reject(error)
        }.bind(this))
        server.listen(this._port, this._address, 511, () => {
          resolve()
        })
        this._server = server
      }
      resolve()
    })
  }

  /**
   * Stop this server
   *
   * @return {Promise}
   */
  stop () {
    return new Promise((resolve) => {
      if (this._server) {
        this._server.shutdown()
        this._server = null
      }
      resolve()
    })
  }

  /**
   * Handle a HTTP request
   */
  handle (request, response) {
    let endpoint = this.route(request.method, request.url)
    if (endpoint) {
      let method = endpoint[0]
      let args = endpoint.slice(1)
      return method.call(this, request, response, ...args)
                   .catch(error => this.error500(request, response, error))
    } else {
      this.error400(request, response)
      return Promise.resolve()
    }
  }

  /**
   * Route a HTTP request
   *
   * @param {string} verb - The request's HTTP verb (aka. "method") eg. GET
   * @param {string} path - The requested path
   * @return {array} - An array with first element being the method to call,
   *                   and subsequent elements being the call arguments
   */
  route (verb, path) {
    if (path === '/') return [this.home]
    if (path === '/favicon.ico') return [this.statico, 'favicon.ico']
    if (path.substring(0, 8) === '/static/') return [this.statico, path.substring(8)]

    let matches = path.match(/^\/(.+?)(!(.+))?$/)
    if (matches) {
      let id = matches[1]
      let method = matches[3]
      if (verb === 'POST' && id) return [this.post, id]
      else if (verb === 'GET' && id) return [this.get, id]
      else if (verb === 'PUT' && id && method) return [this.put, id, method]
      else if (verb === 'DELETE' && id) return [this.delete, id]
    }

    return null
  }

  home (request, response) {
    if (!acceptsJson(request)) {
      return this.statico(request, response, 'index.html')
    } else {
      return this._host.options()
        .then(options => {
          response.setHeader('Content-Type', 'application/json')
          response.end(JSON.stringify(options))
        })
    }
  }

  statico (request, response, path_) {
    return new Promise((resolve) => {
      let staticPath = path.join(__dirname, '../../static')
      let requestedPath = path.join(staticPath, url.parse(path_).pathname)
      if (!pathIsInside(requestedPath, staticPath)) {
        this.error403(request, response, path_)
        resolve()
      } else {
        fs.readFile(requestedPath, (error, content) => {
          if (error) {
            if (error.code === 'ENOENT') {
              this.error404(request, response, path_)
            } else {
              this.error500(request, response, error)
            }
            response.end()
          } else {
            let contentType = {
              '.html': 'text/html',
              '.js': 'text/javascript',
              '.css': 'text/css',
              '.json': 'application/json',

              '.png': 'image/png',
              '.jpg': 'image/jpg',
              '.gif': 'image/gif',
              '.svg': 'image/svg+xml',

              '.woff': 'application/font-woff',
              '.ttf': 'application/font-ttf',
              '.eot': 'application/vnd.ms-fontobject',
              '.otf': 'application/font-otf'
            }[String(path.extname(path_)).toLowerCase()] || 'application/octect-stream'
            response.setHeader('Content-Type', contentType)
            response.end(content, 'utf-8')
          }
          resolve()
        })
      }
    })
  }

  post (request, response, type) {
    return bodify(request)
      .then(body => {
        let options = body ? JSON.parse(body) : {}
        return this._host.post(type, options)
          .then(id => {
            response.setHeader('Content-Type', 'application/json')
            response.end(id)
          })
      })
  }

  get (request, response, id) {
    return this._host.get(id)
      .then(instance => {
        response.setHeader('Content-Type', 'application/json')
        response.end(JSON.stringify(instance))
      })
  }

  put (request, response, id, method) {
    return bodify(request)
      .then(body => {
        // Ensure arguments are an array
        let args = []
        if (body) {
          let value = JSON.parse(body)
          if (value instanceof Array) {
            args = value
          } else if (value instanceof Object) {
            args = Object.keys(value).map(key => value[key])
          } else {
            args = [value]
          }
        }
        return args
      })
      .then(args => {
        return this._host.put(id, method, args)
          .then(result => {
            response.setHeader('Content-Type', 'application/json')
            response.end(JSON.stringify(result))
          })
      })
  }

  delete (request, response, id) {
    return this._host.delete(id)
      .then(() => {
        response.end()
      })
  }

  error (request, response, status, error) {
    response.statusCode = status
    let content = JSON.stringify(error)
    if (acceptsJson(request)) response.setHeader('Content-Type', 'application/json')
    response.end(content)
  }

  error400 (request, response) {
    this.error(request, response, 400, {error: 'Bad request', details: request.method + ' ' + request.url})
  }

  error403 (request, response, details) {
    this.error(request, response, 403, {error: 'Access denied', details: details})
  }

  error404 (request, response, details) {
    this.error(request, response, 404, {error: 'Not found', details: details})
  }

  error500 (request, response, error) {
    // Ignore this for code coverage it's difficult to test
    /* istanbul ignore next */
    (function () {
      response.statusCode = 500
      let content
      let what = error ? error.stack : ''
      if (acceptsJson(request)) {
        content = JSON.stringify({error: 'Internal error', what: what})
        response.setHeader('Content-Type', 'application/json')
      } else {
        content = 'Internal error' + (what ? (': ' + what) : '')
      }
      response.end(content)
    }())
  }

}

function acceptsJson (request) {
  let accept = request.headers['accept'] || ''
  return accept.match(/application\/json/)
}

function bodify (request) {
  return new Promise((resolve) => {
    // Check if in tests and using a mock request
    if (request._setBody) resolve(request.body)
    else {
      // Ignore this for code coverage it's difficult to test
      /* istanbul ignore next */
      (function () {
        var body = []
        request.on('data', function (chunk) {
          body.push(chunk)
        }).on('end', function () {
          body = Buffer.concat(body).toString() // eslint-disable-line no-undef
          resolve(body)
        })
      }())
    }
  })
}

module.exports = HostHttpServer
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs-extra'),
    config = require('../../config/'),
    path = require('path'),
    pckgPath = path.resolve('mo-app.json'),
    pckgFallbackPath = path.resolve('app.json'),
    ZipFile = require('../helpers/zipfile'),
    requests = require('../helpers/requests'),
    chalk = require('chalk'),
    info = chalk.yellow,
    error = chalk.bold.red,
    success = chalk.bold.green;

module.exports = function(options) {
  var distFolder = options.dist || config.distFolder || 'build';

  try {
        let fileExists = fs.statSync(pckgPath);
    } catch (e) {
        try {
            let fallbackFileExists = fs.statSync(pckgFallbackPath);
            fs.rename(pckgFallbackPath, pckgPath, function(renameErr) {
                if ( renameErr ) console.log('ERROR: ' + renameErr);
                console.log(info('fallback file , app.json found , renaming the file to mo-app.json'));
            });
        } catch (err) {
            return console.error(error('Error in Reading File. ' + err));
        }
    }

  fs.readJSON(pckgPath, function(err, json) {
  	if(err) {
  		console.error(error(err.message))
  		return
  	}

    let zipper = new ZipFile(distFolder, json.name, json.version)
    let created = false

    return zipper.zip()
      .then((data) => {
        console.log(info('Zipped package file'))
        return requests.postPackageData(json, options)
      })
      .then((data) => {
        console.log(info('Created package on server with following:'))
        console.log(data)
        created = true
        return requests.putPackageData(json.name, json.version, options)

      })
      .then((data) => {
        console.log(success('All done'))
        return
      })
      .catch((err) => {
        if (created) {
          console.info(info('Removing app from server'))
          requests.deletePackage(json, options)
        }
        console.info(info('Could not deploy:'))
        console.error(error(err.message || err))
      })

  })
}
<commit_msg>feat: adds file option to explicity specify moapp file<commit_after>'use strict';

var fs = require('fs-extra'),
    config = require('../../config/'),
    path = require('path'),
    ZipFile = require('../helpers/zipfile'),
    requests = require('../helpers/requests'),
    chalk = require('chalk'),
    info = chalk.yellow,
    error = chalk.bold.red,
    success = chalk.bold.green;

module.exports = function(options) {
  var distFolder = options.dist || config.distFolder || 'build';
  var packageFile = path.resolve(options.file || 'mo-app.json');

  try {
    let fileExists = fs.statSync(packageFile);
  } catch (err) {
    return console.error(error('Error in Reading File. ' + err));
  }

  fs.readJSON(packageFile, function(err, json) {
    if(err) {
      console.error(error(err.message))
      return
    }

    let zipper = new ZipFile(distFolder, json.name, json.version)
    let created = false

    return zipper.zip()
      .then((data) => {
        console.log(info('Zipped package file'))
        return requests.postPackageData(json, options)
      })
      .then((data) => {
        console.log(info('Created package on server with following:'))
        console.log(data)
        created = true
        return requests.putPackageData(json.name, json.version, options)

      })
      .then((data) => {
        console.log(success('All done'))
        return
      })
      .catch((err) => {
        if (created) {
          console.info(info('Removing app from server'))
          requests.deletePackage(json, options)
        }
        console.info(info('Could not deploy:'))
        console.error(error(err.message || err))
      })

  })
}
<|endoftext|>


<commit_before>(function() {
  // properties

  const path = require("path");
  const xmlHelper = require("../lib/xmlHelper.js");

  // entry
  module.exports = {
    writePreferences: writePreferences
  };

  // injects config.xml preferences into AndroidManifest.xml file.
  function writePreferences(context, preferences) {
    // read manifest
    const manifest = getManifest(context);

    // update manifest
    manifest.file = updateBranchMetaData(manifest.file, preferences);
    manifest.file = updateBranchReferrerTracking(manifest.file);
    manifest.file = updateLaunchOptionToSingleTask(
      manifest.file,
      manifest.mainActivityIndex
    );
    manifest.file = updateBranchURIScheme(
      manifest.file,
      manifest.mainActivityIndex,
      preferences
    );
    manifest.file = updateBranchAppLinks(
      manifest.file,
      manifest.mainActivityIndex,
      preferences,
      manifest.targetSdk
    );

    // save manifest
    xmlHelper.writeJsonAsXml(manifest.path, manifest.file);
  }

  // get AndroidManifest.xml information
  function getManifest(context) {
    let pathToManifest;
    let manifest;

    try {
      // cordova platform add android@6.0.0
      pathToManifest = path.join(
        context.opts.projectRoot,
        "platforms",
        "android",
        "AndroidManifest.xml"
      );
      manifest = xmlHelper.readXmlAsJson(pathToManifest);
    } catch (e) {
      try {
        // cordova platform add android@7.0.0
        pathToManifest = path.join(
          context.opts.projectRoot,
          "platforms",
          "android",
          "app",
          "src",
          "main",
          "AndroidManifest.xml"
        );
        manifest = xmlHelper.readXmlAsJson(pathToManifest);
      } catch (e) {
        throw new Error(`BRANCH SDK: Cannot read AndroidManfiest.xml ${e}`);
      }
    }
    const mainActivityIndex = getMainLaunchActivityIndex(
      manifest.manifest.application[0].activity
    );
    const targetSdk =
      manifest.manifest["uses-sdk"][0].$["android:targetSdkVersion"];

    return {
      file: manifest,
      path: pathToManifest,
      mainActivityIndex: mainActivityIndex,
      targetSdk: targetSdk
    };
  }

  // adds to <application> for Branch init and testmode:
  //    <meta-data android:name="io.branch.sdk.BranchKey" android:value="key_live_icCccJIpd7GlYY5oOmoEtpafuDiuyXhT" />
  //    <meta-data android:name="io.branch.sdk.TestMode" android:value="false" />
  function updateBranchMetaData(manifest, preferences) {
    let metadatas = manifest.manifest.application[0]["meta-data"] || [];
    const metadata = [];
    const keys = ["io.branch.sdk.BranchKey", "io.branch.sdk.TestMode"];
    const vals = [
      preferences.branchKey,
      preferences.androidTestMode || "false"
    ];

    // remove old
    for (var i = 0; i < keys.length; i++) {
      metadatas = removeBasedOnAndroidName(metadatas, keys[i]);
    }

    // add new
    for (i = 0; i < keys.length; i++) {
      const key = keys[i];
      const val = vals[i];
      metadata.push({
        $: {
          "android:name": key,
          "android:value": val
        }
      });
    }
    manifest.manifest.application[0]["meta-data"] = metadatas.concat(metadata);

    return manifest;
  }

  // adds to <application> for install referrer tracking (optional)
  //    <receiver android:name="io.branch.referral.InstallListener" android:exported="true">
  //       <intent-filter>
  //           <action android:name="com.android.vending.INSTALL_REFERRER" />
  //       </intent-filter>
  //    </receiver>
  function updateBranchReferrerTracking(manifest) {
    let receivers = manifest.manifest.application[0].receiver || [];
    const androidName = "io.branch.referral.InstallListener";

    // remove old
    receivers = removeBasedOnAndroidName(receivers, androidName);

    // add new
    manifest.manifest.application[0].receiver = receivers.concat([
      {
        $: {
          "android:name": androidName,
          "android:exported": true
        },
        "intent-filter": [
          {
            action: [
              {
                $: {
                  "android:name": "com.android.vending.INSTALL_REFERRER"
                }
              }
            ]
          }
        ]
      }
    ]);

    return manifest;
  }

  // adds to main <activity>:
  //    android:launchMode="singleTask"
  function updateLaunchOptionToSingleTask(manifest, mainActivityIndex) {
    manifest.manifest.application[0].activity[mainActivityIndex].$[
      "android:launchMode"
    ] =
      "singleTask";
    return manifest;
  }

  // adds to main <activity> for URI Scheme
  //    <intent-filter android:name="io.branch.sdk.UriScheme">
  //        <data android:scheme="ethantest" />
  //        <action android:name="android.intent.action.VIEW" />
  //        <category android:name="android.intent.category.DEFAULT" />
  //        <category android:name="android.intent.category.BROWSABLE" />
  //    </intent-filter>
  function updateBranchURIScheme(manifest, mainActivityIndex, preferences) {
    let intentFilters =
      manifest.manifest.application[0].activity[mainActivityIndex][
        "intent-filter"
      ] || [];
    const androidName = "io.branch.sdk.UriScheme";

    // remove
    intentFilters = removeBasedOnAndroidName(intentFilters, androidName);

    // add
    manifest.manifest.application[0].activity[mainActivityIndex][
      "intent-filter"
    ] = intentFilters.concat([
      {
        $: {
          "android:name": androidName
        },
        action: [
          {
            $: {
              "android:name": "android.intent.action.VIEW"
            }
          }
        ],
        category: [
          {
            $: {
              "android:name": "android.intent.category.DEFAULT"
            }
          },
          {
            $: {
              "android:name": "android.intent.category.BROWSABLE"
            }
          }
        ],
        data: [
          {
            $: {
              "android:scheme": preferences.uriScheme
            }
          }
        ]
      }
    ]);

    return manifest;
  }

  // adds to main <activity> for App Links (optional)
  //    <intent-filter android:name="io.branch.sdk.AppLink" android:autoVerify="true">
  //       <action android:name="android.intent.action.VIEW" />
  //       <category android:name="android.intent.category.DEFAULT" />
  //       <category android:name="android.intent.category.BROWSABLE" />
  //       <data android:scheme="https" android:host="ethan.app.link" />
  //       <data android:scheme="https" android:host="ethan-alternate.app.link" />
  //    </intent-filter>
  function updateBranchAppLinks(
    manifest,
    mainActivityIndex,
    preferences,
    targetSdk
  ) {
    let intentFilters =
      manifest.manifest.application[0].activity[mainActivityIndex][
        "intent-filter"
      ] || [];
    const data = getAppLinkIntentFilterData(preferences);
    const androidName = "io.branch.sdk.AppLink";
    const header = {
      "android:name": androidName,
      "android:autoVerify": "true"
    };
    if (targetSdk && parseInt(targetSdk) < 23) {
      delete header["android:autoVerify"];
    }

    // remove
    intentFilters = removeBasedOnAndroidName(intentFilters, androidName);

    // add new (remove old already done in updateBranchURIScheme)
    manifest.manifest.application[0].activity[mainActivityIndex][
      "intent-filter"
    ] = intentFilters.concat([
      {
        $: header,
        action: [
          {
            $: {
              "android:name": "android.intent.action.VIEW"
            }
          }
        ],
        category: [
          {
            $: {
              "android:name": "android.intent.category.DEFAULT"
            }
          },
          {
            $: {
              "android:name": "android.intent.category.BROWSABLE"
            }
          }
        ],
        data: data
      }
    ]);

    return manifest;
  }

  // determine the Branch link domain <data> to append to the App Link intent filter
  function getAppLinkIntentFilterData(preferences) {
    const intentFilterData = [];
    const linkDomains = preferences.linkDomain;

    for (let i = 0; i < linkDomains.length; i++) {
      const linkDomain = linkDomains[i];

      // app.link link domains need -alternate associated domains as well (for Deep Views)
      if (linkDomain.indexOf("app.link") !== -1) {
        const first = linkDomain.split(".")[0];
        const rest = linkDomain
          .split(".")
          .slice(1)
          .join(".");
        const alternate = `${first}-alternate` + `.${rest}`;

        intentFilterData.push(getAppLinkIntentFilterDictionary(linkDomain));
        intentFilterData.push(getAppLinkIntentFilterDictionary(alternate));
      } else {
        // bnc.lt and custom domains
        if (preferences.androidPrefix === null) {
          throw new Error(
            'BRANCH SDK: Invalid "android-prefix" in <branch-config> in your config.xml. Docs https://goo.gl/GijGKP'
          );
        }
        intentFilterData.push(
          getAppLinkIntentFilterDictionary(
            linkDomain,
            preferences.androidPrefix
          )
        );
      }
    }

    return intentFilterData;
  }

  // generate the array dictionary for <data> component for the App Link intent filter
  function getAppLinkIntentFilterDictionary(linkDomain, androidPrefix) {
    const scheme = "https";
    const output = {
      $: {
        "android:host": linkDomain,
        "android:scheme": scheme
      }
    };

    if (androidPrefix) {
      output.$["android:pathPrefix"] = androidPrefix;
    }

    return output;
  }

  // remove previous Branch related <meta-data> and <receiver> based on android:name
  function removeBasedOnAndroidName(items, androidName) {
    const without = [];
    for (let i = 0; i < items.length; i++) {
      const item = items[i];
      if (item.hasOwnProperty("$") && item.$.hasOwnProperty("android:name")) {
        const key = item.$["android:name"];
        if (key === androidName) {
          continue;
        }
        without.push(item);
      } else {
        without.push(item);
      }
    }
    return without;
  }

  // get the main <activity> because Branch Intent Filters must be in the main Launch Activity
  function getMainLaunchActivityIndex(activities) {
    let launchActivityIndex = -1;

    for (let i = 0; i < activities.length; i++) {
      const activity = activities[i];
      if (isLaunchActivity(activity)) {
        launchActivityIndex = i;
        break;
      }
    }

    return launchActivityIndex;
  }

  // determine if <activity> is the main activity
  function isLaunchActivity(activity) {
    const intentFilters = activity["intent-filter"];
    let isLauncher = false;

    if (intentFilters == null || intentFilters.length === 0) {
      return false;
    }

    isLauncher = intentFilters.some(intentFilter => {
      const action = intentFilter.action;
      const category = intentFilter.category;

      if (
        action == null ||
        action.length !== 1 ||
        category == null ||
        category.length !== 1
      ) {
        return false;
      }

      const isMainAction =
        action[0].$["android:name"] === "android.intent.action.MAIN";
      const isLauncherCategory =
        category[0].$["android:name"] === "android.intent.category.LAUNCHER";

      return isMainAction && isLauncherCategory;
    });

    return isLauncher;
  }
})();
<commit_msg>fix: android-prefix no longer needed for custom link domains<commit_after>(function() {
  // properties

  const path = require("path");
  const xmlHelper = require("../lib/xmlHelper.js");

  // entry
  module.exports = {
    writePreferences: writePreferences
  };

  // injects config.xml preferences into AndroidManifest.xml file.
  function writePreferences(context, preferences) {
    // read manifest
    const manifest = getManifest(context);

    // update manifest
    manifest.file = updateBranchMetaData(manifest.file, preferences);
    manifest.file = updateBranchReferrerTracking(manifest.file);
    manifest.file = updateLaunchOptionToSingleTask(
      manifest.file,
      manifest.mainActivityIndex
    );
    manifest.file = updateBranchURIScheme(
      manifest.file,
      manifest.mainActivityIndex,
      preferences
    );
    manifest.file = updateBranchAppLinks(
      manifest.file,
      manifest.mainActivityIndex,
      preferences,
      manifest.targetSdk
    );

    // save manifest
    xmlHelper.writeJsonAsXml(manifest.path, manifest.file);
  }

  // get AndroidManifest.xml information
  function getManifest(context) {
    let pathToManifest;
    let manifest;

    try {
      // cordova platform add android@6.0.0
      pathToManifest = path.join(
        context.opts.projectRoot,
        "platforms",
        "android",
        "AndroidManifest.xml"
      );
      manifest = xmlHelper.readXmlAsJson(pathToManifest);
    } catch (e) {
      try {
        // cordova platform add android@7.0.0
        pathToManifest = path.join(
          context.opts.projectRoot,
          "platforms",
          "android",
          "app",
          "src",
          "main",
          "AndroidManifest.xml"
        );
        manifest = xmlHelper.readXmlAsJson(pathToManifest);
      } catch (e) {
        throw new Error(`BRANCH SDK: Cannot read AndroidManfiest.xml ${e}`);
      }
    }
    const mainActivityIndex = getMainLaunchActivityIndex(
      manifest.manifest.application[0].activity
    );
    const targetSdk =
      manifest.manifest["uses-sdk"][0].$["android:targetSdkVersion"];

    return {
      file: manifest,
      path: pathToManifest,
      mainActivityIndex: mainActivityIndex,
      targetSdk: targetSdk
    };
  }

  // adds to <application> for Branch init and testmode:
  //    <meta-data android:name="io.branch.sdk.BranchKey" android:value="key_live_icCccJIpd7GlYY5oOmoEtpafuDiuyXhT" />
  //    <meta-data android:name="io.branch.sdk.TestMode" android:value="false" />
  function updateBranchMetaData(manifest, preferences) {
    let metadatas = manifest.manifest.application[0]["meta-data"] || [];
    const metadata = [];
    const keys = ["io.branch.sdk.BranchKey", "io.branch.sdk.TestMode"];
    const vals = [
      preferences.branchKey,
      preferences.androidTestMode || "false"
    ];

    // remove old
    for (var i = 0; i < keys.length; i++) {
      metadatas = removeBasedOnAndroidName(metadatas, keys[i]);
    }

    // add new
    for (i = 0; i < keys.length; i++) {
      const key = keys[i];
      const val = vals[i];
      metadata.push({
        $: {
          "android:name": key,
          "android:value": val
        }
      });
    }
    manifest.manifest.application[0]["meta-data"] = metadatas.concat(metadata);

    return manifest;
  }

  // adds to <application> for install referrer tracking (optional)
  //    <receiver android:name="io.branch.referral.InstallListener" android:exported="true">
  //       <intent-filter>
  //           <action android:name="com.android.vending.INSTALL_REFERRER" />
  //       </intent-filter>
  //    </receiver>
  function updateBranchReferrerTracking(manifest) {
    let receivers = manifest.manifest.application[0].receiver || [];
    const androidName = "io.branch.referral.InstallListener";

    // remove old
    receivers = removeBasedOnAndroidName(receivers, androidName);

    // add new
    manifest.manifest.application[0].receiver = receivers.concat([
      {
        $: {
          "android:name": androidName,
          "android:exported": true
        },
        "intent-filter": [
          {
            action: [
              {
                $: {
                  "android:name": "com.android.vending.INSTALL_REFERRER"
                }
              }
            ]
          }
        ]
      }
    ]);

    return manifest;
  }

  // adds to main <activity>:
  //    android:launchMode="singleTask"
  function updateLaunchOptionToSingleTask(manifest, mainActivityIndex) {
    manifest.manifest.application[0].activity[mainActivityIndex].$[
      "android:launchMode"
    ] =
      "singleTask";
    return manifest;
  }

  // adds to main <activity> for URI Scheme
  //    <intent-filter android:name="io.branch.sdk.UriScheme">
  //        <data android:scheme="ethantest" />
  //        <action android:name="android.intent.action.VIEW" />
  //        <category android:name="android.intent.category.DEFAULT" />
  //        <category android:name="android.intent.category.BROWSABLE" />
  //    </intent-filter>
  function updateBranchURIScheme(manifest, mainActivityIndex, preferences) {
    let intentFilters =
      manifest.manifest.application[0].activity[mainActivityIndex][
        "intent-filter"
      ] || [];
    const androidName = "io.branch.sdk.UriScheme";

    // remove
    intentFilters = removeBasedOnAndroidName(intentFilters, androidName);

    // add
    manifest.manifest.application[0].activity[mainActivityIndex][
      "intent-filter"
    ] = intentFilters.concat([
      {
        $: {
          "android:name": androidName
        },
        action: [
          {
            $: {
              "android:name": "android.intent.action.VIEW"
            }
          }
        ],
        category: [
          {
            $: {
              "android:name": "android.intent.category.DEFAULT"
            }
          },
          {
            $: {
              "android:name": "android.intent.category.BROWSABLE"
            }
          }
        ],
        data: [
          {
            $: {
              "android:scheme": preferences.uriScheme
            }
          }
        ]
      }
    ]);

    return manifest;
  }

  // adds to main <activity> for App Links (optional)
  //    <intent-filter android:name="io.branch.sdk.AppLink" android:autoVerify="true">
  //       <action android:name="android.intent.action.VIEW" />
  //       <category android:name="android.intent.category.DEFAULT" />
  //       <category android:name="android.intent.category.BROWSABLE" />
  //       <data android:scheme="https" android:host="ethan.app.link" />
  //       <data android:scheme="https" android:host="ethan-alternate.app.link" />
  //    </intent-filter>
  function updateBranchAppLinks(
    manifest,
    mainActivityIndex,
    preferences,
    targetSdk
  ) {
    let intentFilters =
      manifest.manifest.application[0].activity[mainActivityIndex][
        "intent-filter"
      ] || [];
    const data = getAppLinkIntentFilterData(preferences);
    const androidName = "io.branch.sdk.AppLink";
    const header = {
      "android:name": androidName,
      "android:autoVerify": "true"
    };
    if (targetSdk && parseInt(targetSdk) < 23) {
      delete header["android:autoVerify"];
    }

    // remove
    intentFilters = removeBasedOnAndroidName(intentFilters, androidName);

    // add new (remove old already done in updateBranchURIScheme)
    manifest.manifest.application[0].activity[mainActivityIndex][
      "intent-filter"
    ] = intentFilters.concat([
      {
        $: header,
        action: [
          {
            $: {
              "android:name": "android.intent.action.VIEW"
            }
          }
        ],
        category: [
          {
            $: {
              "android:name": "android.intent.category.DEFAULT"
            }
          },
          {
            $: {
              "android:name": "android.intent.category.BROWSABLE"
            }
          }
        ],
        data: data
      }
    ]);

    return manifest;
  }

  // determine the Branch link domain <data> to append to the App Link intent filter
  function getAppLinkIntentFilterData(preferences) {
    const intentFilterData = [];
    const linkDomains = preferences.linkDomain;

    for (let i = 0; i < linkDomains.length; i++) {
      const linkDomain = linkDomains[i];

      // app.link link domains need -alternate associated domains as well (for Deep Views)
      if (linkDomain.indexOf("app.link") !== -1) {
        const first = linkDomain.split(".")[0];
        const rest = linkDomain
          .split(".")
          .slice(1)
          .join(".");
        const alternate = `${first}-alternate` + `.${rest}`;

        intentFilterData.push(getAppLinkIntentFilterDictionary(linkDomain));
        intentFilterData.push(getAppLinkIntentFilterDictionary(alternate));
      } else {
        // bnc.lt
        if (
          linkDomain.indexOf("bnc.lt") !== -1 &&
          preferences.androidPrefix === null
        ) {
          throw new Error(
            'BRANCH SDK: Invalid "android-prefix" in <branch-config> in your config.xml. Docs https://goo.gl/GijGKP'
          );
        }
        intentFilterData.push(
          getAppLinkIntentFilterDictionary(
            linkDomain,
            preferences.androidPrefix
          )
        );
      }
    }

    return intentFilterData;
  }

  // generate the array dictionary for <data> component for the App Link intent filter
  function getAppLinkIntentFilterDictionary(linkDomain, androidPrefix) {
    const scheme = "https";
    const output = {
      $: {
        "android:host": linkDomain,
        "android:scheme": scheme
      }
    };

    if (androidPrefix) {
      output.$["android:pathPrefix"] = androidPrefix;
    }

    return output;
  }

  // remove previous Branch related <meta-data> and <receiver> based on android:name
  function removeBasedOnAndroidName(items, androidName) {
    const without = [];
    for (let i = 0; i < items.length; i++) {
      const item = items[i];
      if (item.hasOwnProperty("$") && item.$.hasOwnProperty("android:name")) {
        const key = item.$["android:name"];
        if (key === androidName) {
          continue;
        }
        without.push(item);
      } else {
        without.push(item);
      }
    }
    return without;
  }

  // get the main <activity> because Branch Intent Filters must be in the main Launch Activity
  function getMainLaunchActivityIndex(activities) {
    let launchActivityIndex = -1;

    for (let i = 0; i < activities.length; i++) {
      const activity = activities[i];
      if (isLaunchActivity(activity)) {
        launchActivityIndex = i;
        break;
      }
    }

    return launchActivityIndex;
  }

  // determine if <activity> is the main activity
  function isLaunchActivity(activity) {
    const intentFilters = activity["intent-filter"];
    let isLauncher = false;

    if (intentFilters == null || intentFilters.length === 0) {
      return false;
    }

    isLauncher = intentFilters.some(intentFilter => {
      const action = intentFilter.action;
      const category = intentFilter.category;

      if (
        action == null ||
        action.length !== 1 ||
        category == null ||
        category.length !== 1
      ) {
        return false;
      }

      const isMainAction =
        action[0].$["android:name"] === "android.intent.action.MAIN";
      const isLauncherCategory =
        category[0].$["android:name"] === "android.intent.category.LAUNCHER";

      return isMainAction && isLauncherCategory;
    });

    return isLauncher;
  }
})();
<|endoftext|>


<commit_before>'use strict';

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

const ROOT_DIR = process.argv[2];

function convertFile(file, data) {
  var newPath = `${path.basename(file, '.txt')}.json`;

  var overviewData = {};

  data.split('\n')
    .map(x => x.trim())
    .forEach(line => {
      var matches = line.match(/^"(pos_x|pos_y|scale)"\s+"(\S+)"/);

      if (!matches) {
        return;
      }

      overviewData[matches[1]] = parseFloat(matches[2]);
    });

  fs.writeFile(newPath, JSON.stringify(overviewData, null, 2), assert.ifError);
}

function convertAll(files) {
  for (var file of files) {
    if (!file.endsWith('.txt')) {
      console.log('Skipping %s', file);
      continue;
    }

    (file => {
      fs.readFile(path.join(ROOT_DIR, file), 'utf8', (err, data) => {
        assert.ifError(err);
        convertFile(file, data);
      });
    })(file);
  }
}

fs.readdir(ROOT_DIR, (err, files) => {
  assert.ifError(err);
  convertAll(files);
});
<commit_msg>:zap: Updated `source_overview_txt_to_json` script to parse more leniantly<commit_after>'use strict';

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

const ROOT_DIR = process.argv[2];

function convertFile(file, data) {
  var newPath = `${path.basename(file, '.txt')}.json`;

  var overviewData = {};

  data.split('\n')
    .map(x => x.trim())
    .forEach(line => {
      var matches = line.match(/^\"?(pos_x|pos_y|scale)\"?\s+"?(\S+)"?/);

      if (!matches) {
        return;
      }

      overviewData[matches[1]] = parseFloat(matches[2]);
    });

  fs.writeFile(newPath, JSON.stringify(overviewData, null, 2), assert.ifError);
}

function convertAll(files) {
  for (var file of files) {
    if (!file.endsWith('.txt')) {
      console.log('Skipping %s', file);
      continue;
    }

    (file => {
      fs.readFile(path.join(ROOT_DIR, file), 'utf8', (err, data) => {
        assert.ifError(err);
        convertFile(file, data);
      });
    })(file);
  }
}

fs.readdir(ROOT_DIR, (err, files) => {
  assert.ifError(err);
  convertAll(files);
});
<|endoftext|>


<commit_before>/**
 * The JazzeePageStandard type
  @extends ApplyPage
 */
function JazzeePageStandard(){}
JazzeePageStandard.prototype = new JazzeePage;
JazzeePageStandard.prototype.constructor = JazzeePageStandard;

JazzeePageStandard.prototype.workspace = function(){
  JazzeePage.prototype.workspace.call(this);
  var pageClass = this;
  $('#pageToolbar').append(this.pagePropertiesButton());
  if(!this.isGlobal || this.pageBuilder.editGlobal){
    var dropdown = $('
');
    for(var i = 0; i < this.pageBuilder.elementTypes.length; i++){
      var item = $('').html(this.pageBuilder.elementTypes[i].typeName).attr('href', '#').data('elementTypes', this.pageBuilder.elementTypes[i]);
      item.bind('click', function(e){
        var elementType = $(e.target).data('elementTypes');
        var element = new window[elementType.typeClass].prototype.newElement('new' + pageClass.pageBuilder.getUniqueId(),'New ' + elementType.typeName + ' Element',elementType.typeId,elementType.typeName,elementType.typeClass,'new',pageClass);
        element.workspace();
        pageClass.addElement(element);
        pageClass.markModified();
        pageClass.synchronizeElementList();
        return false;
      });
      dropdown.append($('
').append(item));
    }
    var button = $('<button>').html('New Element').button({
      icons: {
        primary: 'ui-icon-plus',
        secondary: 'ui-icon-carat-1-s'
      }
    });
    button.qtip({
      position: {
        my: 'top-left',
        at: 'bottom-left'
      },
      show: {
        event: 'click'
      },
      hide: {
        event: 'unfocus click',
        fixed: true
      },
      content: {
        text: dropdown,
        title: {
          text: 'Choose element type',
          button: true
        }
      }
    });
    $('#pageToolbar').append(button);

    for(var i = 0; i < this.elements.length; i++){
      this.elements[i].workspace();
    }
    this.synchronizeElementList();
  }
};

/**
 * Create the page properties dropdown
*/
JazzeePageStandard.prototype.pageProperties = function(){
  var pageClass = this;

  var div = $('
');
  div.append(this.isRequiredButton());
  div.append(this.showAnswerStatusButton());
  if(!this.isGlobal || this.pageBuilder.editGlobal){
    if(pageClass.answerStatusDisplay == 1){
      var button = $('<button>').html('Edit Answer Status Display').attr('id', 'editDisplayButton').bind('click', function(e){
        $('.qtip').qtip('api').hide();
        pageClass.displayAnswerStatusForm();
      });
      button.button({
        icons: {
          primary: 'ui-icon-newwin'
        }
      });
      div.append(button);
    }

    $('#answerStatusDisplayButton input', div).bind('click',function(e){
      //rebuild the tooltip so the edit status display button will show up or be hidden
      div.replaceWith(pageClass.pageProperties());
      return true;
    });
  }

  var slider = $('
');
  slider.slider({
    value: this.min,
    min: 0,
    max: 20,
    step: 1,
    slide: function( event, ui ) {
      pageClass.setProperty('min', ui.value);
      $('#minValue').html(pageClass.min == 0?'No Minimum':pageClass.min);
    }
  });
  div.append($('
').html('Minimum Answers Required ').append($('').attr('id', 'minValue').html(this.min == 0?'No Minimum':this.min)));
  div.append(slider);

  var slider = $('
');
  slider.slider({
    value: this.max,
    min: 0,
    max: 20,
    step: 1,
    slide: function( event, ui ) {
      pageClass.setProperty('max', ui.value);
      $('#maxValue').html(pageClass.max == 0?'No Maximum':pageClass.max);
    }
  });
  div.append($('
').html('Maximum Answers Allowed ').append($('').attr('id', 'maxValue').html(this.max == 0?'No Maximum':this.max)));
  div.append(slider);

  return div;
};

/**
 * Add a copy of an element to the page
 * @param {ApplyElement} element
 */
JazzeePageStandard.prototype.copyElement = function(e){
  var obj = e.getDataObject();
  obj.id = 'newelement' + this.pageBuilder.getUniqueId();
  obj.title = 'Copy of ' + obj.title;
  var element = new window[obj.typeClass]();
  element.init(obj, this);
  element.status = 'new';
  element.markModified();
  for(var i = 0; i < obj.list.length; i++){
    element.newListItem(obj.list[i].value);
  }
  this.addElement(element);
  element.workspace();
  this.markModified();
  this.synchronizeElementList();
};

/**
 * Synchronize the element list after it has been created
 * Walk through the elements and make sure they are all have the right weight and click functionality
 */
JazzeePageStandard.prototype.synchronizeElementList = function(){
  var pageClass = this;

  $('#elements > div').each(function(i){
    $(this).data('element').setProperty('weight',i+1);
  });
  var list = $('#elements');
  list.sortable();
  list.bind("sortupdate", function(event, ui) {
    pageClass.markModified();
    pageClass.synchronizeElementList();
  });
};

/**
 * Display the form for setting up answer status
 * @returns {jQuery}
 */
JazzeePageStandard.prototype.displayAnswerStatusForm = function(){
  var pageClass = this;

  var obj = new FormObject();
  var field = obj.newField({name: 'legend', value: 'Answer Status'});

  var element = field.newElement('TextInput', 'title');
  element.label = 'Title';
  element.required = true;
  element.value = pageClass.getVariable('answerStatusTitle');
  var element = field.newElement('Textarea', 'text');
  element.label = 'Text';
  element.required = true;
  element.value = pageClass.getVariable('answerStatusText');
  element.instructions = 'The following will be replaced with the applicant input on this answer:';
  for(var i in pageClass.elements){
    var el = pageClass.elements[i];
    var text = el.title.replace(/\s+/, '_');
    text = '_' + text.toUpperCase() + '_';
    element.instructions += '
' + text + ': ' + el.title;
  }

  var form = new Form();
  var formObject = form.create(obj);
  $('form',formObject).append($('<button type="submit" name="submit">').html('Save'));
  var dialog = pageClass.displayForm(obj);
  $('form', dialog).unbind().bind('submit',function(e){
    pageClass.setVariable('answerStatusTitle',  $('input[name=title]', this).val());
    pageClass.setVariable('answerStatusText', $('textarea[name=text]', this).val());
    dialog.dialog("destroy").remove();
    return false;
  });//end submit
  dialog.dialog('open');
};<commit_msg>Complete the fix started in [003005bbc1c36f9f23ce415781f7c808504acea3] to get answer status display working with firefox.<commit_after>/**
 * The JazzeePageStandard type
  @extends ApplyPage
 */
function JazzeePageStandard(){}
JazzeePageStandard.prototype = new JazzeePage;
JazzeePageStandard.prototype.constructor = JazzeePageStandard;

JazzeePageStandard.prototype.workspace = function(){
  JazzeePage.prototype.workspace.call(this);
  var pageClass = this;
  $('#pageToolbar').append(this.pagePropertiesButton());
  if(!this.isGlobal || this.pageBuilder.editGlobal){
    var dropdown = $('
');
    for(var i = 0; i < this.pageBuilder.elementTypes.length; i++){
      var item = $('').html(this.pageBuilder.elementTypes[i].typeName).attr('href', '#').data('elementTypes', this.pageBuilder.elementTypes[i]);
      item.bind('click', function(e){
        var elementType = $(e.target).data('elementTypes');
        var element = new window[elementType.typeClass].prototype.newElement('new' + pageClass.pageBuilder.getUniqueId(),'New ' + elementType.typeName + ' Element',elementType.typeId,elementType.typeName,elementType.typeClass,'new',pageClass);
        element.workspace();
        pageClass.addElement(element);
        pageClass.markModified();
        pageClass.synchronizeElementList();
        return false;
      });
      dropdown.append($('
').append(item));
    }
    var button = $('<button>').html('New Element').button({
      icons: {
        primary: 'ui-icon-plus',
        secondary: 'ui-icon-carat-1-s'
      }
    });
    button.qtip({
      position: {
        my: 'top-left',
        at: 'bottom-left'
      },
      show: {
        event: 'click'
      },
      hide: {
        event: 'unfocus click',
        fixed: true
      },
      content: {
        text: dropdown,
        title: {
          text: 'Choose element type',
          button: true
        }
      }
    });
    $('#pageToolbar').append(button);

    for(var i = 0; i < this.elements.length; i++){
      this.elements[i].workspace();
    }
    this.synchronizeElementList();
  }
};

/**
 * Create the page properties dropdown
*/
JazzeePageStandard.prototype.pageProperties = function(){
  var pageClass = this;

  var div = $('
');
  div.append(this.isRequiredButton());
  div.append(this.showAnswerStatusButton());
  if(!this.isGlobal || this.pageBuilder.editGlobal){
    if(pageClass.answerStatusDisplay == 1){
      var button = $('<button>').html('Edit Answer Status Display').attr('id', 'editDisplayButton').bind('click', function(e){
        $('.qtip').qtip('api').hide();
        pageClass.displayAnswerStatusForm();
      });
      button.button({
        icons: {
          primary: 'ui-icon-newwin'
        }
      });
      div.append(button);
    }

    $('#answerStatusDisplayButton input', div).bind('change',function(e){
      //rebuild the tooltip so the edit status display button will show up or be hidden
      div.replaceWith(pageClass.pageProperties());
      return true;
    });
  }

  var slider = $('
');
  slider.slider({
    value: this.min,
    min: 0,
    max: 20,
    step: 1,
    slide: function( event, ui ) {
      pageClass.setProperty('min', ui.value);
      $('#minValue').html(pageClass.min == 0?'No Minimum':pageClass.min);
    }
  });
  div.append($('
').html('Minimum Answers Required ').append($('').attr('id', 'minValue').html(this.min == 0?'No Minimum':this.min)));
  div.append(slider);

  var slider = $('
');
  slider.slider({
    value: this.max,
    min: 0,
    max: 20,
    step: 1,
    slide: function( event, ui ) {
      pageClass.setProperty('max', ui.value);
      $('#maxValue').html(pageClass.max == 0?'No Maximum':pageClass.max);
    }
  });
  div.append($('
').html('Maximum Answers Allowed ').append($('').attr('id', 'maxValue').html(this.max == 0?'No Maximum':this.max)));
  div.append(slider);

  return div;
};

/**
 * Add a copy of an element to the page
 * @param {ApplyElement} element
 */
JazzeePageStandard.prototype.copyElement = function(e){
  var obj = e.getDataObject();
  obj.id = 'newelement' + this.pageBuilder.getUniqueId();
  obj.title = 'Copy of ' + obj.title;
  var element = new window[obj.typeClass]();
  element.init(obj, this);
  element.status = 'new';
  element.markModified();
  for(var i = 0; i < obj.list.length; i++){
    element.newListItem(obj.list[i].value);
  }
  this.addElement(element);
  element.workspace();
  this.markModified();
  this.synchronizeElementList();
};

/**
 * Synchronize the element list after it has been created
 * Walk through the elements and make sure they are all have the right weight and click functionality
 */
JazzeePageStandard.prototype.synchronizeElementList = function(){
  var pageClass = this;

  $('#elements > div').each(function(i){
    $(this).data('element').setProperty('weight',i+1);
  });
  var list = $('#elements');
  list.sortable();
  list.bind("sortupdate", function(event, ui) {
    pageClass.markModified();
    pageClass.synchronizeElementList();
  });
};

/**
 * Display the form for setting up answer status
 * @returns {jQuery}
 */
JazzeePageStandard.prototype.displayAnswerStatusForm = function(){
  var pageClass = this;

  var obj = new FormObject();
  var field = obj.newField({name: 'legend', value: 'Answer Status'});

  var element = field.newElement('TextInput', 'title');
  element.label = 'Title';
  element.required = true;
  element.value = pageClass.getVariable('answerStatusTitle');
  var element = field.newElement('Textarea', 'text');
  element.label = 'Text';
  element.required = true;
  element.value = pageClass.getVariable('answerStatusText');
  element.instructions = 'The following will be replaced with the applicant input on this answer:';
  for(var i in pageClass.elements){
    var el = pageClass.elements[i];
    var text = el.title.replace(/\s+/, '_');
    text = '_' + text.toUpperCase() + '_';
    element.instructions += '
' + text + ': ' + el.title;
  }

  var form = new Form();
  var formObject = form.create(obj);
  $('form',formObject).append($('<button type="submit" name="submit">').html('Save'));
  var dialog = pageClass.displayForm(obj);
  $('form', dialog).unbind().bind('submit',function(e){
    pageClass.setVariable('answerStatusTitle',  $('input[name=title]', this).val());
    pageClass.setVariable('answerStatusText', $('textarea[name=text]', this).val());
    dialog.dialog("destroy").remove();
    return false;
  });//end submit
  dialog.dialog('open');
};<|endoftext|>


<commit_before>// ⚠ Warning! It is a long running (minutes) and non-reversible migration.

/* eslint no-await-in-loop: 0 */
import { difference } from 'lodash';

const allFeeds = [
  'RiverOfNews',
  'Hides',
  'Comments',
  'Likes',
  'Posts',
  'Directs',
  'MyDiscussions',
  'Saves',
];

const virtualFeeds = ['RiverOfNews', 'MyDiscussions'];

const feedsWithSubscribers = ['Comments', 'Likes', 'Posts'];

const feedsWithPosts = difference(allFeeds, virtualFeeds);

export async function up(knex) {
  const query = async function (sql, args = {}) {
    const { rows } = await knex.raw(sql, args);
    return rows;
  }

  // Remove all RiverOfNews feeds from the posts table. We don't need them
  // anymore because of dynamic building of homefeeds.
  await query(`with rons as (select array_agg(id) as ids from feeds where name = 'RiverOfNews')
    update posts set feed_ids = posts.feed_ids - rons.ids from rons where posts.feed_ids && rons.ids`);

  // Select users that have more than one feeds with the same name. We should
  // keep only one of these feeds.
  const rows = await query(
    `select user_id, name from feeds group by user_id, name
    having count(*) > 1 order by user_id, name`
  );

  for (const { user_id: userId, name } of rows) {
    // All user's feeds of this name
    const feeds = await query(
      `select * from feeds
      where user_id = :userId and name = :name
      order by created_at`,
      { userId, name }
    );

    let feedUIDToKeep = feeds[0].uid;

    if (
      feedsWithPosts.includes(name) ||
      feedsWithSubscribers.includes(name)
    ) {
      const props = await Promise.all(
        feeds.map(async (feed) => {
          const [
            [{ exists: hasPosts }],
            [{ exists: hasSubscribers }],
          ] = await Promise.all([
            query(`select exists (select 1 from posts where (feed_ids && ?) || (destination_feed_ids && ?))`,
              [feed.id, feed.id]),
            query(`select exists (select 1 from subscriptions where feed_id = ?)`,
              [feed.uid]),
          ]);

          const inUse =
            (feedsWithPosts.includes(name) && hasPosts) ||
            (feedsWithSubscribers.includes(name) && hasSubscribers);
          return {
            id:  feed.id,
            uid: feed.uid,
            hasPosts,
            hasSubscribers,
            inUse,
          };
        })
      );

      const inUseCount = props.filter((p) => p.inUse).length;

      if (inUseCount > 1) {
        // User has more than one feed in use with the same name. Keeping the
        // first feed.
        const [first, ...toDelete] = props;

        // Move subscribers
        if (feedsWithSubscribers.includes(name) && toDelete.some((p) => p.hasSubscribers)) {
          const uidsToDelete = toDelete.map((f) => f.uid);
          const allSubscriptions = await query(
            `select user_id, created_at from subscriptions where feed_id = any(?)`,
            [uidsToDelete],
          );

          await Promise.all(allSubscriptions.map((s) => query(
            `insert into subscriptions
              (feed_id, user_id, created_at) values (:feed_id, :user_id, :created_at)
              on conflict do nothing`,
            { ...s, feed_id: first.uid },
          )));

          await query(`delete from subscriptions where feed_id = any(?)`, [uidsToDelete]);
        }

        // Move posts feeds
        if (feedsWithPosts.includes(name) && toDelete.some((p) => p.hasPosts)) {
          const idsToDelete = toDelete.map((f) => f.id);
          const firstId = first.id;
          const fields = ['feed_ids', 'destination_feed_ids'];

          // Add firstId to posts with idsToDelete
          await Promise.all(fields.map((field) => query(
            `update posts set ${field} = (${field} | :firstId) where ${field} && :idsToDelete`,
            { firstId, idsToDelete },
          )));

          // Remove idsToDelete
          await Promise.all(fields.map((field) => query(
            `update posts set ${field} = (${field} - :idsToDelete) where ${field} && :idsToDelete`,
            { firstId, idsToDelete },
          )));
        }
      } else if (inUseCount === 1) {
        feedUIDToKeep = props.find((p) => p.inUse).uid;
      } else {
        // Can safely remove all feeds except first
      }
    } else {
      // Can safely remove all feeds except first
    }

    await query(
      `delete from feeds where uid = any(?)`,
      [feeds.map((f) => f.uid).filter((uid) => uid !== feedUIDToKeep)],
    );
  }

  // Add unique constraint on feeds table for user_id and name
  await knex.raw(`alter table feeds add constraint feeds_unique_feed_names unique(user_id, name)`);
}

export async function down(knex) {
  await knex.raw(`alter table feeds drop constraint feeds_unique_feed_names`);
}
<commit_msg>Fix incorrect SQL syntax in migration<commit_after>// ⚠ Warning! It is a long running (minutes) and non-reversible migration.

/* eslint no-await-in-loop: 0 */
import { difference } from 'lodash';

const allFeeds = [
  'RiverOfNews',
  'Hides',
  'Comments',
  'Likes',
  'Posts',
  'Directs',
  'MyDiscussions',
  'Saves',
];

const virtualFeeds = ['RiverOfNews', 'MyDiscussions'];

const feedsWithSubscribers = ['Comments', 'Likes', 'Posts'];

const feedsWithPosts = difference(allFeeds, virtualFeeds);

export async function up(knex) {
  const query = async function (sql, args = {}) {
    const { rows } = await knex.raw(sql, args);
    return rows;
  }

  // Remove all RiverOfNews feeds from the posts table. We don't need them
  // anymore because of dynamic building of homefeeds.
  await query(`with rons as (select array_agg(id) as ids from feeds where name = 'RiverOfNews')
    update posts set feed_ids = posts.feed_ids - rons.ids from rons where posts.feed_ids && rons.ids`);

  // Select users that have more than one feeds with the same name. We should
  // keep only one of these feeds.
  const rows = await query(
    `select user_id, name from feeds group by user_id, name
    having count(*) > 1 order by user_id, name`
  );

  for (const { user_id: userId, name } of rows) {
    // All user's feeds of this name
    const feeds = await query(
      `select * from feeds
      where user_id = :userId and name = :name
      order by created_at`,
      { userId, name }
    );

    let feedUIDToKeep = feeds[0].uid;

    if (
      feedsWithPosts.includes(name) ||
      feedsWithSubscribers.includes(name)
    ) {
      const props = await Promise.all(
        feeds.map(async (feed) => {
          const [
            [{ exists: hasPosts }],
            [{ exists: hasSubscribers }],
          ] = await Promise.all([
            query(`select exists (select 1 from posts where (feed_ids && ?) or (destination_feed_ids && ?))`,
              [feed.id, feed.id]),
            query(`select exists (select 1 from subscriptions where feed_id = ?)`,
              [feed.uid]),
          ]);

          const inUse =
            (feedsWithPosts.includes(name) && hasPosts) ||
            (feedsWithSubscribers.includes(name) && hasSubscribers);
          return {
            id:  feed.id,
            uid: feed.uid,
            hasPosts,
            hasSubscribers,
            inUse,
          };
        })
      );

      const inUseCount = props.filter((p) => p.inUse).length;

      if (inUseCount > 1) {
        // User has more than one feed in use with the same name. Keeping the
        // first feed.
        const [first, ...toDelete] = props;

        // Move subscribers
        if (feedsWithSubscribers.includes(name) && toDelete.some((p) => p.hasSubscribers)) {
          const uidsToDelete = toDelete.map((f) => f.uid);
          const allSubscriptions = await query(
            `select user_id, created_at from subscriptions where feed_id = any(?)`,
            [uidsToDelete],
          );

          await Promise.all(allSubscriptions.map((s) => query(
            `insert into subscriptions
              (feed_id, user_id, created_at) values (:feed_id, :user_id, :created_at)
              on conflict do nothing`,
            { ...s, feed_id: first.uid },
          )));

          await query(`delete from subscriptions where feed_id = any(?)`, [uidsToDelete]);
        }

        // Move posts feeds
        if (feedsWithPosts.includes(name) && toDelete.some((p) => p.hasPosts)) {
          const idsToDelete = toDelete.map((f) => f.id);
          const firstId = first.id;
          const fields = ['feed_ids', 'destination_feed_ids'];

          // Add firstId to posts with idsToDelete
          await Promise.all(fields.map((field) => query(
            `update posts set ${field} = (${field} | :firstId) where ${field} && :idsToDelete`,
            { firstId, idsToDelete },
          )));

          // Remove idsToDelete
          await Promise.all(fields.map((field) => query(
            `update posts set ${field} = (${field} - :idsToDelete) where ${field} && :idsToDelete`,
            { firstId, idsToDelete },
          )));
        }
      } else if (inUseCount === 1) {
        feedUIDToKeep = props.find((p) => p.inUse).uid;
      } else {
        // Can safely remove all feeds except first
      }
    } else {
      // Can safely remove all feeds except first
    }

    await query(
      `delete from feeds where uid = any(?)`,
      [feeds.map((f) => f.uid).filter((uid) => uid !== feedUIDToKeep)],
    );
  }

  // Add unique constraint on feeds table for user_id and name
  await knex.raw(`alter table feeds add constraint feeds_unique_feed_names unique(user_id, name)`);
}

export async function down(knex) {
  await knex.raw(`alter table feeds drop constraint feeds_unique_feed_names`);
}
<|endoftext|>


<commit_before>if (typeof document !== 'undefined') {
  var canvas = document.createElement('canvas')
  var context = canvas.getContext('webgl', {
    antialias: false,
    stencil: true,
    preserveDrawingBuffer: true
  })
  canvas.style.position = 'fixed'
  canvas.style.top = '0'
  canvas.style.right = '0'
  canvas.style.width = '256px'
  canvas.style.height = '256px'
  document.body.appendChild(canvas)

  module.exports = function (width, height) {
    canvas.width = width
    canvas.height = height
    return context
  }

  module.exports.resize = function (gl, w, h) {
    canvas.width = w
    canvas.height = h
  }

  module.exports.destroy = function (gl) { }
} else {
  var CONTEXT = require('gl')(1, 1, {preserveDrawingBuffer: true})
  var RESIZE = CONTEXT.getExtension('STACKGL_resize_drawingbuffer')

  module.exports = function (w, h) {
    RESIZE.resize(w, h)
    return CONTEXT
  }

  module.exports.resize = function (gl, w, h) {
    RESIZE.resize(w, h)
  }

  module.exports.destroy = function (gl) {
  }
}
<commit_msg>Fix IE context creation<commit_after>if (typeof document !== 'undefined') {
  var canvas = document.createElement('canvas')
  var opts = {
    antialias: false,
    stencil: true,
    preserveDrawingBuffer: true
  }
  var context = canvas.getContext('webgl', opts) || canvas.getContext('experimental-webgl', opts)
  canvas.style.position = 'fixed'
  canvas.style.top = '0'
  canvas.style.right = '0'
  canvas.style.width = '256px'
  canvas.style.height = '256px'
  document.body.appendChild(canvas)

  module.exports = function (width, height) {
    canvas.width = width
    canvas.height = height
    return context
  }

  module.exports.resize = function (gl, w, h) {
    canvas.width = w
    canvas.height = h
  }

  module.exports.destroy = function (gl) { }
} else {
  var CONTEXT = require('gl')(1, 1, {preserveDrawingBuffer: true})
  var RESIZE = CONTEXT.getExtension('STACKGL_resize_drawingbuffer')

  module.exports = function (w, h) {
    RESIZE.resize(w, h)
    return CONTEXT
  }

  module.exports.resize = function (gl, w, h) {
    RESIZE.resize(w, h)
  }

  module.exports.destroy = function (gl) {
  }
}
<|endoftext|>


<commit_before>//
// Copyright (C) 2006 rPath, Inc
// All Rights Reserved
//

var labelsCache = {};
var versionCache = {};

var staticPath = '';

var green = {'style': 'color: green; font-weight: bold;'};
var gray = {'style': 'color: gray;'};

var spinnerImg = 'apps/mint/images/circle-ball-dark-antialiased.gif';
var nextImg = 'apps/mint/images/next.gif';
var prevImg = 'apps/mint/images/prev.gif';
var prevImgDisabled = 'apps/mint/images/prev_disabled.gif';

//
// some DOM helper functions
//
function forwardLink(attrs, text) {
    return LI(null, A(attrs, IMG({'src': staticPath + nextImg}), " " + text));
}

function returnImg() {
    return IMG({'src': staticPath + prevImg});
}

function disabledReturn() {
    return DIV(gray, IMG({'src': staticPath + prevImgDisabled}), " Back");
}

function buildTroveSpec(n, v, f) {
    return n + "=" + v + "[" + f + "]";
}

function trailingRevision(v) {
    var parts = v.split(":");
    var revision = parts[parts.length-1];
    return revision;
}

// ctor
function TrovePicker(projectId, serverName, troveName, pickerId, mintStaticPath, allowNone) {
    this.projectId = projectId;
    this.serverName = serverName;
    this.troveName = troveName;
    this.elId = pickerId;
    staticPath = mintStaticPath;
    this.allowNone = allowNone;

    if(troveName) {
        this.allowNameChoice = false;
    }

    this.buildTrovePicker();
}

TrovePicker.prototype.troveName = null;
TrovePicker.prototype.serverName = null;
TrovePicker.prototype.label = null;
TrovePicker.prototype.version = null;
TrovePicker.prototype.flavorCache = null;
TrovePicker.prototype.domCache = {};
TrovePicker.prototype.allowNameChoice = true;
TrovePicker.prototype.aloowNone = false;

TrovePicker.prototype.working = function(isWorking) {
    if(isWorking) {
        showElement(this.elId + 'spinnerList');
        hideElement(this.elId + 'selectionList');
    }
    else {
        hideElement(this.elId + 'spinnerList');
        showElement(this.elId + 'selectionList');
    }
}

// Build the initial DOM for the trove picker and
// begin retrieving all leaves for a given
// trove name on a server.
TrovePicker.prototype.buildTrovePicker = function() {
    oldEl = $(this.elId);
    picker = DIV({'id': this.elId, 'class': 'trovePicker'});
    spinner = UL({'id': this.elId + 'spinnerList'},
        LI(null, "Loading...", IMG({'src': staticPath + spinnerImg}))
    );
    appendChildNodes(picker, SPAN({'id': this.elId + 'prompt', 'class': 'prompt'}),
        P({'id': this.elId + 'return'}),
        spinner,
        UL({'id': this.elId + 'selectionList'}),
        INPUT({'id': this.elId.replace("-", "_") + 'Spec', 'name': this.elId + 'Spec', 'type': 'hidden'})
    );
    swapDOM(oldEl, picker);

    if(this.troveName) {
        this.getAllTroveLabels();
    } else {
        this.getGroupTroves();
    }
}

// A flavor has been chosen--continue on
TrovePicker.prototype.pickFlavor = function(e) {
    var n = e.src().name;
    var v = e.src().version;
    var shortv = trailingRevision(v);
    var f = e.src().flavor;
    var shortf = e.src().shortFlavor;

    var troveSpec = buildTroveSpec(n, v, f);
    setNodeAttribute($(this.elId.replace("-", "_") + 'Spec'), 'value', troveSpec);

    oldEl = $(this.elId + 'selectionList');
    newEl = DIV({'id': this.elId + 'selectionList'},
        SPAN({'style': 'font-weight: bold;'}, 'Group: '),
        SPAN(null, n + "=" + shortv + " [" + shortf + "]")
    );
    swapDOM(oldEl, newEl);

    // return to getTroveVersions
    returnLink = A(null, returnImg(), " Back");
    returnLink.troveName = n;
    returnLink.version = e.src().version;
    returnLink.label = e.src().label;
    connect(returnLink, "onclick", this, "showTroveFlavors");
    replaceChildNodes($(this.elId + 'return'), returnLink);
    replaceChildNodes($(this.elId + 'prompt'), "Selected group:");

    if(this.elId == "distTrove") {
        var sb = $('submitButton');
        sb.disabled = false;
        handleBuildTypes(f);
    }
}

TrovePicker.prototype.pickNoTrove = function(e) {
    setNodeAttribute($(this.elId.replace("-", "_") + 'Spec'), 'value', 'NONE');
    var n = e.src().troveName;

    oldEl = $(this.elId + 'selectionList');
    newEl = DIV({'id': this.elId + 'selectionList'},
        SPAN({'style': 'font-weight: bold;'}, 'Group: '),
        SPAN(null, "No " + n + " will be used for this build.")
    );
    swapDOM(oldEl, newEl);

    // return to getTroveVersions
    returnLink = A(null, returnImg(), " Back");
    returnLink.troveName = n;
    connect(returnLink, "onclick", this, "getAllTroveLabels");
    replaceChildNodes($(this.elId + 'return'), returnLink);
}

// Show all flavors available for a given trove and version
TrovePicker.prototype.showTroveFlavors = function(e) {
    this.version = e.src().version;
    this.label = e.src().label;

    oldList = $(this.elId + 'selectionList');
    ul = UL({ 'id': this.elId + 'selectionList' });

    for(var i in this.flavorCache[this.version]) {
        flavor = this.flavorCache[this.version][i];
        link = forwardLink(null, flavor[0]);
        link.name = this.troveName;
        link.version = this.version;
        link.flavor = flavor[1];
        link.shortFlavor = flavor[0];
        link.label = this.label;
        connect(link, "onclick", this, "pickFlavor");
        appendChildNodes(ul, link);
    }
    // return to getTroveVersions
    returnLink = A(null, returnImg(), " Back");
    returnLink.label = this.label;
    connect(returnLink, "onclick", this, "getTroveVersions");
    replaceChildNodes($(this.elId + 'return'), returnLink);

    swapDOM(oldList, ul);
    this.working(false);
    replaceChildNodes($(this.elId + 'prompt'), "Please choose a flavor:");
}

// Show all versions of a trove on a given label
TrovePicker.prototype.getTroveVersions = function(e) {
    this.label = e.src().label;
    var par = this;
    var key = this.label + "=" + this.troveName;

    var setupList = function(req) {
        var versionDict = req[0];
        var versionList = req[1];
        oldList = $(par.elId + 'selectionList');

        par.flavorCache = versionDict;

        if(!par.domCache[key]) {
            ul = UL({ 'id': par.elId + 'selectionList' });
            for(var i in versionList) {
                link = forwardLink(null, trailingRevision(versionList[i]));
                link.version = versionList[i];
                link.label = par.label;
                connect(link, "onclick", par, "showTroveFlavors");
                appendChildNodes(ul, link);
            }
            par.domCache[key] = ul;
        }
        swapDOM(oldList, par.domCache[key]);

        par.working(false);
        replaceChildNodes($(par.elId + 'prompt'), "Please choose a version:");

        // return to getAllTroveLabels
        returnLink = A(null, returnImg(), " Back");
        returnLink.troveName = par.troveName;
        connect(returnLink, "onclick", par, "getAllTroveLabels");
        replaceChildNodes($(par.elId + 'return'), returnLink);
    };

    var callback = function(aReq) {
        logDebug("[JSON] response: ", aReq.responseText);

        req = evalJSONRequest(aReq);
        versionCache[key] = req;
        setupList(req);
    };

    if(!versionCache[key]) {
        this.working(true);
        var req = new JsonRpcRequest("jsonrpc/", "getTroveVersions");
        req.setAuth(getCookieValue("pysid"));
        req.setCallback(callback);
        req.send(true, [this.projectId, this.label, this.troveName]);
    } else {
        setupList(versionCache[key]);
    }
}

// Fetch all labels a trove exists on a given server
TrovePicker.prototype.getAllTroveLabels = function(e) {
    if(e) {
        this.troveName = e.src().troveName;
    }
    var key = this.serverName + "=" + this.troveName;
    var par = this; // save the parent for the subfunction's use

    var setupList = function(labelList) {
        oldList = $(par.elId + 'selectionList');
        ul = UL({ 'id': par.elId + 'selectionList' });

        if(labelList.length < 1) {
            appendChildNodes(ul, LI(null, "No " + par.troveName + " packages found"));
        }
        for(var i in labelList) {
            link = forwardLink(null, labelList[i]);
            link.label = labelList[i];
            connect(link, "onclick", par, "getTroveVersions");
            appendChildNodes(ul, link);
        }
        if(labelList.length > 0 && par.allowNone) {
            link = forwardLink(null, "Do not use " + par.troveName + " for this build.");
            link.troveName = par.troveName;
            connect(link, "onclick", par, "pickNoTrove");
            appendChildNodes(ul, link);
        }
        swapDOM(oldList, ul);
        par.working(false);
        replaceChildNodes($(par.elId + 'prompt'), "Please choose a label:");

        if(par.allowNameChoice) {
            // return to getGroupTroves
            returnLink = A(null, returnImg(), " Back");
            connect(returnLink, "onclick", par, "getGroupTroves");
            replaceChildNodes($(par.elId + 'return'), returnLink);
        } else {
            replaceChildNodes($(par.elId + 'return'), disabledReturn());
        }
    };

    var callback = function(aReq) {
        logDebug("[JSON] response: ", aReq.responseText);

        labelList = evalJSONRequest(aReq);
        labelsCache[key] = labelList;
        setupList(labelList);
    };

    if(!labelsCache[key]) {
        this.working(true);
        var req = new JsonRpcRequest("jsonrpc/", "getAllTroveLabels");
        req.setAuth(getCookieValue("pysid"));
        req.setCallback(callback);
        req.send(true, [this.projectId, this.serverName, this.troveName]);
    } else {
        setupList(labelsCache[key]);
    }
}

TrovePicker.prototype.getGroupTroves = function() {
    var par = this;

    var setupList = function(troveList) {
        oldList = $(par.elId + 'selectionList');
        ul = UL({'id': par.elId + 'selectionList'});

        if(troveList.length < 1) {
            appendChildNodes(ul, LI(null, "No groups found. Cook a group with the Group Builder and try again."));
        }
        for(var i in troveList) {
            link = forwardLink(null, troveList[i]);
            link.troveName = troveList[i];
            connect(link, "onclick", par, "getAllTroveLabels");
            appendChildNodes(ul, link);
        }
        swapDOM(oldList, ul);
        par.working(false);
        replaceChildNodes($(par.elId + 'prompt'), "Please choose a group:");
        replaceChildNodes($(par.elId + 'return'), disabledReturn());
    };

    var callback = function(aReq) {
        logDebug(aReq.responseText);
        troveList = evalJSONRequest(aReq);
        par.troveList = troveList;
        setupList(troveList);
    };

    this.working(true);
    var req = new JsonRpcRequest("jsonrpc/", "getGroupTroves");
    req.setAuth(getCookieValue("pysid"));
    req.setCallback(callback);
    req.send(true, [this.projectId]);
}

TrovePicker.prototype.handleError = function() {
    this.working(false);
    oldList = $(this.elId + 'spinnerList');
    ul = UL({'id': this.elId + 'spinnerList'}, LI({'style': 'color: red;'}, "Error connecting to server"));
    swapDOM(oldList, ul);
}

var allTypes = map(parseInt, keys(buildTypeNames));

var defaultType = INSTALLABLE_ISO;
var x86_64Types = [INSTALLABLE_ISO];
var xenTypes = [RAW_HD_IMAGE, RAW_FS_IMAGE, TARBALL];

function handleBuildTypes(flavor) {
    forEach(allTypes, function(x) {
        var el = $('buildtype_' + x);
        var elLabel = $('buildtype_' + x + '_label');
        if(el) {
            el.disabled = false;
            setOpacity(elLabel, 1.0);
        }
        if(x == defaultType) {
            el.checked = true;
        }
    });

    if(flavor) {
        // bootableTypes are not currently compatible with x86_64
        // so, if that arch was selected, disable it:
        selectiveDisable(flavor, "x86_64", x86_64Types);

        // only allow a few build types to be built from a xen flavor:
        selectiveDisable(flavor, "xen", xenTypes);
    }
}

function selectiveDisable(flavor, flavorMatch, allowed) {
    if(flavor.match(flavorMatch)) {
        one = iter(allTypes);
        forEach(one, function (x) {
            var el = $('buildtype_'+x);
            var elLabel = $('buildtype_' + x + '_label');
            if(el) {
                if (findValue(allowed, x) == -1) {
                    el.disabled = true;
                    setOpacity(elLabel, 0.5);
                }
                else {
                    el.disabled = false;
                    setOpacity(elLabel, 1.0);
                    if(x == allowed[0]) {
                        el.checked = true;
                    }
                }
            }
        });
    }
}
<commit_msg>don't match \!xen<commit_after>//
// Copyright (C) 2006 rPath, Inc
// All Rights Reserved
//

var labelsCache = {};
var versionCache = {};

var staticPath = '';

var green = {'style': 'color: green; font-weight: bold;'};
var gray = {'style': 'color: gray;'};

var spinnerImg = 'apps/mint/images/circle-ball-dark-antialiased.gif';
var nextImg = 'apps/mint/images/next.gif';
var prevImg = 'apps/mint/images/prev.gif';
var prevImgDisabled = 'apps/mint/images/prev_disabled.gif';

//
// some DOM helper functions
//
function forwardLink(attrs, text) {
    return LI(null, A(attrs, IMG({'src': staticPath + nextImg}), " " + text));
}

function returnImg() {
    return IMG({'src': staticPath + prevImg});
}

function disabledReturn() {
    return DIV(gray, IMG({'src': staticPath + prevImgDisabled}), " Back");
}

function buildTroveSpec(n, v, f) {
    return n + "=" + v + "[" + f + "]";
}

function trailingRevision(v) {
    var parts = v.split(":");
    var revision = parts[parts.length-1];
    return revision;
}

// ctor
function TrovePicker(projectId, serverName, troveName, pickerId, mintStaticPath, allowNone) {
    this.projectId = projectId;
    this.serverName = serverName;
    this.troveName = troveName;
    this.elId = pickerId;
    staticPath = mintStaticPath;
    this.allowNone = allowNone;

    if(troveName) {
        this.allowNameChoice = false;
    }

    this.buildTrovePicker();
}

TrovePicker.prototype.troveName = null;
TrovePicker.prototype.serverName = null;
TrovePicker.prototype.label = null;
TrovePicker.prototype.version = null;
TrovePicker.prototype.flavorCache = null;
TrovePicker.prototype.domCache = {};
TrovePicker.prototype.allowNameChoice = true;
TrovePicker.prototype.aloowNone = false;

TrovePicker.prototype.working = function(isWorking) {
    if(isWorking) {
        showElement(this.elId + 'spinnerList');
        hideElement(this.elId + 'selectionList');
    }
    else {
        hideElement(this.elId + 'spinnerList');
        showElement(this.elId + 'selectionList');
    }
}

// Build the initial DOM for the trove picker and
// begin retrieving all leaves for a given
// trove name on a server.
TrovePicker.prototype.buildTrovePicker = function() {
    oldEl = $(this.elId);
    picker = DIV({'id': this.elId, 'class': 'trovePicker'});
    spinner = UL({'id': this.elId + 'spinnerList'},
        LI(null, "Loading...", IMG({'src': staticPath + spinnerImg}))
    );
    appendChildNodes(picker, SPAN({'id': this.elId + 'prompt', 'class': 'prompt'}),
        P({'id': this.elId + 'return'}),
        spinner,
        UL({'id': this.elId + 'selectionList'}),
        INPUT({'id': this.elId.replace("-", "_") + 'Spec', 'name': this.elId + 'Spec', 'type': 'hidden'})
    );
    swapDOM(oldEl, picker);

    if(this.troveName) {
        this.getAllTroveLabels();
    } else {
        this.getGroupTroves();
    }
}

// A flavor has been chosen--continue on
TrovePicker.prototype.pickFlavor = function(e) {
    var n = e.src().name;
    var v = e.src().version;
    var shortv = trailingRevision(v);
    var f = e.src().flavor;
    var shortf = e.src().shortFlavor;

    var troveSpec = buildTroveSpec(n, v, f);
    setNodeAttribute($(this.elId.replace("-", "_") + 'Spec'), 'value', troveSpec);

    oldEl = $(this.elId + 'selectionList');
    newEl = DIV({'id': this.elId + 'selectionList'},
        SPAN({'style': 'font-weight: bold;'}, 'Group: '),
        SPAN(null, n + "=" + shortv + " [" + shortf + "]")
    );
    swapDOM(oldEl, newEl);

    // return to getTroveVersions
    returnLink = A(null, returnImg(), " Back");
    returnLink.troveName = n;
    returnLink.version = e.src().version;
    returnLink.label = e.src().label;
    connect(returnLink, "onclick", this, "showTroveFlavors");
    replaceChildNodes($(this.elId + 'return'), returnLink);
    replaceChildNodes($(this.elId + 'prompt'), "Selected group:");

    if(this.elId == "distTrove") {
        var sb = $('submitButton');
        sb.disabled = false;
        handleBuildTypes(f);
    }
}

TrovePicker.prototype.pickNoTrove = function(e) {
    setNodeAttribute($(this.elId.replace("-", "_") + 'Spec'), 'value', 'NONE');
    var n = e.src().troveName;

    oldEl = $(this.elId + 'selectionList');
    newEl = DIV({'id': this.elId + 'selectionList'},
        SPAN({'style': 'font-weight: bold;'}, 'Group: '),
        SPAN(null, "No " + n + " will be used for this build.")
    );
    swapDOM(oldEl, newEl);

    // return to getTroveVersions
    returnLink = A(null, returnImg(), " Back");
    returnLink.troveName = n;
    connect(returnLink, "onclick", this, "getAllTroveLabels");
    replaceChildNodes($(this.elId + 'return'), returnLink);
}

// Show all flavors available for a given trove and version
TrovePicker.prototype.showTroveFlavors = function(e) {
    this.version = e.src().version;
    this.label = e.src().label;

    oldList = $(this.elId + 'selectionList');
    ul = UL({ 'id': this.elId + 'selectionList' });

    for(var i in this.flavorCache[this.version]) {
        flavor = this.flavorCache[this.version][i];
        link = forwardLink(null, flavor[0]);
        link.name = this.troveName;
        link.version = this.version;
        link.flavor = flavor[1];
        link.shortFlavor = flavor[0];
        link.label = this.label;
        connect(link, "onclick", this, "pickFlavor");
        appendChildNodes(ul, link);
    }
    // return to getTroveVersions
    returnLink = A(null, returnImg(), " Back");
    returnLink.label = this.label;
    connect(returnLink, "onclick", this, "getTroveVersions");
    replaceChildNodes($(this.elId + 'return'), returnLink);

    swapDOM(oldList, ul);
    this.working(false);
    replaceChildNodes($(this.elId + 'prompt'), "Please choose a flavor:");
}

// Show all versions of a trove on a given label
TrovePicker.prototype.getTroveVersions = function(e) {
    this.label = e.src().label;
    var par = this;
    var key = this.label + "=" + this.troveName;

    var setupList = function(req) {
        var versionDict = req[0];
        var versionList = req[1];
        oldList = $(par.elId + 'selectionList');

        par.flavorCache = versionDict;

        if(!par.domCache[key]) {
            ul = UL({ 'id': par.elId + 'selectionList' });
            for(var i in versionList) {
                link = forwardLink(null, trailingRevision(versionList[i]));
                link.version = versionList[i];
                link.label = par.label;
                connect(link, "onclick", par, "showTroveFlavors");
                appendChildNodes(ul, link);
            }
            par.domCache[key] = ul;
        }
        swapDOM(oldList, par.domCache[key]);

        par.working(false);
        replaceChildNodes($(par.elId + 'prompt'), "Please choose a version:");

        // return to getAllTroveLabels
        returnLink = A(null, returnImg(), " Back");
        returnLink.troveName = par.troveName;
        connect(returnLink, "onclick", par, "getAllTroveLabels");
        replaceChildNodes($(par.elId + 'return'), returnLink);
    };

    var callback = function(aReq) {
        logDebug("[JSON] response: ", aReq.responseText);

        req = evalJSONRequest(aReq);
        versionCache[key] = req;
        setupList(req);
    };

    if(!versionCache[key]) {
        this.working(true);
        var req = new JsonRpcRequest("jsonrpc/", "getTroveVersions");
        req.setAuth(getCookieValue("pysid"));
        req.setCallback(callback);
        req.send(true, [this.projectId, this.label, this.troveName]);
    } else {
        setupList(versionCache[key]);
    }
}

// Fetch all labels a trove exists on a given server
TrovePicker.prototype.getAllTroveLabels = function(e) {
    if(e) {
        this.troveName = e.src().troveName;
    }
    var key = this.serverName + "=" + this.troveName;
    var par = this; // save the parent for the subfunction's use

    var setupList = function(labelList) {
        oldList = $(par.elId + 'selectionList');
        ul = UL({ 'id': par.elId + 'selectionList' });

        if(labelList.length < 1) {
            appendChildNodes(ul, LI(null, "No " + par.troveName + " packages found"));
        }
        for(var i in labelList) {
            link = forwardLink(null, labelList[i]);
            link.label = labelList[i];
            connect(link, "onclick", par, "getTroveVersions");
            appendChildNodes(ul, link);
        }
        if(labelList.length > 0 && par.allowNone) {
            link = forwardLink(null, "Do not use " + par.troveName + " for this build.");
            link.troveName = par.troveName;
            connect(link, "onclick", par, "pickNoTrove");
            appendChildNodes(ul, link);
        }
        swapDOM(oldList, ul);
        par.working(false);
        replaceChildNodes($(par.elId + 'prompt'), "Please choose a label:");

        if(par.allowNameChoice) {
            // return to getGroupTroves
            returnLink = A(null, returnImg(), " Back");
            connect(returnLink, "onclick", par, "getGroupTroves");
            replaceChildNodes($(par.elId + 'return'), returnLink);
        } else {
            replaceChildNodes($(par.elId + 'return'), disabledReturn());
        }
    };

    var callback = function(aReq) {
        logDebug("[JSON] response: ", aReq.responseText);

        labelList = evalJSONRequest(aReq);
        labelsCache[key] = labelList;
        setupList(labelList);
    };

    if(!labelsCache[key]) {
        this.working(true);
        var req = new JsonRpcRequest("jsonrpc/", "getAllTroveLabels");
        req.setAuth(getCookieValue("pysid"));
        req.setCallback(callback);
        req.send(true, [this.projectId, this.serverName, this.troveName]);
    } else {
        setupList(labelsCache[key]);
    }
}

TrovePicker.prototype.getGroupTroves = function() {
    var par = this;

    var setupList = function(troveList) {
        oldList = $(par.elId + 'selectionList');
        ul = UL({'id': par.elId + 'selectionList'});

        if(troveList.length < 1) {
            appendChildNodes(ul, LI(null, "No groups found. Cook a group with the Group Builder and try again."));
        }
        for(var i in troveList) {
            link = forwardLink(null, troveList[i]);
            link.troveName = troveList[i];
            connect(link, "onclick", par, "getAllTroveLabels");
            appendChildNodes(ul, link);
        }
        swapDOM(oldList, ul);
        par.working(false);
        replaceChildNodes($(par.elId + 'prompt'), "Please choose a group:");
        replaceChildNodes($(par.elId + 'return'), disabledReturn());
    };

    var callback = function(aReq) {
        logDebug(aReq.responseText);
        troveList = evalJSONRequest(aReq);
        par.troveList = troveList;
        setupList(troveList);
    };

    this.working(true);
    var req = new JsonRpcRequest("jsonrpc/", "getGroupTroves");
    req.setAuth(getCookieValue("pysid"));
    req.setCallback(callback);
    req.send(true, [this.projectId]);
}

TrovePicker.prototype.handleError = function() {
    this.working(false);
    oldList = $(this.elId + 'spinnerList');
    ul = UL({'id': this.elId + 'spinnerList'}, LI({'style': 'color: red;'}, "Error connecting to server"));
    swapDOM(oldList, ul);
}

var allTypes = map(parseInt, keys(buildTypeNames));

var defaultType = INSTALLABLE_ISO;
var x86_64Types = [INSTALLABLE_ISO];
var xenTypes = [RAW_HD_IMAGE, RAW_FS_IMAGE, TARBALL];

function handleBuildTypes(flavor) {
    forEach(allTypes, function(x) {
        var el = $('buildtype_' + x);
        var elLabel = $('buildtype_' + x + '_label');
        if(el) {
            el.disabled = false;
            setOpacity(elLabel, 1.0);
        }
        if(x == defaultType) {
            el.checked = true;
        }
    });

    if(flavor) {
        // bootableTypes are not currently compatible with x86_64
        // so, if that arch was selected, disable it:
        selectiveDisable(flavor, "x86_64", x86_64Types);

        // only allow a few build types to be built from a xen flavor:
        selectiveDisable(flavor, ",xen", xenTypes);
    }
}

function selectiveDisable(flavor, flavorMatch, allowed) {
    if(flavor.match(flavorMatch)) {
        one = iter(allTypes);
        forEach(one, function (x) {
            var el = $('buildtype_'+x);
            var elLabel = $('buildtype_' + x + '_label');
            if(el) {
                if (findValue(allowed, x) == -1) {
                    el.disabled = true;
                    setOpacity(elLabel, 0.5);
                }
                else {
                    el.disabled = false;
                    setOpacity(elLabel, 1.0);
                    if(x == allowed[0]) {
                        el.checked = true;
                    }
                }
            }
        });
    }
}
<|endoftext|>