<commit_before>// Author: Nathan Van Gheem
// Contact: nathan@vangheem.us
// Version: 1.0
//
// Description:
//
// License:
//
// Copyright (C) 2010 Plone Foundation
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

define([
  'jquery',
  'underscore',
  'backbone',
  'js/ui/views/toolbar',
  'js/ui/views/buttongroup',
  'js/ui/views/button',
  'js/patterns/structure/views/table',
  'js/patterns/structure/views/selectionwell',
  'js/patterns/structure/views/order',
  'js/patterns/structure/views/tags',
  'js/patterns/structure/views/properties',
  'js/patterns/structure/views/workflow',
  'js/patterns/structure/views/delete',
  'js/patterns/structure/views/rename',
  'js/patterns/structure/views/selectionbutton',
  'js/patterns/structure/views/paging',
  'js/patterns/structure/views/textfilter',
  'js/patterns/structure/collections/result',
  'js/patterns/structure/collections/selected',
  'mockup-patterns-dropzone'
], function($, _, Backbone, Toolbar, ButtonGroup, ButtonView, TableView, SelectionWellView,
            OrderView, TagsView, PropertiesView, WorkflowView, DeleteView, RenameView,
            SelectionButtonView, PagingView, TextFilterView, ResultCollection,
            SelectedCollection, DropZone) {
  "use strict";

  var DISABLE_EVENT = 'DISABLE';

  var AppView = Backbone.View.extend({
    tagName: 'div',
    /* we setup binding here and specifically for every button so there is a
     * way to override default click event behavior.
     * Otherwise, if we bound all buttons to the same event, there is no way
     * to override the event or stop bubbling it. */
    buttonClickEvents: {
      'cut': 'cutCopyClickEvent',
      'copy': 'cutCopyClickEvent',
      'order': DISABLE_EVENT, //disable default
      'tags': DISABLE_EVENT, //disable
      'properties': DISABLE_EVENT,
      'workflow': DISABLE_EVENT,
      'delete': DISABLE_EVENT,
      'rename': DISABLE_EVENT
    },
    buttonViewMapping: {
      'folder.order': OrderView,
      'secondary.tags': TagsView,
      'secondary.properties': PropertiesView,
      'secondary.workflow': WorkflowView,
      'primary.delete': DeleteView,
      'secondary.rename': RenameView
    },
    status: '',
    statusType: 'info',
    pasteOperation: null,
    initialize: function(){
      var self = this;
      self.options.additionalCriterias = [];
      self.collection = new ResultCollection([], {
        url: self.options.collectionUrl
      });
      self.collection.queryParser = function(){
        var term = null;
        if(self.toolbar){
          term = self.toolbar.get('filter').term;
        }
        return JSON.stringify({
          criteria: self.queryHelper.getCriterias(term, {
            additionalCriterias: self.options.additionalCriterias
          })
        });
      };

      self.queryHelper = self.options.queryHelper;
      self.selectedCollection = new SelectedCollection();
      self.collection.queryHelper = self.queryHelper;
      self.tableView = new TableView({app: self});
      self.pagingView = new PagingView({app: self});
      self.pasteAllowed = self.options.pasteAllowed;

      /* initialize buttons */
      self.setupButtons();

      self.wellView = new SelectionWellView({
        collection: self.selectedCollection,
        button: self.toolbar.get('selected'),
        app: self
      });

      self.buttonViews = {};
      _.map(self.buttonViewMapping, function(ViewClass, key, list){
        var name = key.split('.');
        var group = name[0];
        var buttonName = name[1];
        self.buttonViews[key] = new ViewClass({
          button: self.buttons[group].get(buttonName),
          app: self
        });
      });

      self.toolbar.get('selected').disable();
      self.buttons.primary.disable();
      self.buttons.secondary.disable();

      self.selectedCollection.on('add remove reset', function(modal, collection) {
        if (collection.length) {
          self.toolbar.get('selected').enable();
          self.buttons.primary.enable();
          self.buttons.secondary.enable();
          if(!self.pasteAllowed){
            self.buttons.primary.get('paste').disable();
          }
        } else {
          this.toolbar.get('selected').disable();
          self.buttons.primary.disable();
          self.buttons.secondary.disable();
        }
      }, self);

      self.collection.on('sync', function(){
        // need to reload models inside selectedCollection so they get any
        // updated metadata
        var uids = [];
        self.selectedCollection.each(function(item){
          uids.push(item.attributes.UID);
        });
        self.queryHelper.search(
          'UID', 'plone.app.querystring.operation.list.contains',
          uids,
          function(data){
            _.each(data.results, function(attributes){
              var item = self.selectedCollection.getByUID(attributes.UID);
              item.attributes = attributes;
            });
          },
          false);
      });

      /* detect shift clicks */
      self.shift_clicked = false;
      $(document).bind('keyup keydown', function(e){
        self.shift_clicked = e.shiftKey;
      });
    },
    getSelectedUids: function(){
      var self = this;
      var uids = [];
      self.selectedCollection.each(function(item){
        uids.push(item.uid());
      });
      return uids;
    },
    defaultButtonClickEvent: function(button){
      var self = this;
      var data = null, callback = null;

      if(button.url){
        // handle ajax now

        if(arguments.length > 1){
          var arg1 = arguments[1];
          if(!arg1.preventDefault){
            data = arg1;
          }
        }
        if(arguments.length > 2){
          var arg2 = arguments[2];
          if(typeof(arg2) === 'function'){
            callback = arg2;
          }
        }
        if(data === null){
          data = {};
        }
        data.selection = JSON.stringify(self.getSelectedUids());
        data._authenticator = $('input[name="_authenticator"]').val();
        data.folder = self.options.queryHelper.getCurrentPath();
        data.pasteOperation = self.pasteOperation;

        var url = button.url.replace('{path}', self.options.queryHelper.getCurrentPath());
        $.ajax({
          url: url,
          type: 'POST',
          data: data,
          success: function(data){
            if(data.status === 'success'){
              self.collection.reset();
            }
            if(data.msg){
              // give status message somewhere...
              self.setStatus(data.msg);
            }
            if(callback !== null){
              callback(data);
            }
            self.collection.pager();
          },
          error: function(data){
            if(data.status === 404){
              window.alert('operation url "' + url + '" is not valid');
            }
          }
        });
      }
    },
    cutCopyClickEvent: function(button){
      var self = this;
      var txt;
      if(button.id === 'cut'){
        txt = 'cut ';
        self.pasteOperation = 'cut';
      }else{
        txt = 'copied ';
        self.pasteOperation = 'copy';
      }
      txt += 'selection';
      self.setStatus(txt);
      self.pasteAllowed = true;
      self.buttons.primary.get('paste').enable();
    },
    setupButtons: function(){
      var self = this;
      self.buttons = {};
      var items = [];
      items.push(new SelectionButtonView({
        title: 'Selected',
        collection: this.selectedCollection
      }));
      _.each(_.pairs(this.options.buttonGroups), function(group){
        var buttons = [];
        _.each(group[1], function(button){
          button = new ButtonView(button);
          buttons.push(button);
          // bind click events now...
          var ev = self.buttonClickEvents[button.id];
          if(ev !== DISABLE_EVENT){
            if(ev === undefined){
              ev = 'defaultButtonClickEvent'; // default click event
            }
            button.on('button:click', self[ev], self);
          }
        });
        self.buttons[group[0]] = new ButtonGroup({
          items: buttons,
          id: group[0],
          app: self
        });
        items.push(self.buttons[group[0]]);
      });
      items.push(new TextFilterView({
        id: 'filter',
        app: this
      }));
      this.toolbar = new Toolbar({
        items: items
      });
    },
    setStatus: function(txt, type){
      this.status = txt;
      if(type === undefined){
        type = 'info';
      }
      this.statusType = type;
      this.$('td.status').addClass(type).html(txt);
    },
    render: function(){
      var self = this;

      self.$el.append(self.toolbar.render().el);
      self.$el.append(self.wellView.render().el);
      _.each(self.buttonViews, function(view){
        self.$el.append(view.render().el);
      });

      self.$el.append(self.tableView.render().el);
      self.$el.append(self.pagingView.render().el);

      /* dropzone support */
      var uploadUrl = self.options.uploadUrl;
      if(uploadUrl){
        self.dropzone = new DropZone(self.$el, {
          className: 'structure-dropzone',
          clickable: false,
          url: uploadUrl,
          autoCleanResults: true,
          success: function(e, data){
            self.tableView.render();
          }
        }).dropzone;
        self.dropzone.on('sending', function(){
          self.$el.addClass('dropping');
        });
        self.dropzone.on('complete', function(){
          self.$el.removeClass('dropping');
        });
      }
      return self;
    }
  });

  return AppView;
});
<commit_msg>order results<commit_after>// Author: Nathan Van Gheem
// Contact: nathan@vangheem.us
// Version: 1.0
//
// Description:
//
// License:
//
// Copyright (C) 2010 Plone Foundation
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 51
// Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

define([
  'jquery',
  'underscore',
  'backbone',
  'js/ui/views/toolbar',
  'js/ui/views/buttongroup',
  'js/ui/views/button',
  'js/patterns/structure/views/table',
  'js/patterns/structure/views/selectionwell',
  'js/patterns/structure/views/order',
  'js/patterns/structure/views/tags',
  'js/patterns/structure/views/properties',
  'js/patterns/structure/views/workflow',
  'js/patterns/structure/views/delete',
  'js/patterns/structure/views/rename',
  'js/patterns/structure/views/selectionbutton',
  'js/patterns/structure/views/paging',
  'js/patterns/structure/views/textfilter',
  'js/patterns/structure/collections/result',
  'js/patterns/structure/collections/selected',
  'mockup-patterns-dropzone'
], function($, _, Backbone, Toolbar, ButtonGroup, ButtonView, TableView,
            SelectionWellView, OrderView, TagsView, PropertiesView,
            WorkflowView, DeleteView, RenameView, SelectionButtonView,
            PagingView, TextFilterView, ResultCollection, SelectedCollection,
            DropZone) {
  "use strict";

  var DISABLE_EVENT = 'DISABLE';

  var AppView = Backbone.View.extend({
    tagName: 'div',
    /* we setup binding here and specifically for every button so there is a
     * way to override default click event behavior.
     * Otherwise, if we bound all buttons to the same event, there is no way
     * to override the event or stop bubbling it. */
    buttonClickEvents: {
      'cut': 'cutCopyClickEvent',
      'copy': 'cutCopyClickEvent',
      'order': DISABLE_EVENT, //disable default
      'tags': DISABLE_EVENT, //disable
      'properties': DISABLE_EVENT,
      'workflow': DISABLE_EVENT,
      'delete': DISABLE_EVENT,
      'rename': DISABLE_EVENT
    },
    buttonViewMapping: {
      'folder.order': OrderView,
      'secondary.tags': TagsView,
      'secondary.properties': PropertiesView,
      'secondary.workflow': WorkflowView,
      'primary.delete': DeleteView,
      'secondary.rename': RenameView
    },
    status: '',
    statusType: 'info',
    pasteOperation: null,
    initialize: function(){
      var self = this;
      self.options.additionalCriterias = [];
      self.collection = new ResultCollection([], {
        url: self.options.collectionUrl
      });
      self.collection.queryParser = function(){
        var term = null;
        if(self.toolbar){
          term = self.toolbar.get('filter').term;
        }
        return JSON.stringify({
          criteria: self.queryHelper.getCriterias(term, {
            additionalCriterias: self.options.additionalCriterias
          }),
          sort_on: 'getObjPositionInParent'
        });
      };

      self.queryHelper = self.options.queryHelper;
      self.selectedCollection = new SelectedCollection();
      self.collection.queryHelper = self.queryHelper;
      self.tableView = new TableView({app: self});
      self.pagingView = new PagingView({app: self});
      self.pasteAllowed = self.options.pasteAllowed;

      /* initialize buttons */
      self.setupButtons();

      self.wellView = new SelectionWellView({
        collection: self.selectedCollection,
        button: self.toolbar.get('selected'),
        app: self
      });

      self.buttonViews = {};
      _.map(self.buttonViewMapping, function(ViewClass, key, list){
        var name = key.split('.');
        var group = name[0];
        var buttonName = name[1];
        self.buttonViews[key] = new ViewClass({
          button: self.buttons[group].get(buttonName),
          app: self
        });
      });

      self.toolbar.get('selected').disable();
      self.buttons.primary.disable();
      self.buttons.secondary.disable();

      self.selectedCollection.on('add remove reset', function(modal, collection) {
        if (collection.length) {
          self.toolbar.get('selected').enable();
          self.buttons.primary.enable();
          self.buttons.secondary.enable();
          if(!self.pasteAllowed){
            self.buttons.primary.get('paste').disable();
          }
        } else {
          this.toolbar.get('selected').disable();
          self.buttons.primary.disable();
          self.buttons.secondary.disable();
        }
      }, self);

      self.collection.on('sync', function(){
        // need to reload models inside selectedCollection so they get any
        // updated metadata
        var uids = [];
        self.selectedCollection.each(function(item){
          uids.push(item.attributes.UID);
        });
        self.queryHelper.search(
          'UID', 'plone.app.querystring.operation.list.contains',
          uids,
          function(data){
            _.each(data.results, function(attributes){
              var item = self.selectedCollection.getByUID(attributes.UID);
              item.attributes = attributes;
            });
          },
          false);
      });

      /* detect shift clicks */
      self.shift_clicked = false;
      $(document).bind('keyup keydown', function(e){
        self.shift_clicked = e.shiftKey;
      });
    },
    getSelectedUids: function(){
      var self = this;
      var uids = [];
      self.selectedCollection.each(function(item){
        uids.push(item.uid());
      });
      return uids;
    },
    defaultButtonClickEvent: function(button){
      var self = this;
      var data = null, callback = null;

      if(button.url){
        // handle ajax now

        if(arguments.length > 1){
          var arg1 = arguments[1];
          if(!arg1.preventDefault){
            data = arg1;
          }
        }
        if(arguments.length > 2){
          var arg2 = arguments[2];
          if(typeof(arg2) === 'function'){
            callback = arg2;
          }
        }
        if(data === null){
          data = {};
        }
        data.selection = JSON.stringify(self.getSelectedUids());
        data._authenticator = $('input[name="_authenticator"]').val();
        data.folder = self.options.queryHelper.getCurrentPath();
        data.pasteOperation = self.pasteOperation;

        var url = button.url.replace('{path}', self.options.queryHelper.getCurrentPath());
        $.ajax({
          url: url,
          type: 'POST',
          data: data,
          success: function(data){
            if(data.status === 'success'){
              self.collection.reset();
            }
            if(data.msg){
              // give status message somewhere...
              self.setStatus(data.msg);
            }
            if(callback !== null){
              callback(data);
            }
            self.collection.pager();
          },
          error: function(data){
            if(data.status === 404){
              window.alert('operation url "' + url + '" is not valid');
            }
          }
        });
      }
    },
    cutCopyClickEvent: function(button){
      var self = this;
      var txt;
      if(button.id === 'cut'){
        txt = 'cut ';
        self.pasteOperation = 'cut';
      }else{
        txt = 'copied ';
        self.pasteOperation = 'copy';
      }
      txt += 'selection';
      self.setStatus(txt);
      self.pasteAllowed = true;
      self.buttons.primary.get('paste').enable();
    },
    setupButtons: function(){
      var self = this;
      self.buttons = {};
      var items = [];
      items.push(new SelectionButtonView({
        title: 'Selected',
        collection: this.selectedCollection
      }));
      _.each(_.pairs(this.options.buttonGroups), function(group){
        var buttons = [];
        _.each(group[1], function(button){
          button = new ButtonView(button);
          buttons.push(button);
          // bind click events now...
          var ev = self.buttonClickEvents[button.id];
          if(ev !== DISABLE_EVENT){
            if(ev === undefined){
              ev = 'defaultButtonClickEvent'; // default click event
            }
            button.on('button:click', self[ev], self);
          }
        });
        self.buttons[group[0]] = new ButtonGroup({
          items: buttons,
          id: group[0],
          app: self
        });
        items.push(self.buttons[group[0]]);
      });
      items.push(new TextFilterView({
        id: 'filter',
        app: this
      }));
      this.toolbar = new Toolbar({
        items: items
      });
    },
    setStatus: function(txt, type){
      this.status = txt;
      if(type === undefined){
        type = 'info';
      }
      this.statusType = type;
      this.$('td.status').addClass(type).html(txt);
    },
    render: function(){
      var self = this;

      self.$el.append(self.toolbar.render().el);
      self.$el.append(self.wellView.render().el);
      _.each(self.buttonViews, function(view){
        self.$el.append(view.render().el);
      });

      self.$el.append(self.tableView.render().el);
      self.$el.append(self.pagingView.render().el);

      /* dropzone support */
      var uploadUrl = self.options.uploadUrl;
      if(uploadUrl){
        self.dropzone = new DropZone(self.$el, {
          className: 'structure-dropzone',
          clickable: false,
          url: uploadUrl,
          autoCleanResults: true,
          success: function(e, data){
            self.tableView.render();
          }
        }).dropzone;
        self.dropzone.on('sending', function(){
          self.$el.addClass('dropping');
        });
        self.dropzone.on('complete', function(){
          self.$el.removeClass('dropping');
        });
      }
      return self;
    }
  });

  return AppView;
});
<|endoftext|>


<commit_before>/**
 * Express configuration
 */

'use strict';

var express = require('express');
var favicon = require('serve-favicon');
var morgan = require('morgan');
var compression = require('compression');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var cookieParser = require('cookie-parser');
var errorHandler = require('errorhandler');
var path = require('path');
var config = require('./environment');
var passport = require('passport');

module.exports = function(app) {
  var env = app.get('env');

  app.set('views', config.root + '/server/views');
  app.engine('html', require('ejs').renderFile);
  app.set('view engine', 'html');
  app.use(compression());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(bodyParser.json());
  app.use(methodOverride());
  app.use(cookieParser());
  app.use(passport.initialize());

  if ('production' === env) {
    app.use(favicon(path.join(config.root, 'dist', 'favicon.ico')));
    app.use(express.static(path.join(config.root, 'dist')));
    app.set('appPath', config.root + '/dist');
    app.use(morgan('dev'));
  }

  if ('development' === env || 'test' === env) {
    app.use(require('connect-livereload')());
    app.use(express.static(path.join(config.root, '.tmp')));
    app.use(express.static(path.join(config.root, 'client')));
    app.set('appPath', 'client');
    app.use(morgan('dev'));
    app.use(errorHandler()); // Error handler - has to be last
  }
};
<commit_msg>Fix express config dev<commit_after>/**
 * Express configuration
 */

'use strict';

var express = require('express');
var favicon = require('serve-favicon');
var morgan = require('morgan');
var compression = require('compression');
var bodyParser = require('body-parser');
var methodOverride = require('method-override');
var cookieParser = require('cookie-parser');
var errorHandler = require('errorhandler');
var path = require('path');
var config = require('./environment');
var passport = require('passport');

module.exports = function(app) {
  var env = app.get('env');

  app.set('views', config.root + '/server/views');
  app.engine('html', require('ejs').renderFile);
  app.set('view engine', 'html');
  app.use(compression());
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(bodyParser.json());
  app.use(methodOverride());
  app.use(cookieParser());
  app.use(passport.initialize());

  if ('production' === env) {
    app.use(favicon(path.join(config.root, 'dist', 'favicon.ico')));
    app.use(express.static(path.join(config.root, 'dist')));
    app.set('appPath', config.root + '/dist');
    app.use(morgan('dev'));
  }

  if ('development' === env || 'test' === env) {
    app.use(require('connect-livereload')());
    app.use(express.static(path.join(config.root, '.tmp')));
    app.use(express.static(path.join(config.root, 'client')));
    app.set('appPath', config.root + '/client');
    app.use(morgan('dev'));
    app.use(errorHandler()); // Error handler - has to be last
  }
};
<|endoftext|>


<commit_before>// Copyright 2022, University of Colorado Boulder
const startTime = Date.now();

const execute = require( '../../../perennial-alias/js/common/execute' );
const _ = require( 'lodash' ); // eslint-disable-line
const fs = require( 'fs' );

// constants
// Don't use getActiveRepos() since it cannot be run from the root
const contents = fs.readFileSync( 'perennial/data/active-repos', 'utf8' ).trim();
const repos = contents.split( '\n' ).map( sim => sim.trim() );

const args = process.argv.slice( 2 );
const all = args.includes( '--all' );

/**
 * Identify all repos with uncommitted changes, and run the precommit hooks on them.
 *
 * USAGE:
 * cd ${root containing all repos}
 * node chipper/js/scripts/precommit-hook-multi.js
 *
 * OPTIONS:
 *
 * --all
 * Runs the precommit hooks on all repos.  Default behavior is to only check the repos with uncommitted changes.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
( async () => {

  let reposToTest = repos;

  if ( !all ) {

    // Detect uncommitted changes in each repo:
    // https://stackoverflow.com/questions/3878624/how-do-i-programmatically-determine-if-there-are-uncommitted-changes
    // git update-index --refresh
    // git diff-index --quiet HEAD --

    const updateIndexPromises = repos.map( repo => execute( 'git', 'update-index --refresh'.split( ' ' ), `${repo}`, {

      // resolve errors so Promise.all doesn't fail on first repo that cannot pull/rebase
      errors: 'resolve'
    } ) );
    await Promise.all( updateIndexPromises );

    const diffIndexPromises = repos.map( repo => execute( 'git', 'diff-index --quiet HEAD --'.split( ' ' ), `${repo}`, {

      // resolve errors so Promise.all doesn't fail on first repo that cannot pull/rebase
      errors: 'resolve'
    } ) );
    const results = await Promise.all( diffIndexPromises );

    // Find out which repos have uncommitted changes
    const changedRepos = [];
    for ( let i = 0; i < results.length; i++ ) {
      const repo = repos[ i ];
      const result = results[ i ];

      if ( result.code === 0 ) {

        // was up-to-date
      }
      else {

        // needs to push
        changedRepos.push( repo );
      }
    }
    console.log( 'detected changed repos: ' + changedRepos.join( ', ' ) );

    reposToTest = changedRepos;
  }

  // This is done sequentially so we don't spawn a bunch of uncached tsc at once, but in the future we may want to optimize
  // to run one sequentially then the rest in parallel
  for ( let i = 0; i < reposToTest.length; i++ ) {

    process.stdout.write( reposToTest[ i ] + ': ' );

    const result = await execute( 'node', [ '../chipper/js/scripts/hook-pre-commit.js' ], `${reposToTest[ i ]}`, {

      // resolve errors so Promise.all doesn't fail on first repo that cannot pull/rebase
      errors: 'resolve'
    } );
    if ( result.code === 0 ) {

      console.log( 'Success' );
    }
    else {
      console.log();
      result.stdout.trim().length > 0 && console.log( result.stdout.trim() );
      result.stderr.trim().length > 0 && console.log( result.stderr.trim() );
    }
  }

  const endTime = Date.now();
  console.log( 'Done in ' + ( endTime - startTime ) + 'ms' );
} )();<commit_msg>combine into a single child_process per repo, https://github.com/phetsims/chipper/issues/1301<commit_after>// Copyright 2022, University of Colorado Boulder
const startTime = Date.now();

const execute = require( '../../../perennial-alias/js/common/execute' );
const _ = require( 'lodash' ); // eslint-disable-line
const fs = require( 'fs' );
const child_process = require( 'child_process' );

// constants
// Don't use getActiveRepos() since it cannot be run from the root
const contents = fs.readFileSync( 'perennial/data/active-repos', 'utf8' ).trim();
const repos = contents.split( '\n' ).map( sim => sim.trim() );

const args = process.argv.slice( 2 );
const all = args.includes( '--all' );

/**
 * Identify all repos with uncommitted changes, and run the precommit hooks on them.
 *
 * USAGE:
 * cd ${root containing all repos}
 * node chipper/js/scripts/precommit-hook-multi.js
 *
 * OPTIONS:
 *
 * --all
 * Runs the precommit hooks on all repos.  Default behavior is to only check the repos with uncommitted changes.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
( async () => {

  let reposToTest = repos;

  if ( !all ) {

    const execOnRepo = repo => {
      return new Promise( resolve => {

        // Detect uncommitted changes in each repo:
        // https://stackoverflow.com/questions/3878624/how-do-i-programmatically-determine-if-there-are-uncommitted-changes
        // git update-index --refresh
        // git diff-index --quiet HEAD --
        // This will error if the diff-index shows any changes in the repo, otherwise error is null.
        child_process.exec( 'git update-index --refresh && git diff-index --quiet HEAD --', { cwd: repo },
          error => resolve( error ) );
      } );
    };

    const results = await Promise.all( repos.map( execOnRepo ) );

    // Find out which repos have uncommitted changes
    const changedRepos = [];
    for ( let i = 0; i < results.length; i++ ) {
      const repo = repos[ i ];
      const result = results[ i ];

      if ( result === null ) {

        // was up-to-date
      }
      else {

        // needs to push
        changedRepos.push( repo );
      }
    }
    console.log( 'detected changed repos: ' + changedRepos.join( ', ' ) );

    reposToTest = changedRepos;
  }

  // This is done sequentially so we don't spawn a bunch of uncached tsc at once, but in the future we may want to optimize
  // to run one sequentially then the rest in parallel
  for ( let i = 0; i < reposToTest.length; i++ ) {

    process.stdout.write( reposToTest[ i ] + ': ' );

    const result = await execute( 'node', [ '../chipper/js/scripts/hook-pre-commit.js' ], `${reposToTest[ i ]}`, {

      // resolve errors so Promise.all doesn't fail on first repo that cannot pull/rebase
      errors: 'resolve'
    } );
    if ( result.code === 0 ) {

      console.log( 'Success' );
    }
    else {
      console.log();
      result.stdout.trim().length > 0 && console.log( result.stdout.trim() );
      result.stderr.trim().length > 0 && console.log( result.stderr.trim() );
    }
  }

  const endTime = Date.now();
  console.log( 'Done in ' + ( endTime - startTime ) + 'ms' );
} )();<|endoftext|>


<commit_before>/* global expect */

/**
 * Mail
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
 * @copyright Christoph Wurst 2017
 */

define([
	'views/attachments',
	'jquery'
], function(AttachmentView, $) {

	describe('AttachmentsView', function() {

		var view;

		beforeEach(function() {
			$('body').append('

');
			view = new AttachmentView({});
		});

		afterEach(function() {
			view.remove();
			$('#mail-attachments-template').remove();
		});

		it('produces the correct HTML', function() {
			view.render();

			expect(this.AttachmentView.el.innerHTML)
				.toContain('

\n\
<button type="button" id="add-local-attachment" style="display: inline-block;">\n\
   Add attachment\n\
</button>\n\
<button type="button" id="add-cloud-attachment" style="display: inline-block;">\n\
   Add from Files\n\
</button>\n\
<input type="file" multiple="" id="local-attachments" style="display: none;">\n');
		});
	});
});
<commit_msg>Fix js tests<commit_after>/* global expect */

/**
 * Mail
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Christoph Wurst <christoph@winzerhof-wurst.at>
 * @copyright Christoph Wurst 2017
 */

define([
	'views/attachments',
	'jquery'
], function(AttachmentView, $) {

	describe('AttachmentsView', function() {

		var view;

		beforeEach(function() {
			$('body').append('

');
			view = new AttachmentView({});
		});

		afterEach(function() {
			view.remove();
			$('#mail-attachments-template').remove();
		});

		it('produces the correct HTML', function() {
			view.render();

			expect(view.el.innerHTML)
				.toContain('

\n\
<button type="button" id="add-local-attachment" style="display: inline-block;">\n\
   Add attachment\n\
</button>\n\
<button type="button" id="add-cloud-attachment" style="display: inline-block;">\n\
   Add from Files\n\
</button>\n\
<input type="file" multiple="" id="local-attachments" style="display: none;">\n');
		});
	});
});
<|endoftext|>


<commit_before>var electronPackager = Meteor.wrapAsync(Npm.require("electron-packager"));
var fs = Npm.require('fs');
var mkdirp = Meteor.wrapAsync(Npm.require('mkdirp'));
var path = Npm.require('path');
var proc = Npm.require('child_process');
var dirsum = Meteor.wrapAsync(Npm.require('lucy-dirsum'));
var readFile = Meteor.wrapAsync(fs.readFile);
var writeFile = Meteor.wrapAsync(fs.writeFile);
var stat = Meteor.wrapAsync(fs.stat);
var util = Npm.require('util');
var rimraf = Meteor.wrapAsync(Npm.require('rimraf'));
var ncp = Meteor.wrapAsync(Npm.require('ncp'));

var exec = Meteor.wrapAsync(function(command, options, callback){
  proc.exec(command, options, function(err, stdout, stderr){
    callback(err, {stdout: stdout, stderr: stderr});
  });
});

var exists = function(path) {
  try {
    stat(path);
    return true;
  } catch(e) {
    return false;
  }
};

var projectRoot = function(){
  if (process.platform === "win32"){
    return process.env.METEOR_SHELL_DIR.split(".meteor")[0];
  } else {
    return process.env.PWD;
  }
};

var electronSettings = Meteor.settings.electron || {};

var IS_MAC = (process.platform === 'darwin');

/* Entry Point */
createBinaries = function() {
  var results = {};
  var builds;
  if (electronSettings.builds){
    builds = electronSettings.builds;
  } else {
    //just build for the current platform/architecture
    // TODO(wearhere): Re-enable this once https://github.com/rissem/meteor-electron/issues/39 is
    // fixed.
    // builds = [{platform: process.platform, arch: process.arch}];
    console.error('You must specify one or more builds in Meteor.settings.electron.');
    return results;
  }

  if (_.isEmpty(builds)) {
    console.error('No builds available for this platform.');
    return results;
  }

  builds.forEach(function(buildInfo){
    var buildRequired = false;

    var buildDirs = createBuildDirectories(buildInfo);

    /* Write out Electron application files */
    var appVersion = electronSettings.version;
    var appName = electronSettings.name || "electron";
    var appDescription = electronSettings.description;

    var resolvedAppSrcDir;
    if (electronSettings.appSrcDir) {
      resolvedAppSrcDir = path.join(projectRoot(), electronSettings.appSrcDir);
    } else {
      // See http://stackoverflow.com/a/29745318/495611 for how the package asset directory is derived.
      // We can't read this from the project directory like the user-specified app directory since
      // we may be loaded from Atmosphere rather than locally.
      resolvedAppSrcDir = path.join(process.cwd(), 'assets', 'packages', 'quark_electron', 'app');
    }

    // Check if the package.json has changed before copying over the app files, to account for
    // changes made in the app source dir.
    var packagePath = packageJSONPath(resolvedAppSrcDir);
    var packageJSON = Npm.require(packagePath);

    // Fill in missing package.json fields (note: before the comparison).
    // This isn't just a convenience--`Squirrel.Windows` requires the description and version.
    packageJSON = _.defaults(packageJSON, {
      name: appName && appName.toLowerCase().replace(/\s/g, '-'),
      productName: appName,
      description: appDescription,
      version: appVersion
    });
    // Check if the package has changed before we possibly copy over the app source since that will
    // of course sync `package.json`.
    var packageHasChanged = packageJSONHasChanged(packageJSON, buildDirs.app);

    if (appHasChanged(resolvedAppSrcDir, buildDirs.working)) {
      buildRequired = true;

      // Copy the app directory over while also pruning old files.
      if (IS_MAC) {
        // Ensure that the app source directory ends in a slash so we copy its contents.
        // Except node_modules from pruning since we prune that below.
        // TODO(wearhere): `rsync` also uses checksums to only copy what's necessary so theoretically we
        // could always `rsync` rather than checking if the directory's changed first.
         exec(util.format('rsync -a --delete --force --filter="P node_modules" "%s" "%s"',
          path.join(resolvedAppSrcDir, '/'), buildDirs.app));
      } else {
        // TODO(wearhere): More efficient sync on Windows (where `rsync` isn't available.)
        rimraf(buildDirs.app);
        mkdirp(buildDirs.app);
        ncp(resolvedAppSrcDir, buildDirs.app);
      }
    }

    /* Write out the application package.json */
    // Do this after writing out the application files, since that will overwrite `package.json`.
    if (packageHasChanged || !IS_MAC) {
      // For some reason when this file isn't manually removed it fails to be overwritten with an
      // EACCES error.
      rimraf(packageJSONPath(buildDirs.app));
      writeFile(packageJSONPath(buildDirs.app), JSON.stringify(packageJSON));

      exec("npm install && npm prune", {cwd: buildDirs.app});
    }

    /* Write out Electron Settings */
    var settings = _.defaults({}, electronSettings, {
      rootUrl: process.env.ROOT_URL
    });

    var signingIdentity = electronSettings.sign;
    var signingIdentityRequiredAndMissing = false;
    if (canServeUpdates(buildInfo.platform)) {
      // Enable the auto-updater if possible.
      if ((buildInfo.platform === 'darwin') && !signingIdentity) {
        // If the app isn't signed and we try to use the auto-updater, it will
        // throw an exception. Log an error if the settings have changed, below.
        signingIdentityRequiredAndMissing = true;
      } else {
        settings.updateFeedUrl = settings.rootUrl + UPDATE_FEED_PATH;
      }
    }

    if (settingsHaveChanged(settings, buildDirs.app)) {
      if (signingIdentityRequiredAndMissing) {
        console.error('Developer ID signing identity is missing: remote updates will not work.');
      }
      buildRequired = true;
      writeFile(settingsPath(buildDirs.app), JSON.stringify(settings));
    }

    var packagerSettings = getPackagerSettings(buildInfo, buildDirs);
    if (packagerSettings.icon && iconHasChanged(packagerSettings.icon, buildDirs.working)) {
      buildRequired = true;
    }

    // TODO(wearhere): If/when the signing identity expires, does its name change? If not, we'll need
    // to force the app to be rebuilt somehow.

    if (packagerSettingsHaveChanged(packagerSettings, buildDirs.working)) {
      buildRequired = true;
    }

    var app = appPath(appName, buildInfo.platform, buildInfo.arch, buildDirs.build);
    if (!exists(app)) {
      buildRequired = true;
    }

    /* Create Build */
    if (buildRequired) {
      var build = electronPackager(packagerSettings)[0];
      console.log("Build created for ", buildInfo.platform, buildInfo.arch, "at", build);
    }

    /* Package the build for download if specified. */
    // TODO(rissem): make this platform independent

    if (electronSettings.autoPackage && (buildInfo.platform === 'darwin')) {
      // The auto-updater framework only supports installing ZIP releases:
      // https://github.com/Squirrel/Squirrel.Mac#update-json-format
      var downloadName = (appName || "app") + ".zip";
      var compressedDownload = path.join(buildDirs.final, downloadName);

      if (buildRequired || !exists(compressedDownload)) {
        // Use `ditto` to ZIP the app because I couldn't find a good npm module to do it and also that's
        // what a couple of other related projects do:
        // - https://github.com/Squirrel/Squirrel.Mac/blob/8caa2fa2007b29a253f7f5be8fc9f36ace6aa30e/Squirrel/SQRLZipArchiver.h#L24
        // - https://github.com/jenslind/electron-release/blob/4a2a701c18664ec668c3570c3907c0fee72f5e2a/index.js#L109
        exec('ditto -ck --sequesterRsrc --keepParent "' + app + '" "' + compressedDownload + '"');
        console.log("Downloadable created at", compressedDownload);
      }
    }

    results[buildInfo.platform + "-" + buildInfo.arch] = {
      app: app,
      buildRequired: buildRequired
    };
  });

  return results;
};

function createBuildDirectories(build){
  // Use a predictable directory so that other scripts can locate the builds, also so that the builds
  // may be cached:

  var workingDir = path.join(projectRoot(), '.meteor-electron', build.platform + "-" + build.arch);
  mkdirp(workingDir);

  //TODO consider seeding the binaryDir from package assets so package
  //could work without an internet connection

  // *binaryDir* holds the vanilla electron apps
  var binaryDir = path.join(workingDir, "releases");
  mkdirp(binaryDir);

  // *appDir* holds the electron application that points to a meteor app
  var appDir = path.join(workingDir, "apps");
  mkdirp(appDir);

  // *buildDir* contains the uncompressed apps
  var buildDir = path.join(workingDir, "builds");
  mkdirp(buildDir);

  // *finalDir* contains zipped apps ready to be downloaded
  var finalDir = path.join(workingDir, "final");
  mkdirp(finalDir);

  return {
    working: workingDir,
    binary: binaryDir,
    app: appDir,
    build: buildDir,
    final: finalDir
  };
}

function getPackagerSettings(buildInfo, dirs){
  var packagerSettings = {
    dir: dirs.app,
    name: electronSettings.name || "Electron",
    platform: buildInfo.platform,
    arch: buildInfo.arch,
    version: "0.36.1",
    out: dirs.build,
    cache: dirs.binary,
    overwrite: true,
    // The EXE's `ProductName` is the preferred title of application shortcuts created by `Squirrel.Windows`.
    // If we don't set it, it will default to "Electron".
    'version-string': {
      ProductName: electronSettings.name || 'Electron'
    }
  };

  if (electronSettings.version) {
    packagerSettings['app-version'] = electronSettings.version;
  }
  if (electronSettings.icon) {
    var icon = electronSettings.icon[buildInfo.platform];
    if (icon) {
      var iconPath = path.join(projectRoot(), icon);
      packagerSettings.icon = iconPath;
    }
  }
  if (electronSettings.sign) {
    packagerSettings.sign = electronSettings.sign;
  }
  if (electronSettings.protocols) {
    packagerSettings.protocols = electronSettings.protocols;
  }
  return packagerSettings;
}

function settingsPath(appDir) {
  return path.join(appDir, 'electronSettings.json');
}

function settingsHaveChanged(settings, appDir) {
  var electronSettingsPath = settingsPath(appDir);
  var existingElectronSettings;
  try {
    existingElectronSettings = Npm.require(electronSettingsPath);
  } catch(e) {
    // No existing settings.
  }
  return !existingElectronSettings || !_.isEqual(settings, existingElectronSettings);
}

function appHasChanged(appSrcDir, workingDir) {
  var appChecksumPath = path.join(workingDir, 'appChecksum.txt');
  var existingAppChecksum;
  try {
    existingAppChecksum = readFile(appChecksumPath, 'utf8');
  } catch(e) {
    // No existing checksum.
  }

  var appChecksum = dirsum(appSrcDir);
  if (appChecksum !== existingAppChecksum) {
    writeFile(appChecksumPath, appChecksum);
    return true;
  } else {
    return false;
  }
}

function packageJSONPath(appDir) {
  return path.join(appDir, 'package.json');
}

function packageJSONHasChanged(packageJSON, appDir) {
  var packagePath = packageJSONPath(appDir);
  var existingPackageJSON;
  try {
    existingPackageJSON = Npm.require(packagePath);
  } catch(e) {
    // No existing package.
  }

  return !existingPackageJSON || !_.isEqual(packageJSON, existingPackageJSON);
}

function packagerSettingsHaveChanged(settings, workingDir) {
  var settingsPath = path.join(workingDir, 'lastUsedPackagerSettings.json');
  var existingPackagerSettings;
  try {
    existingPackagerSettings = Npm.require(settingsPath);
  } catch(e) {
    // No existing settings.
  }

  if (!existingPackagerSettings || !_.isEqual(settings, existingPackagerSettings)) {
    writeFile(settingsPath, JSON.stringify(settings));
    return true;
  } else {
    return false;
  }
}

function iconHasChanged(iconPath, workingDir) {
  var iconChecksumPath = path.join(workingDir, 'iconChecksum.txt');
  var existingIconChecksum;
  try {
    existingIconChecksum = readFile(iconChecksumPath, 'utf8');
  } catch(e) {
    // No existing checksum.
  }

  // `dirsum` works for files too.
  var iconChecksum = dirsum(iconPath);
  if (iconChecksum !== existingIconChecksum) {
    writeFile(iconChecksumPath, iconChecksum);
    return true;
  } else {
    return false;
  }
}

function appPath(appName, platform, arch, buildDir) {
  var appExtension = (platform === 'darwin') ? '.app' : '.exe';
  return path.join(buildDir, [appName, platform, arch].join('-'), appName + appExtension);
}
<commit_msg>Conditionally rewrite the package.json/reinstall node modules on Windows<commit_after>var electronPackager = Meteor.wrapAsync(Npm.require("electron-packager"));
var fs = Npm.require('fs');
var mkdirp = Meteor.wrapAsync(Npm.require('mkdirp'));
var path = Npm.require('path');
var proc = Npm.require('child_process');
var dirsum = Meteor.wrapAsync(Npm.require('lucy-dirsum'));
var readFile = Meteor.wrapAsync(fs.readFile);
var writeFile = Meteor.wrapAsync(fs.writeFile);
var stat = Meteor.wrapAsync(fs.stat);
var util = Npm.require('util');
var rimraf = Meteor.wrapAsync(Npm.require('rimraf'));
var ncp = Meteor.wrapAsync(Npm.require('ncp'));

var exec = Meteor.wrapAsync(function(command, options, callback){
  proc.exec(command, options, function(err, stdout, stderr){
    callback(err, {stdout: stdout, stderr: stderr});
  });
});

var exists = function(path) {
  try {
    stat(path);
    return true;
  } catch(e) {
    return false;
  }
};

var projectRoot = function(){
  if (process.platform === "win32"){
    return process.env.METEOR_SHELL_DIR.split(".meteor")[0];
  } else {
    return process.env.PWD;
  }
};

var electronSettings = Meteor.settings.electron || {};

var IS_MAC = (process.platform === 'darwin');

/* Entry Point */
createBinaries = function() {
  var results = {};
  var builds;
  if (electronSettings.builds){
    builds = electronSettings.builds;
  } else {
    //just build for the current platform/architecture
    // TODO(wearhere): Re-enable this once https://github.com/rissem/meteor-electron/issues/39 is
    // fixed.
    // builds = [{platform: process.platform, arch: process.arch}];
    console.error('You must specify one or more builds in Meteor.settings.electron.');
    return results;
  }

  if (_.isEmpty(builds)) {
    console.error('No builds available for this platform.');
    return results;
  }

  builds.forEach(function(buildInfo){
    var buildRequired = false;

    var buildDirs = createBuildDirectories(buildInfo);

    /* Write out Electron application files */
    var appVersion = electronSettings.version;
    var appName = electronSettings.name || "electron";
    var appDescription = electronSettings.description;

    var resolvedAppSrcDir;
    if (electronSettings.appSrcDir) {
      resolvedAppSrcDir = path.join(projectRoot(), electronSettings.appSrcDir);
    } else {
      // See http://stackoverflow.com/a/29745318/495611 for how the package asset directory is derived.
      // We can't read this from the project directory like the user-specified app directory since
      // we may be loaded from Atmosphere rather than locally.
      resolvedAppSrcDir = path.join(process.cwd(), 'assets', 'packages', 'quark_electron', 'app');
    }

    // Check if the package.json has changed before copying over the app files, to account for
    // changes made in the app source dir.
    var packagePath = packageJSONPath(resolvedAppSrcDir);
    var packageJSON = Npm.require(packagePath);

    // Fill in missing package.json fields (note: before the comparison).
    // This isn't just a convenience--`Squirrel.Windows` requires the description and version.
    packageJSON = _.defaults(packageJSON, {
      name: appName && appName.toLowerCase().replace(/\s/g, '-'),
      productName: appName,
      description: appDescription,
      version: appVersion
    });
    // Check if the package has changed before we possibly copy over the app source since that will
    // of course sync `package.json`.
    var packageHasChanged = packageJSONHasChanged(packageJSON, buildDirs.app);

    var didOverwriteNodeModules = false;

    if (appHasChanged(resolvedAppSrcDir, buildDirs.working)) {
      buildRequired = true;

      // Copy the app directory over while also pruning old files.
      if (IS_MAC) {
        // Ensure that the app source directory ends in a slash so we copy its contents.
        // Except node_modules from pruning since we prune that below.
        // TODO(wearhere): `rsync` also uses checksums to only copy what's necessary so theoretically we
        // could always `rsync` rather than checking if the directory's changed first.
         exec(util.format('rsync -a --delete --force --filter="P node_modules" "%s" "%s"',
          path.join(resolvedAppSrcDir, '/'), buildDirs.app));
      } else {
        // TODO(wearhere): More efficient sync on Windows (where `rsync` isn't available.)
        rimraf(buildDirs.app);
        mkdirp(buildDirs.app);
        ncp(resolvedAppSrcDir, buildDirs.app);
        didOverwriteNodeModules = true;
      }
    }

    /* Write out the application package.json */
    // Do this after writing out the application files, since that will overwrite `package.json`.
    // This logic is a little bit inefficient: it's not the case that _every_ change to package.json
    // means that we have to reinstall the node modules; and if we overwrote the node modules, we
    // don't necessarily have to rewrite `package.json`. But doing it altogether is simplest.
    if (packageHasChanged || didOverwriteNodeModules) {
      // For some reason when this file isn't manually removed it fails to be overwritten with an
      // EACCES error.
      rimraf(packageJSONPath(buildDirs.app));
      writeFile(packageJSONPath(buildDirs.app), JSON.stringify(packageJSON));

      exec("npm install && npm prune", {cwd: buildDirs.app});
    }

    /* Write out Electron Settings */
    var settings = _.defaults({}, electronSettings, {
      rootUrl: process.env.ROOT_URL
    });

    var signingIdentity = electronSettings.sign;
    var signingIdentityRequiredAndMissing = false;
    if (canServeUpdates(buildInfo.platform)) {
      // Enable the auto-updater if possible.
      if ((buildInfo.platform === 'darwin') && !signingIdentity) {
        // If the app isn't signed and we try to use the auto-updater, it will
        // throw an exception. Log an error if the settings have changed, below.
        signingIdentityRequiredAndMissing = true;
      } else {
        settings.updateFeedUrl = settings.rootUrl + UPDATE_FEED_PATH;
      }
    }

    if (settingsHaveChanged(settings, buildDirs.app)) {
      if (signingIdentityRequiredAndMissing) {
        console.error('Developer ID signing identity is missing: remote updates will not work.');
      }
      buildRequired = true;
      writeFile(settingsPath(buildDirs.app), JSON.stringify(settings));
    }

    var packagerSettings = getPackagerSettings(buildInfo, buildDirs);
    if (packagerSettings.icon && iconHasChanged(packagerSettings.icon, buildDirs.working)) {
      buildRequired = true;
    }

    // TODO(wearhere): If/when the signing identity expires, does its name change? If not, we'll need
    // to force the app to be rebuilt somehow.

    if (packagerSettingsHaveChanged(packagerSettings, buildDirs.working)) {
      buildRequired = true;
    }

    var app = appPath(appName, buildInfo.platform, buildInfo.arch, buildDirs.build);
    if (!exists(app)) {
      buildRequired = true;
    }

    /* Create Build */
    if (buildRequired) {
      var build = electronPackager(packagerSettings)[0];
      console.log("Build created for ", buildInfo.platform, buildInfo.arch, "at", build);
    }

    /* Package the build for download if specified. */
    // TODO(rissem): make this platform independent

    if (electronSettings.autoPackage && (buildInfo.platform === 'darwin')) {
      // The auto-updater framework only supports installing ZIP releases:
      // https://github.com/Squirrel/Squirrel.Mac#update-json-format
      var downloadName = (appName || "app") + ".zip";
      var compressedDownload = path.join(buildDirs.final, downloadName);

      if (buildRequired || !exists(compressedDownload)) {
        // Use `ditto` to ZIP the app because I couldn't find a good npm module to do it and also that's
        // what a couple of other related projects do:
        // - https://github.com/Squirrel/Squirrel.Mac/blob/8caa2fa2007b29a253f7f5be8fc9f36ace6aa30e/Squirrel/SQRLZipArchiver.h#L24
        // - https://github.com/jenslind/electron-release/blob/4a2a701c18664ec668c3570c3907c0fee72f5e2a/index.js#L109
        exec('ditto -ck --sequesterRsrc --keepParent "' + app + '" "' + compressedDownload + '"');
        console.log("Downloadable created at", compressedDownload);
      }
    }

    results[buildInfo.platform + "-" + buildInfo.arch] = {
      app: app,
      buildRequired: buildRequired
    };
  });

  return results;
};

function createBuildDirectories(build){
  // Use a predictable directory so that other scripts can locate the builds, also so that the builds
  // may be cached:

  var workingDir = path.join(projectRoot(), '.meteor-electron', build.platform + "-" + build.arch);
  mkdirp(workingDir);

  //TODO consider seeding the binaryDir from package assets so package
  //could work without an internet connection

  // *binaryDir* holds the vanilla electron apps
  var binaryDir = path.join(workingDir, "releases");
  mkdirp(binaryDir);

  // *appDir* holds the electron application that points to a meteor app
  var appDir = path.join(workingDir, "apps");
  mkdirp(appDir);

  // *buildDir* contains the uncompressed apps
  var buildDir = path.join(workingDir, "builds");
  mkdirp(buildDir);

  // *finalDir* contains zipped apps ready to be downloaded
  var finalDir = path.join(workingDir, "final");
  mkdirp(finalDir);

  return {
    working: workingDir,
    binary: binaryDir,
    app: appDir,
    build: buildDir,
    final: finalDir
  };
}

function getPackagerSettings(buildInfo, dirs){
  var packagerSettings = {
    dir: dirs.app,
    name: electronSettings.name || "Electron",
    platform: buildInfo.platform,
    arch: buildInfo.arch,
    version: "0.36.1",
    out: dirs.build,
    cache: dirs.binary,
    overwrite: true,
    // The EXE's `ProductName` is the preferred title of application shortcuts created by `Squirrel.Windows`.
    // If we don't set it, it will default to "Electron".
    'version-string': {
      ProductName: electronSettings.name || 'Electron'
    }
  };

  if (electronSettings.version) {
    packagerSettings['app-version'] = electronSettings.version;
  }
  if (electronSettings.icon) {
    var icon = electronSettings.icon[buildInfo.platform];
    if (icon) {
      var iconPath = path.join(projectRoot(), icon);
      packagerSettings.icon = iconPath;
    }
  }
  if (electronSettings.sign) {
    packagerSettings.sign = electronSettings.sign;
  }
  if (electronSettings.protocols) {
    packagerSettings.protocols = electronSettings.protocols;
  }
  return packagerSettings;
}

function settingsPath(appDir) {
  return path.join(appDir, 'electronSettings.json');
}

function settingsHaveChanged(settings, appDir) {
  var electronSettingsPath = settingsPath(appDir);
  var existingElectronSettings;
  try {
    existingElectronSettings = Npm.require(electronSettingsPath);
  } catch(e) {
    // No existing settings.
  }
  return !existingElectronSettings || !_.isEqual(settings, existingElectronSettings);
}

function appHasChanged(appSrcDir, workingDir) {
  var appChecksumPath = path.join(workingDir, 'appChecksum.txt');
  var existingAppChecksum;
  try {
    existingAppChecksum = readFile(appChecksumPath, 'utf8');
  } catch(e) {
    // No existing checksum.
  }

  var appChecksum = dirsum(appSrcDir);
  if (appChecksum !== existingAppChecksum) {
    writeFile(appChecksumPath, appChecksum);
    return true;
  } else {
    return false;
  }
}

function packageJSONPath(appDir) {
  return path.join(appDir, 'package.json');
}

function packageJSONHasChanged(packageJSON, appDir) {
  var packagePath = packageJSONPath(appDir);
  var existingPackageJSON;
  try {
    existingPackageJSON = Npm.require(packagePath);
  } catch(e) {
    // No existing package.
  }

  return !existingPackageJSON || !_.isEqual(packageJSON, existingPackageJSON);
}

function packagerSettingsHaveChanged(settings, workingDir) {
  var settingsPath = path.join(workingDir, 'lastUsedPackagerSettings.json');
  var existingPackagerSettings;
  try {
    existingPackagerSettings = Npm.require(settingsPath);
  } catch(e) {
    // No existing settings.
  }

  if (!existingPackagerSettings || !_.isEqual(settings, existingPackagerSettings)) {
    writeFile(settingsPath, JSON.stringify(settings));
    return true;
  } else {
    return false;
  }
}

function iconHasChanged(iconPath, workingDir) {
  var iconChecksumPath = path.join(workingDir, 'iconChecksum.txt');
  var existingIconChecksum;
  try {
    existingIconChecksum = readFile(iconChecksumPath, 'utf8');
  } catch(e) {
    // No existing checksum.
  }

  // `dirsum` works for files too.
  var iconChecksum = dirsum(iconPath);
  if (iconChecksum !== existingIconChecksum) {
    writeFile(iconChecksumPath, iconChecksum);
    return true;
  } else {
    return false;
  }
}

function appPath(appName, platform, arch, buildDir) {
  var appExtension = (platform === 'darwin') ? '.app' : '.exe';
  return path.join(buildDir, [appName, platform, arch].join('-'), appName + appExtension);
}
<|endoftext|>


<commit_before>import {urlReEx} from '../constants';

const regEXMap = {
    url : urlReEx
};
//todo: Check why is this done
window.r2 = regEXMap.url;

export const baseSchema = {
    'mainObjectCheck':{
        keysCheck: ['merchantTxnId', 'amount', 'currency', 'userDetails', 'returnUrl',
            'notifyUrl', 'requestSignature', 'merchantAccessKey', 'customParameters', 'requestOrigin', 'mode', 'offerToken']
    },
    returnUrl: {
        presence: true,
        custFormat: {
            pattern: regEXMap.url,
            message: 'should be proper URL string'
        }
    },
    notifyUrl: {
        custFormat: {
            pattern: regEXMap.url,
            message: 'should be proper URL string'
        }
    },
    requestSignature: { presence: true },
    merchantTxnId : { presence: true },
    amount: { presence: true }, //todo: can write custom validator as - numOrStr: true
    //currency: {presence: true},
    //merchantAccessKey: { presence: true },
    userDetails: {
        presence: true,
        keysCheck: ['email', 'firstName', 'lastName', 'address', 'mobileNo']
    },
    'userDetails.email': { presence: true, email: true },
    'userDetails.address': {
        keysCheck: ['street1', 'street2', 'city', 'state', 'country', 'zip']
    },
    'userDetails.mobileNo' : {
        length: {maximum: 15}
    }
};<commit_msg>email and mobile made non mandatory<commit_after>import {urlReEx} from '../constants';

const regEXMap = {
    url : urlReEx
};
//todo: Check why is this done
window.r2 = regEXMap.url;

export const baseSchema = {
    'mainObjectCheck':{
        keysCheck: ['merchantTxnId', 'amount', 'currency', 'userDetails', 'returnUrl',
            'notifyUrl', 'requestSignature', 'merchantAccessKey', 'customParameters', 'requestOrigin', 'mode', 'offerToken']
    },
    returnUrl: {
        presence: true,
        custFormat: {
            pattern: regEXMap.url,
            message: 'should be proper URL string'
        }
    },
    notifyUrl: {
        custFormat: {
            pattern: regEXMap.url,
            message: 'should be proper URL string'
        }
    },
    requestSignature: { presence: true },
    merchantTxnId : { presence: true },
    amount: { presence: true }, //todo: can write custom validator as - numOrStr: true
    //currency: {presence: true},
    //merchantAccessKey: { presence: true },
    userDetails: {
        presence: true,
        keysCheck: ['email', 'firstName', 'lastName', 'address', 'mobileNo']
    },
    'userDetails.email': { //presence: true,
                           email: true },
    'userDetails.address': {
        keysCheck: ['street1', 'street2', 'city', 'state', 'country', 'zip']
    },
    'userDetails.mobileNo' : {
        length: {maximum: 15}
    }
};<|endoftext|>


<commit_before>/*
 * PurePO2JSON v3.0.0
 * by André Zanghelini (An_dz)
 *
 * with previous contributions by Roland Reck (QuHno)
 * IBM i18n idea and formula from Roland Reck (QuHno)
 */
"use strict";

/**
 * @brief Convert special characters to their unicode number
 *
 * Used by the `replace` method to convert the characters to
 * their unicode values between underlines.
 */
function specialChars(match, linefeed, special) {
	// get rid of new lines
	if (linefeed === "n") {
		return "_10_";
	}
	if (linefeed === "r") {
		return "_13_";
	}
	// convert special chars to their code
	if (special !== undefined) {
		return "_" + match.charCodeAt(0) + "_";
	}
	return match;
}

/**
 * @brief Class representing a message
 *
 * This class is a construction for one message, it holds all
 * the data about the message, including original text, context
 * and all possible translations (if there are plurals)
 *
 * @warning: The setters append and don't replace
 */
class Message {
	/**
	 * @brief Construct a new message
	 *
	 * @param lf {string} The linefeed for each line,
	 * minimised JSON if left as an empty string
	 */
	constructor(lf, ibmi18n) {
		this.linefeed = lf;
		this.ibmi18n  = ibmi18n;
		this.tab      = (lf === "" ? "" : "\t");
		this.space    = (lf === "" ? "" : " ");
		this.str      = [];
		this.id       = "";
		this.ctxt     = "";
		this.lastUsed = null;
	}
	/**
	 * @brief Set or amend msgid
	 *
	 * @param text {string} The text to set
	 */
	set msgid(text) {
		this.id = text;
		this.lastUsed = this.id;
	}
	get msgid() {
		return this.id;
	}
	/**
	 * @brief Set the context
	 *
	 * @param context {string} The text to set
	 */
	set msgctxt(context) {
		this.ctxt = context;
		this.lastUsed = this.ctxt;
	}
	get msgctxt() {
		return this.ctxt;
	}
	/**
	 * @brief Set or amend msgid
	 *
	 * @param string {string} The text to set
	 */
	set msgstr(string) {
		const index = this.str.push(string) - 1;
		this.lastUsed = this.str[index];
	}
	get msgstr() {
		return this.str;
	}
	/**
	 * @brief Appends text to the last defined property
	 *
	 * @param string {string} The text to append
	 */
	set amend(string) {
		this.lastUsed += string;
	}
	/**
	 * @brief Increase string to safe length if enabled
	 *
	 * Applies the IBM algorithm that increases the length of the
	 * string to test screen space in multiple languages.
	 *
	 * @param string {string} the text to apply IBM algorithm
	 *
	 * @return {string} String with increase length or same string
	 */
	getIBM(string) {
		if (this.ibmi18n !== true) {
			return string;
		}

		const length = string.length;

		return `${string}${"-".repeat(
			(3 / Math.log(length) + 0.7) * length - length
		)}`;
	}
	/**
	 * @brief Return a constructed JSON as a string
	 *
	 * @return {string} JSON of the message
	 */
	toString() {
		const lf = this.linefeed;
		const tab = this.tab;
		const space = this.space;

		const msgctxt = (this.msgctxt.length > 0 ? "\x04" : "");

		const id = `${this.msgctxt}${msgctxt}${this.msgid}`.replace(
			/\\(n|r)|([^a-z0-9])/g, specialChars
		);

		const json = [];

		this.msgstr.forEach((string, index) => {
			if (string.length === 0) {
				string = this.msgid;
			}

			json.push(
				`${id}${index}:${space}{${lf}` +
					`${tab}"message":${space}"${this.getIBM(string)}"${lf}` +
				"}"
			);
		});

		return json.join(`,${lf}`);
	}
}

/**
 * @brief Gets a PO file and converts to JSON
 *
 * Gets a PO file as a `String` and returns a `String`
 * with the contents of a JSON file.
 *
 * @param[in] file    {String}  The PO file to convert
 * @param[in] minify  {Boolean} If the result must be minified (optional)
 * @param[in] ibmi18n {Boolean} If the result expands strings to match W3C
 * Internationalisation guideline. @see https://www.w3.org/International/articles/article-text-size
 *
 * @return {String} JSON file
 *
 * @info The W3C formula comes from a simplification of the following formula:
 * X = (((3 * ln(length) + 0.7) * length) - length)
 * Where:
 * p = (300 * ln(length) + 70) <-- This returns the percentage of expansion
 * p = (  3 * ln(length) + 0.7) <-- This returns the same but divided by 100
 * e = (p * length) <-- This returns the final expanded length
 * f = (e - length) <-- This returns the amount of chars to add
 */
function purePO2JSON(file, minify, ibmi18n) {
	// Check line feed
	const linefeed = file.match(/\r\n?|\n/)[0];
	file = file.split(linefeed);

	const lf = (minify ? "" : linefeed);

	const messages = [];
	let ignoreline = true;

	file.forEach(function choose(line) {
		// if the line has no text or begins with '#' (comment)
		if (line.length === 0 || line[0] === "#") {
			return;
		}

		const msg = line.match(
			/^msg(?:(id)(_plural)?|(str)(?:\[\d\])?|(ctxt))\s"(.*)"$/
		);
		const currentMessage = messages[messages.length - 1];

		// line is continuation of a string
		if (msg === null) {
			// only amend if there's anything to amend
			if (currentMessage !== undefined) {
				currentMessage.amend = line.substring(1, line.length - 1);
			}

			return;
		}

		const msgid        = Boolean(msg[1]);
		const msgid_plural = Boolean(msg[2]);
		const msgstr       = Boolean(msg[3]);
		const msgctxt      = Boolean(msg[4]);
		const text         = msg[5];

		// ignore the first msgid/msgstr pair
		if (ignoreline === true) {
			// stop ignoring after the first msgid
			if (msgstr === true) {
				ignoreline = false;
			}

			return;
		}

		if (msgctxt) {
			const length = messages.push(new Message(lf, ibmi18n));
			messages[length - 1].msgctxt = text;
			return;
		}

		if (msgid) {
			if (
				currentMessage === undefined     ||
				currentMessage.msgid.length > 0  ||
				currentMessage.msgctxt.length === 0
			) {
				const length = messages.push(new Message(lf, ibmi18n));
				messages[length - 1].msgid = text;
				return;
			}

			currentMessage.msgid = text;
			return;
		}

		if (msgid_plural) {
			return;
		}

		if (msgstr) {
			currentMessage.msgstr = text;
		}
	});

	console.info("All done, just copy the content of the page now. ;D");

	// Join all lines again with the original line feed
	return `{${lf}${messages.join(`,${lf}`)}${lf}}`;
}
<commit_msg>Fix missing quotes around id key<commit_after>/*
 * PurePO2JSON v3.0.0
 * by André Zanghelini (An_dz)
 *
 * with previous contributions by Roland Reck (QuHno)
 * IBM i18n idea and formula from Roland Reck (QuHno)
 */
"use strict";

/**
 * @brief Convert special characters to their unicode number
 *
 * Used by the `replace` method to convert the characters to
 * their unicode values between underlines.
 */
function specialChars(match, linefeed, special) {
	// get rid of new lines
	if (linefeed === "n") {
		return "_10_";
	}
	if (linefeed === "r") {
		return "_13_";
	}
	// convert special chars to their code
	if (special !== undefined) {
		return "_" + match.charCodeAt(0) + "_";
	}
	return match;
}

/**
 * @brief Class representing a message
 *
 * This class is a construction for one message, it holds all
 * the data about the message, including original text, context
 * and all possible translations (if there are plurals)
 *
 * @warning: The setters append and don't replace
 */
class Message {
	/**
	 * @brief Construct a new message
	 *
	 * @param lf {string} The linefeed for each line,
	 * minimised JSON if left as an empty string
	 */
	constructor(lf, ibmi18n) {
		this.linefeed = lf;
		this.ibmi18n  = ibmi18n;
		this.tab      = (lf === "" ? "" : "\t");
		this.space    = (lf === "" ? "" : " ");
		this.str      = [];
		this.id       = "";
		this.ctxt     = "";
		this.lastUsed = null;
	}
	/**
	 * @brief Set or amend msgid
	 *
	 * @param text {string} The text to set
	 */
	set msgid(text) {
		this.id = text;
		this.lastUsed = this.id;
	}
	get msgid() {
		return this.id;
	}
	/**
	 * @brief Set the context
	 *
	 * @param context {string} The text to set
	 */
	set msgctxt(context) {
		this.ctxt = context;
		this.lastUsed = this.ctxt;
	}
	get msgctxt() {
		return this.ctxt;
	}
	/**
	 * @brief Set or amend msgid
	 *
	 * @param string {string} The text to set
	 */
	set msgstr(string) {
		const index = this.str.push(string) - 1;
		this.lastUsed = this.str[index];
	}
	get msgstr() {
		return this.str;
	}
	/**
	 * @brief Appends text to the last defined property
	 *
	 * @param string {string} The text to append
	 */
	set amend(string) {
		this.lastUsed += string;
	}
	/**
	 * @brief Increase string to safe length if enabled
	 *
	 * Applies the IBM algorithm that increases the length of the
	 * string to test screen space in multiple languages.
	 *
	 * @param string {string} the text to apply IBM algorithm
	 *
	 * @return {string} String with increase length or same string
	 */
	getIBM(string) {
		if (this.ibmi18n !== true) {
			return string;
		}

		const length = string.length;

		return `${string}${"-".repeat(
			(3 / Math.log(length) + 0.7) * length - length
		)}`;
	}
	/**
	 * @brief Return a constructed JSON as a string
	 *
	 * @return {string} JSON of the message
	 */
	toString() {
		const lf = this.linefeed;
		const tab = this.tab;
		const space = this.space;

		const msgctxt = (this.msgctxt.length > 0 ? "\x04" : "");

		const id = `${this.msgctxt}${msgctxt}${this.msgid}`.replace(
			/\\(n|r)|([^a-z0-9])/g, specialChars
		);

		const json = [];

		this.msgstr.forEach((string, index) => {
			if (string.length === 0) {
				string = this.msgid;
			}

			json.push(
				`"${id}${index}":${space}{${lf}` +
					`${tab}"message":${space}"${this.getIBM(string)}"${lf}` +
				"}"
			);
		});

		return json.join(`,${lf}`);
	}
}

/**
 * @brief Gets a PO file and converts to JSON
 *
 * Gets a PO file as a `String` and returns a `String`
 * with the contents of a JSON file.
 *
 * @param[in] file    {String}  The PO file to convert
 * @param[in] minify  {Boolean} If the result must be minified (optional)
 * @param[in] ibmi18n {Boolean} If the result expands strings to match W3C
 * Internationalisation guideline. @see https://www.w3.org/International/articles/article-text-size
 *
 * @return {String} JSON file
 *
 * @info The W3C formula comes from a simplification of the following formula:
 * X = (((3 * ln(length) + 0.7) * length) - length)
 * Where:
 * p = (300 * ln(length) + 70) <-- This returns the percentage of expansion
 * p = (  3 * ln(length) + 0.7) <-- This returns the same but divided by 100
 * e = (p * length) <-- This returns the final expanded length
 * f = (e - length) <-- This returns the amount of chars to add
 */
function purePO2JSON(file, minify, ibmi18n) {
	// Check line feed
	const linefeed = file.match(/\r\n?|\n/)[0];
	file = file.split(linefeed);

	const lf = (minify ? "" : linefeed);

	const messages = [];
	let ignoreline = true;

	file.forEach(function choose(line) {
		// if the line has no text or begins with '#' (comment)
		if (line.length === 0 || line[0] === "#") {
			return;
		}

		const msg = line.match(
			/^msg(?:(id)(_plural)?|(str)(?:\[\d\])?|(ctxt))\s"(.*)"$/
		);
		const currentMessage = messages[messages.length - 1];

		// line is continuation of a string
		if (msg === null) {
			// only amend if there's anything to amend
			if (currentMessage !== undefined) {
				currentMessage.amend = line.substring(1, line.length - 1);
			}

			return;
		}

		const msgid        = Boolean(msg[1]);
		const msgid_plural = Boolean(msg[2]);
		const msgstr       = Boolean(msg[3]);
		const msgctxt      = Boolean(msg[4]);
		const text         = msg[5];

		// ignore the first msgid/msgstr pair
		if (ignoreline === true) {
			// stop ignoring after the first msgid
			if (msgstr === true) {
				ignoreline = false;
			}

			return;
		}

		if (msgctxt) {
			const length = messages.push(new Message(lf, ibmi18n));
			messages[length - 1].msgctxt = text;
			return;
		}

		if (msgid) {
			if (
				currentMessage === undefined     ||
				currentMessage.msgid.length > 0  ||
				currentMessage.msgctxt.length === 0
			) {
				const length = messages.push(new Message(lf, ibmi18n));
				messages[length - 1].msgid = text;
				return;
			}

			currentMessage.msgid = text;
			return;
		}

		if (msgid_plural) {
			return;
		}

		if (msgstr) {
			currentMessage.msgstr = text;
		}
	});

	console.info("All done, just copy the content of the page now. ;D");

	// Join all lines again with the original line feed
	return `{${lf}${messages.join(`,${lf}`)}${lf}}`;
}
<|endoftext|>


<commit_before>/*
File: qpcr_pools.js
URL: /static/js/qpcr_pools.js
Powers /qpcr_pools - template is run_dir/design/qpcr_pools.html
*/

$(document).ready(function() {
    // Load the data
    load_table();
});
// Initialize sorting and searching javascript plugin

function load_table() {
  var colspan = $('#pools_table > thead > tr:first > th').length + 1; // adding the first column
  $("#pools_table_body").html('
 Loading..
');
  return $.getJSON('/api/v1/qpcr_pools', function(data) {
    $("#pools_table_body").empty();
    $.each(data, function(flow, containers) {
      if(!($.isEmptyObject(containers))){
        $.each(containers, function(container, pools){
          var avg_wait_calc = 0;
          var tbl_row = $('
');
          tbl_row.append($('').html(flow));
          tbl_row.append($('').html(function() {
              var to_return = '';
              to_return = to_return + container + ' '+pools['samples'].length+'';
              to_return = to_return + '
 \

';
              to_return = to_return + '

Sample	Well	Waiting (in days)

';
              $.each(pools['samples'], function(pool, sample){
                var wait = getDaysAndDateLabel(sample['queue_time'], 'date')[0];
                to_return = to_return +
                '
'+
                  ''+sample['name']+'	'+
                  ''+sample['well']+'	'+
                  ''+wait+'	'+
                '
';
                //use avg_wait_calc as a counter
                avg_wait_calc = avg_wait_calc + wait;
                });
                to_return = to_return +'
';
              return to_return;
          }));
          tbl_row.append($('').html(function(){
            var to_return = '';
            $.each( pools['projects'], function(pid, pname){
              console.log(pid, pname)
              to_return = to_return + '
'+pname+' ('+pid+')
'
            });
            return to_return;
          }));
          tbl_row.append($('').html(function(){
            var to_return = '';
            $.each( pools['library_types'], function(i, library_type){
              to_return = to_return + '
'+ library_type +'
'
            });
            return to_return;
          }));
          tbl_row.append($('').html(function(){
            var to_return = '';
            $.each( pools['runmodes'], function(i, runmode){
              to_return = to_return + '
'+ runmode +'
'
            });
            return to_return;
          }));
          //get average wait time for all samples in a pool.
          avg_wait_calc = avg_wait_calc/pools['samples'].length;
          var daysAndLabel = getDaysAndDateLabel(avg_wait_calc, 'label');
          tbl_row.append($('').html(''+(avg_wait_calc).toFixed(1)+''));
          $("#pools_table_body").append(tbl_row);
        })
      }
    })
    init_listjs();
  })
}

function init_listjs() {
    // Setup - add a text input to each footer cell
    $('#pools_table tfoot th').each( function () {
      var title = $('#pools_table thead th').eq( $(this).index() ).text();
      $(this).html( '<input size=10 type="text" placeholder="Search '+title+'" />' );
    } );

    var table = $('#pools_table').DataTable({
      "columnDefs": [
          { "visible": false, "targets": 0 }
      ],
      "paging":false,
      "info":false,
      "order": [],
      "drawCallback": function ( settings ) {
        var api = this.api();
        var rows = api.rows( {page:'current'} ).nodes();
        var last=null;
        api.column(0, {page:'current'} ).data().each( function ( group, i ) {
          if ( last !== group ) {
            $(rows).eq( i ).before(
                '
 thead > tr:first > th').length + 1+'">'+group+'
'
            );
            last = group;
          }
        });
      }
    });

    //Add the bootstrap classes to the search thingy
    $('div.dataTables_filter input').addClass('form-control search search-query');
    $('#pools_table_filter').addClass('form-inline float-right');
    $("#pools_table_filter").appendTo("h1");
    $('#pools_table_filter label input').appendTo($('#pools_table_filter'));
    $('#pools_table_filter label').remove();
    $("#pools_table_filter input").attr("placeholder", "Search..");
    // Apply the search
    table.columns().every( function () {
        var that = this;
        $( 'input', this.footer() ).on( 'keyup change', function () {
            that
            .search( this.value )
            .draw();
        } );
    } );
    $('.expand-proj').on('click', function () {
      if($(this).parent().find('table').css('visibility')=='collapse'){
        $(this).find('.fa').toggleClass('fa-plus-square fa-minus-square');
        $(this).parent().find('table').css('visibility', 'visible');
      }
      else {
        $(this).find('.fa').toggleClass('fa-minus-square fa-plus-square');
        $(this).parent().find('table').css('visibility', 'collapse');
      }
    });
    $('.expand-all').on('click', function () {
      var reqText = {'Expand All': ['Collapse All', 'visible', 'fa-plus-square', 'fa-minus-square'],
                      'Collapse All': ['Expand All', 'collapse', 'fa-minus-square', 'fa-plus-square']};
      $('.expand-all').find('.fa').removeClass(reqText[$('.expand-all').text()][2]);
      $('#pools_table').find('tr').find('.fa').removeClass(reqText[$('.expand-all').text()][2]);
      $('.expand-all').find('.fa').addClass(reqText[$('.expand-all').text()][3]);
      $('#pools_table').find('tr').find('.fa').addClass(reqText[$('.expand-all').text()][3]);
      $('#pools_table').find('tr').find('table').css('visibility', reqText[$('.expand-all').text()][1]);
      $('.expand-all').contents().filter(function(){ return this.nodeType == 3; }).first().replaceWith(reqText[$('.expand-all').text()][0]);
    });
}

$('body').on('click', '.group', function(event) {
  $($("#samples_table").DataTable().column(0).header()).trigger("click")
});

function getDaysAndDateLabel(date, option){
  var number_of_days = 0;
  var label = '';
  if( option=='date' || option=='both' ){
    //calculate number of days from given date to current date
    number_of_days = Math.floor(Math.abs(new Date() - new Date(date))/(1000*86400));
  }
  if (option=='label' || option=='both') {
    if (option=='label'){
      number_of_days = date;
    }
    if (number_of_days < 7){
      label =  'success';
    }
    else if (number_of_days >= 7 && number_of_days < 14) {
      label = 'warning';
    }
    else {
      label = 'danger';
    }
  }
   return [number_of_days, label];
}
<commit_msg>Remove debug line<commit_after>/*
File: qpcr_pools.js
URL: /static/js/qpcr_pools.js
Powers /qpcr_pools - template is run_dir/design/qpcr_pools.html
*/

$(document).ready(function() {
    // Load the data
    load_table();
});
// Initialize sorting and searching javascript plugin

function load_table() {
  var colspan = $('#pools_table > thead > tr:first > th').length + 1; // adding the first column
  $("#pools_table_body").html('
 Loading..
');
  return $.getJSON('/api/v1/qpcr_pools', function(data) {
    $("#pools_table_body").empty();
    $.each(data, function(flow, containers) {
      if(!($.isEmptyObject(containers))){
        $.each(containers, function(container, pools){
          var avg_wait_calc = 0;
          var tbl_row = $('
');
          tbl_row.append($('').html(flow));
          tbl_row.append($('').html(function() {
              var to_return = '';
              to_return = to_return + container + ' '+pools['samples'].length+'';
              to_return = to_return + '
 \

';
              to_return = to_return + '

Sample	Well	Waiting (in days)

';
              $.each(pools['samples'], function(pool, sample){
                var wait = getDaysAndDateLabel(sample['queue_time'], 'date')[0];
                to_return = to_return +
                '
'+
                  ''+sample['name']+'	'+
                  ''+sample['well']+'	'+
                  ''+wait+'	'+
                '
';
                //use avg_wait_calc as a counter
                avg_wait_calc = avg_wait_calc + wait;
                });
                to_return = to_return +'
';
              return to_return;
          }));
          tbl_row.append($('').html(function(){
            var to_return = '';
            $.each( pools['projects'], function(pid, pname){
              to_return = to_return + '
'+pname+' ('+pid+')
'
            });
            return to_return;
          }));
          tbl_row.append($('').html(function(){
            var to_return = '';
            $.each( pools['library_types'], function(i, library_type){
              to_return = to_return + '
'+ library_type +'
'
            });
            return to_return;
          }));
          tbl_row.append($('').html(function(){
            var to_return = '';
            $.each( pools['runmodes'], function(i, runmode){
              to_return = to_return + '
'+ runmode +'
'
            });
            return to_return;
          }));
          //get average wait time for all samples in a pool.
          avg_wait_calc = avg_wait_calc/pools['samples'].length;
          var daysAndLabel = getDaysAndDateLabel(avg_wait_calc, 'label');
          tbl_row.append($('').html(''+(avg_wait_calc).toFixed(1)+''));
          $("#pools_table_body").append(tbl_row);
        })
      }
    })
    init_listjs();
  })
}

function init_listjs() {
    // Setup - add a text input to each footer cell
    $('#pools_table tfoot th').each( function () {
      var title = $('#pools_table thead th').eq( $(this).index() ).text();
      $(this).html( '<input size=10 type="text" placeholder="Search '+title+'" />' );
    } );

    var table = $('#pools_table').DataTable({
      "columnDefs": [
          { "visible": false, "targets": 0 }
      ],
      "paging":false,
      "info":false,
      "order": [],
      "drawCallback": function ( settings ) {
        var api = this.api();
        var rows = api.rows( {page:'current'} ).nodes();
        var last=null;
        api.column(0, {page:'current'} ).data().each( function ( group, i ) {
          if ( last !== group ) {
            $(rows).eq( i ).before(
                '
 thead > tr:first > th').length + 1+'">'+group+'
'
            );
            last = group;
          }
        });
      }
    });

    //Add the bootstrap classes to the search thingy
    $('div.dataTables_filter input').addClass('form-control search search-query');
    $('#pools_table_filter').addClass('form-inline float-right');
    $("#pools_table_filter").appendTo("h1");
    $('#pools_table_filter label input').appendTo($('#pools_table_filter'));
    $('#pools_table_filter label').remove();
    $("#pools_table_filter input").attr("placeholder", "Search..");
    // Apply the search
    table.columns().every( function () {
        var that = this;
        $( 'input', this.footer() ).on( 'keyup change', function () {
            that
            .search( this.value )
            .draw();
        } );
    } );
    $('.expand-proj').on('click', function () {
      if($(this).parent().find('table').css('visibility')=='collapse'){
        $(this).find('.fa').toggleClass('fa-plus-square fa-minus-square');
        $(this).parent().find('table').css('visibility', 'visible');
      }
      else {
        $(this).find('.fa').toggleClass('fa-minus-square fa-plus-square');
        $(this).parent().find('table').css('visibility', 'collapse');
      }
    });
    $('.expand-all').on('click', function () {
      var reqText = {'Expand All': ['Collapse All', 'visible', 'fa-plus-square', 'fa-minus-square'],
                      'Collapse All': ['Expand All', 'collapse', 'fa-minus-square', 'fa-plus-square']};
      $('.expand-all').find('.fa').removeClass(reqText[$('.expand-all').text()][2]);
      $('#pools_table').find('tr').find('.fa').removeClass(reqText[$('.expand-all').text()][2]);
      $('.expand-all').find('.fa').addClass(reqText[$('.expand-all').text()][3]);
      $('#pools_table').find('tr').find('.fa').addClass(reqText[$('.expand-all').text()][3]);
      $('#pools_table').find('tr').find('table').css('visibility', reqText[$('.expand-all').text()][1]);
      $('.expand-all').contents().filter(function(){ return this.nodeType == 3; }).first().replaceWith(reqText[$('.expand-all').text()][0]);
    });
}

$('body').on('click', '.group', function(event) {
  $($("#samples_table").DataTable().column(0).header()).trigger("click")
});

function getDaysAndDateLabel(date, option){
  var number_of_days = 0;
  var label = '';
  if( option=='date' || option=='both' ){
    //calculate number of days from given date to current date
    number_of_days = Math.floor(Math.abs(new Date() - new Date(date))/(1000*86400));
  }
  if (option=='label' || option=='both') {
    if (option=='label'){
      number_of_days = date;
    }
    if (number_of_days < 7){
      label =  'success';
    }
    else if (number_of_days >= 7 && number_of_days < 14) {
      label = 'warning';
    }
    else {
      label = 'danger';
    }
  }
   return [number_of_days, label];
}
<|endoftext|>


<commit_before>//-------------------------------------
//-- Dependencies starter kit
//-------------------------------------

(() => {
	'use strict';

	let addProp = (obj, prop, value) => {
		Object.defineProperty(obj, prop, {
			enumerable:   false,
			writable:     false,
			configurable: false,
			value:        value
		});
	};

	let readonlyObj = (data) => {
		let obj = {};
		for (let prop of Object.keys(data)) {

			if (typeof data[prop] === 'object') {
				data[prop] = readonlyObj(data[prop]);
			}
			addProp(obj, prop, data[prop]);
		}
		return obj;
	};

	// Initialize nwayo
	let nwayo = readonlyObj({
		project: konstan.project,
		version: konstan.nwayo
	});

	let vendor = {};
	addProp(vendor, 'jQuery',       global.jQuery.noConflict(true));
	addProp(vendor, 'jQueryGlobal', global.jQuery);
	addProp(vendor, 'lodash',       global._);
	addProp(vendor, 'Modernizr',    global.Modernizr);
	addProp(vendor, 'PubSub',       global.PubSub);

	addProp(nwayo, 'vendor', vendor);

	addProp(global, 'nwayo', nwayo);

	// Initialize application
	let $    = vendor.jQuery;
	let path = konstan.konstan.path;
	delete konstan.konstan.path;

	let culture = $('html').attr('lang');
	let $body = $('body');
	let bodyClass = $body.attr('class');
	addProp(global, konstan.project, readonlyObj({
		bundle:  konstan.bundle,
		konstan: konstan.konstan,
		path:    path,
		tmpl:    {},
		env:     {
			culture:  culture,
			lang:     culture.substr(0,2),
			pageId:   $body.attr('id'),
			pageTags: !!bodyClass ? _.compact( $('body').attr('class').split(' ') ) : []
		}
	}));

})();
<commit_msg>Update dependencies-starter.js<commit_after>//-------------------------------------
//-- Dependencies starter kit
//-------------------------------------

(() => {
	'use strict';

	let addProp = (obj, prop, value) => {
		Object.defineProperty(obj, prop, {
			enumerable:   false,
			writable:     false,
			configurable: false,
			value:        value
		});
	};

	let readonlyObj = (data) => {
		let obj = {};
		for (let prop of Object.keys(data)) {

			if (typeof data[prop] === 'object') {
				data[prop] = readonlyObj(data[prop]);
			}
			addProp(obj, prop, data[prop]);
		}
		return obj;
	};

	// Initialize nwayo
	let nwayo = readonlyObj({
		project: konstan.project,
		version: konstan.nwayo
	});

	let vendor = {};
	addProp(vendor, 'jQuery',       global.jQuery.noConflict(true));
	addProp(vendor, 'jQueryGlobal', global.jQuery);
	addProp(vendor, 'lodash',       global._);
	addProp(vendor, 'Modernizr',    global.Modernizr);
	addProp(vendor, 'PubSub',       global.PubSub);

	addProp(nwayo, 'vendor', vendor);

	addProp(global, 'nwayo', nwayo);

	// Initialize application
	let $    = vendor.jQuery;
	let path = konstan.konstan.path;
	delete konstan.konstan.path;

	let culture = $('html').attr('lang');
	let $body = $('body');
	let bodyClass = $body.attr('class');
	addProp(global, konstan.project, readonlyObj({
		bundle:  konstan.bundle,
		konstan: konstan.konstan,
		path:    path,
		tmpl:    {},
		env:     {
			culture:  culture,
			lang:     culture.substr(0,2),
			pageId:   $body.attr('id'),
			pageTags: !!bodyClass ? _.compact( bodyClass.split(' ') ) : []
		}
	}));

})();
<|endoftext|>


<commit_before>var GetTilesWithin = require('./GetTilesWithin');

/**
 * Randomizes the indexes of a rectangular region of tiles (in tile coordinates) within the
 * specified layer. Each tile will recieve a new index. New indexes are drawn from the given
 * weightedIndexes array. An example weighted array:
 *
 * [
 *  { index: 6, weight: 4 },    // Probability of index 6 is 4 / 8
 *  { index: 7, weight: 2 },    // Probability of index 7 would be 2 / 8
 *  { index: 8, weight: 1.5 },  // Probability of index 8 would be 1.5 / 8
 *  { index: 26, weight: 0.5 }  // Probability of index 27 would be 0.5 / 8
 * ]
 *
 * The probability of any index being choose is (the index's weight) / (sum of all weights). This
 * method only modifies tile indexes and does not change collisioninformation.
 *
 * @param {integer} [tileX=0] - [description]
 * @param {integer} [tileY=0] - [description]
 * @param {integer} [width=max width based on tileX] - [description]
 * @param {integer} [height=max height based on tileY] - [description]
 * @param {object[]} [weightedIndexes] - An array of objects to randomly draw from during
 * randomization. They should be in the form: { index: 0, weight: 4 }.
 * @param {LayerData} layer - [description]
 */
var WeightedRandomize = function (tileX, tileY, width, height, weightedIndexes, layer)
{
    if (weightedIndexes === undefined) { return; }

    var i;
    var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);

    var weightTotal = 0;
    for (i = 0; i < weightedIndexes.length; i++)
    {
        weightTotal += weightedIndexes[i].weight;
    }

    if (weightTotal <= 0) { return; }

    for (i = 0; i < tiles.length; i++)
    {
        var rand = Math.random() * weightTotal;
        var sum = 0;
        var randomIndex = -1;
        for (var j = 0; j < weightedIndexes.length; j++)
        {
            sum += weightedIndexes[j].weight;
            if (rand <= sum)
            {
                randomIndex = weightedIndexes[j].index;
                break;
            }
        }

        tiles[i].index = randomIndex;
    }
};

module.exports = WeightedRandomize;
<commit_msg>Update WeightedRandomize() to support index arrays<commit_after>var GetTilesWithin = require('./GetTilesWithin');

/**
 * Randomizes the indexes of a rectangular region of tiles (in tile coordinates) within the
 * specified layer. Each tile will recieve a new index. New indexes are drawn from the given
 * weightedIndexes array. An example weighted array:
 *
 * [
 *  { index: 6, weight: 4 },    // Probability of index 6 is 4 / 8
 *  { index: 7, weight: 2 },    // Probability of index 7 would be 2 / 8
 *  { index: 8, weight: 1.5 },  // Probability of index 8 would be 1.5 / 8
 *  { index: 26, weight: 0.5 }  // Probability of index 27 would be 0.5 / 8
 * ]
 *
 * The probability of any index being choose is (the index's weight) / (sum of all weights). This
 * method only modifies tile indexes and does not change collisioninformation.
 *
 * @param {integer} [tileX=0] - [description]
 * @param {integer} [tileY=0] - [description]
 * @param {integer} [width=max width based on tileX] - [description]
 * @param {integer} [height=max height based on tileY] - [description]
 * @param {object[]} [weightedIndexes] - An array of objects to randomly draw from during
 * randomization. They should be in the form: { index: 0, weight: 4 }.
 * @param {LayerData} layer - [description]
 */
var WeightedRandomize = function (tileX, tileY, width, height, weightedIndexes, layer)
{
    if (weightedIndexes === undefined) { return; }

    var i;
    var tiles = GetTilesWithin(tileX, tileY, width, height, null, layer);

    var weightTotal = 0;
    for (i = 0; i < weightedIndexes.length; i++)
    {
        weightTotal += weightedIndexes[i].weight;
    }

    if (weightTotal <= 0) { return; }

    for (i = 0; i < tiles.length; i++)
    {
        var rand = Math.random() * weightTotal;
        var sum = 0;
        var randomIndex = -1;
        for (var j = 0; j < weightedIndexes.length; j++)
        {
            sum += weightedIndexes[j].weight;
            if (rand <= sum)
            {
                var chosen = weightedIndexes[j].index
                randomIndex = Array.isArray(chosen)
                    ? chosen[Math.floor(Math.random() * chosen.length)]
                    : chosen
            }
        }

        tiles[i].index = randomIndex;
    }
};

module.exports = WeightedRandomize;
<|endoftext|>


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

foam.CLASS({
  package: 'foam.core.internal',
  name: 'InterfaceMethod',
  extends: 'foam.core.Method',
  properties: [
    {
      name: 'code',
      required: false
    },
    {
      class: 'Boolean',
      name: 'abstract',
      value: true
    }
  ],
  methods: [
    function installInProto() {
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Interface',
  properties: [
    {
      class: 'String',
      name: 'package'
    },
    {
      class: 'String',
      name: 'name'
    },
    {
      class: 'StringArray',
      name: 'extends'
    },
    {
      class: 'String',
      name: 'id',
      expression: function(package, name) {
        return package + '.' + name;
      }
    },
    {
      class: 'AxiomArray',
      name: 'methods',
      of: 'foam.core.internal.InterfaceMethod'
    },
    {
      class: 'AxiomArray',
      name: 'properties',
      // TODO: Should this be a type of property that defines
      // nothing beyond name and type information?
      of: 'foam.core.Property'
    },
    {
      name: 'axioms_',
      factory: function() { return []; }
    },
    {
      class: 'String',
      name: 'documentation'
    }
  ],
  methods: [
    function getAxiomByName(name) {
      return this.axioms_.filter(function(a) {
        return a.name === name;
      })[0];
    },
    function getAxiomsByClass(cls) {
      return this.axioms_.filter(function(a) {
        return cls.isInstance(a);
      });
    },
    function getOwnAxiomsByClass(cls) {
      return this.getAxiomsByClass(cls);
    },
    function hasOwnAxiom(name) {
      return this.axioms_.some(function(a) { return a.name === name; });
    },
    function isInstance(o) {
      return !! ( o && o.cls_ && o.cls_.getAxiomByName('implements_' + this.id) );
    }
  ]
});

foam.LIB({
  name: 'foam',
  methods: [
    function INTERFACE(m) {
      var model = foam.core.Interface.create(m);
      foam.register(model);
      foam.package.registerClass(model);
    }
  ]
});
<commit_msg>Add topics to Interface. Fix id of interface objects being incorrect if package is empty.<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.core.internal',
  name: 'InterfaceMethod',
  extends: 'foam.core.Method',
  properties: [
    {
      name: 'code',
      required: false
    },
    {
      class: 'Boolean',
      name: 'abstract',
      value: true
    }
  ],
  methods: [
    function installInProto() {
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Interface',
  properties: [
    {
      class: 'String',
      name: 'package'
    },
    {
      class: 'String',
      name: 'name'
    },
    {
      class: 'StringArray',
      name: 'extends'
    },
    {
      class: 'String',
      name: 'id',
      expression: function(package, name) {
        return package ? (package + '.' + name) : name;
      }
    },
    {
      class: 'AxiomArray',
      name: 'methods',
      of: 'foam.core.internal.InterfaceMethod'
    },
    {
      class: 'AxiomArray',
      name: 'properties',
      // TODO: Should this be a type of property that defines
      // nothing beyond name and type information?
      of: 'foam.core.Property'
    },
    {
      class: 'AxiomArray',
      name: 'topics',
      of: 'foam.core.Topic',
      adaptArrayElement: function(o) {
        return typeof o === 'string'        ?
          foam.core.Topic.create({name: o}) :
          foam.core.Topic.create(o)         ;
      }
    },
    {
      name: 'axioms_',
      factory: function() { return []; }
    },
    {
      class: 'String',
      name: 'documentation'
    }
  ],
  methods: [
    function getAxiomByName(name) {
      return this.axioms_.filter(function(a) {
        return a.name === name;
      })[0];
    },
    function getAxiomsByClass(cls) {
      return this.axioms_.filter(function(a) {
        return cls.isInstance(a);
      });
    },
    function getOwnAxiomsByClass(cls) {
      return this.getAxiomsByClass(cls);
    },
    function hasOwnAxiom(name) {
      return this.axioms_.some(function(a) { return a.name === name; });
    },
    function isInstance(o) {
      return !! ( o && o.cls_ && o.cls_.getAxiomByName('implements_' + this.id) );
    }
  ]
});

foam.LIB({
  name: 'foam',
  methods: [
    function INTERFACE(m) {
      var model = foam.core.Interface.create(m);
      foam.register(model);
      foam.package.registerClass(model);
    }
  ]
});
<|endoftext|>