<commit_before>/* eslint-disable import/namespace */
import { readFileSync } from 'fs';

import * as messages from 'messages';

describe('Messages', () => {
  Object.keys(messages).forEach((message) => {
    const { code, description, msg } = messages[message];

    if (typeof messages[message] === 'object') {
      it('should have a code set', () => {
        expect(code).not.toEqual(null);
      });

      it(`should have code length <= 25 for ${code}`, () => {
        // Otherwise the ansi color sequences will be borked
        // as columnify doesn't handle them when wrapping text.
        expect(code.length).toBeLessThan(26);
      });
    }

    if (description) {
      it(`should not have any newlines in description for ${code}`, () => {
        expect(description.split('\n').length).toEqual(1);
      });
    }

    if (msg) {
      it(`should not have any newlines in message for ${code}`, () => {
        expect(msg.split('\n').length).toEqual(1);
      });
    }
  });

  it('should have updated rules.md with new message codes', () => {
    const markdown = readFileSync('docs/rules.md', 'utf8');
    Object.keys(messages).forEach((message) => {
      const { code } = messages[message];
      if (code) {
        // Asserting using indexOf rather than assert.include
        // to avoid inclusion of the whole rules.md as part
        // of the error.
        expect(markdown.indexOf(code) > -1).toBeTruthy();
      }
    });
  });
});
<commit_msg>Fix messages tests<commit_after>/* eslint-disable import/namespace */
import { readFileSync } from 'fs';

import * as messages from 'messages';

describe('Messages', () => {
  Object.keys(messages).forEach((message) => {
    const { code, description, msg } = messages[message];

    if (typeof messages[message] === 'object' && code !== undefined) {
      it('should have a code set', () => {
        expect(code).not.toEqual(null);
      });

      it(`should have code length <= 25 for ${code}`, () => {
        // Otherwise the ansi color sequences will be borked
        // as columnify doesn't handle them when wrapping text.
        expect(code.length).toBeLessThan(26);
      });
    }

    if (description) {
      it(`should not have any newlines in description for ${code}`, () => {
        expect(description.split('\n').length).toEqual(1);
      });
    }

    if (msg) {
      it(`should not have any newlines in message for ${code}`, () => {
        expect(msg.split('\n').length).toEqual(1);
      });
    }
  });

  it('should have updated rules.md with new message codes', () => {
    const markdown = readFileSync('docs/rules.md', 'utf8');
    Object.keys(messages).forEach((message) => {
      const { code } = messages[message];
      if (code) {
        // Asserting using indexOf rather than assert.include
        // to avoid inclusion of the whole rules.md as part
        // of the error.
        expect(markdown.indexOf(code) > -1).toBeTruthy();
      }
    });
  });
});
<|endoftext|>


<commit_before>process.env.ROOT_FOLDER = './tests';

/**
 * Module dependencies.
 */
const apiUnderTest = require('../src');
const supertest = require('supertest');

/**
 * Setup supertest with our API
 */
const request = supertest.agent(apiUnderTest.listen());

/**
 * Tests for the Wildcard (/*) route
 */
describe('Reviewly', () => {
  it('should return content of index.html (Hello World!)', (done) => {
    request
      .get('/')
      .set('host', 'de.stub.mycs.dev')
      .expect(200)
      .expect('Hello World!', done);
  });
  it('should return index.html content', (done) => {
    request
      .get('/file-does-not-exist.html')
      .set('host', 'de.stub.hello.dev')
      .expect(200)
      .expect('Hello World!', done);
  });
  it('should return list of existing features if feature not found', (done) => {
    request
      .get('/')
      .set('host', 'de.EXIST-ME-NOT.hello.dev')
      .expect(200)
      .expect('This feature-branch was not found, here\'s what is already deployed on this server
stub
', done);
  });
});<commit_msg>Update wildcard.spec.js<commit_after>process.env.ROOT_FOLDER = './tests';

/**
 * Module dependencies.
 */
const apiUnderTest = require('../src');
const supertest = require('supertest');

/**
 * Setup supertest with our API
 */
const request = supertest.agent(apiUnderTest.listen());

/**
 * Tests for the Wildcard (/*) route
 */
describe('Reviewly', () => {
  it('should return content of index.html (Hello World!)', (done) => {
    request
      .get('/')
      .set('host', 'de.stub.mycs.dev')
      .expect(200)
      .expect('Hello World!', done);
  });
  it('should return index.html content', (done) => {
    request
      .get('/file-does-not-exist.html')
      .set('host', 'de.stub.hello.dev')
      .expect(200)
      .expect('Not found', done);
  });
  it('should return list of existing features if feature not found', (done) => {
    request
      .get('/')
      .set('host', 'de.EXIST-ME-NOT.hello.dev')
      .expect(200)
      .expect('This feature-branch was not found, here\'s what is already deployed on this server
stub
', done);
  });
});
<|endoftext|>


<commit_before>
var fs = require('fs');
var compressor = require('node-minify');
var extend = require('node.extend');
var fileset = require('fileset');
var util = require('util');
var path = require('path');
var async = require('async');

var config = {
 html: [],
 js: [],
 css: [],
 outputFile: "",
 initJs: "",
 pathBase: "",
 inputPaths: [],
 excludePaths: [],
 extension: {
  html: true,
  js: true,
  css: true
 },
 outputPath: "",
 cssSuffix:"-min",
 mode: "",
 deleteInputs: false
}

function ItemStats() {
 this.numFiles = 0;
 this.inputSize = 0;
 this.outputSize = 0;
 this.compression = function () {
  return parseInt((this.inputSize - this.outputSize) / this.inputSize * 100) + "%";
 };
}

var globalStats = {
 js:  new ItemStats(),
 html:new ItemStats(),
 css: new ItemStats(),
 total: new ItemStats()
};

function init () {
 var file_config = null;
 if (fs.existsSync("iris_packager.json") && fs.statSync("iris_packager.json").isFile()) {
  file_config = fs.readFileSync("iris_packager.json");
  config = extend(true, config, JSON.parse(file_config));
 }

 process.argv.forEach(
  function (val, index, array) {
   var param;

   param = getParam("input=", val);
   if ( param !== null ) {
    config.inputPaths = param.split(",");
   }

   param = getParam("exclude=", val);
   if ( param !== null ) {
    config.excludePaths = param.split(",");
   }

   param = getParam("ext=", val);
   if ( param !== null ) {
    config.extension = (function toObject(arr) {
     var rv = {};
     for (var i = 0; i < arr.length; ++i)
      rv[arr[i]] = true;
     return rv;
    })(param.split(","));
   }

   param = getParam("output=", val);
   if ( param !== null ) {
    config.outputPath = param;
    config.outputFile = "";
   }

   param = getParam("init=", val);
   if ( param !== null ) {
    config.initJs = param;
   }

   param = getParam("base=", val);
   if ( param !== null ) {
    config.pathBase = param;
   }

   param = getParam("cssSuffix=", val);
   if ( param !== null ) {
    config.cssSuffix = param;
   }

   param = getParam("mode=", val);
   if ( param !== null ) {
    config.mode = param;
   }

   param = getParam("delete=", val);
   if ( param !== null ) {
    config.deleteInputs = param;
   }
  }
  );

 if ( !config.inputPaths ) {
 //throw "you must specify the parameter input=path_to_directory/,path_to_file...";
 }

 if ( !config.outputPath && !config.outputFile ) {
  throw "you must specify the parameter output=path/";
 }

 if ( !config.initJs ) {
  throw "you must specify the parameter init=path/file.js";
 }

 if (!config.outputFile) {
  if (fs.existsSync(config.outputPath) && fs.statSync(config.outputPath).isDirectory()) {
   config.outputFile = validatePath(config.outputPath) + config.initJs.match(/[^/]+\.js$/);
  } else {
   config.outputFile = config.outputPath;
  }
 }

 config.js.push(config.initJs);
 console.log("***************************************************************************************");
 console.log("config = " + util.inspect(config));
 console.log("***************************************************************************************");

 var includes = "";
 var excludes = "";
 if (config.inputPaths.length > 0) {
  for (var i = 0; i < config.inputPaths.length; i++) {
   config.inputPaths[i] = config.pathBase + config.inputPaths[i];
  }
  includes = config.inputPaths.join(" ");
  if (config.excludePaths.length > 0) {
   for (i = 0; i < config.excludePaths.length; i++) {
    config.excludePaths[i] = config.pathBase + config.excludePaths[i];
   }
   excludes = config.excludePaths.join(" ");
  }

  fileset(includes,  excludes, function(err, files) {
   if (err) return console.error(err);
   for (var i = 0; i < files.length; i++) {
    var initPath = files[i];
    console.log("scanning '" + initPath + "'...");

    var stats = fs.statSync(initPath);
    if (stats.isFile()) {
     scanFile(initPath);
    } else {
     scanPath(validatePath(initPath));
    }
   }

  }).on('end', function() {

   generateOutput();
  });
 }
}

function validatePath (path) {
 if ( path.substr(-1) !== "/" ) {
  path = path + "/";
 }
 return path;
}

function getParam (label, value) {
 var idx = value.indexOf(label);
 if ( idx > -1 ) {
  return value.substring( idx + label.length );
 }
 return null;
}

function scanPath (path) {
 var filenames = fs.readdirSync(path);
 var fullPath;
 filenames.forEach(function(file) {
  fullPath = path + file;

  if ( fs.statSync(fullPath).isDirectory() ) {
   console.log("found dir '" + fullPath + "'...");
   scanPath(fullPath + "/");
  } else if ( fs.statSync(fullPath).isFile() ) {
   scanFile(fullPath);
  }
 });
}

function scanFile(file) {

 var ext = path.extname(file).replace(".","");

 if (ext === "html" || ext === "js" || ext === "css") {
  console.log("found " + ext + " '" + file + "'...");
  config[ext].push(file);
 }
}

function generateOutput () {
 console.log("Gerenating output. Wait please...");

 calculateInputStats("js");
 calculateInputStats("html");
 calculateInputStats("css");

 async.series([
  function(callback) {
   minifyJs(callback);
  },
  function(callback) {
   minifyCSS(callback);
  },
  function(callback) {
   concatTemplates(callback);
  },
  function(callback) {
   deleteFiles(callback);
  }
  ,function(callback) {
   printResults(callback);
  }
  ]);
}

function minifyJs(callback) {
 if (!config.extension.js || config.js.length == 0 || config.mode === "test") {
  callback(null, "minifyJs");
  return;
 }
 console.log("Minimizing JS...");

 // Compress using Google Closure
 new compressor.minify({
  type: 'gcc',
  fileIn: config.js,
  fileOut: config.outputFile,
  callback: function(err){
   if ( err ) {
    callback(err, "minifyJs");
   } else {
    globalStats.js.outputSize += fs.statSync(this.fileOut).size;
    callback(null, "minifyJs");
   }
  }
 });
}

function minifyCSS(callback) {
 if (!config.extension.css || config.css.length == 0 || config.mode === "test") {
  callback(null, "minifyCSS");
  return;
 }
 console.log("Minimizing CSS...");
 var filesProcessed = 0;
 for ( var f=0, F=config.css.length;f<F; f++ ) {
  var inCSS = config.css[f];
  (function (fileIn) {
   var outCSS = path.dirname(fileIn) + "/" + path.basename(fileIn, ".css") + config.cssSuffix + ".css";
   new compressor.minify({
    type: 'yui-css',
    fileIn: fileIn,
    fileOut: outCSS,
    callback: function(err){
     if ( err ) {
      callback(err, "minifyCSS");
     } else {
      filesProcessed++;
      globalStats.css.outputSize += fs.statSync(outCSS).size;
      if (filesProcessed == config.css.length) {
       callback(null, "minifyCSS");
      }
     }
    }
   });
  })(inCSS);
 }
}

function concatTemplates(callback){
 if (!config.extension.html || config.html.length == 0 || config.mode === "test") {
  callback(null, "concatTemplates");
  return;
 }
 console.log("Concatenating HTML...");
 var content = [];
 for ( var f=0, F=config.html.length;f<F; f++ ) {
  var data = fs.readFileSync(config.html[f], "utf8").replace(/[\r\n\t]/g,"");
  content.push(
   "iris.tmpl('"
   + config.html[f].replace(config.pathBase, "")
   +"','"
   + data.replace(/\'/g, "\\\'")
   + "');\n"
   );
 }

 fs.appendFile(
  config.outputFile,
  content.join(""),
  function(err) {
   if(err) {
    callback(err, "concatTemplates");
   } else {
    globalStats.html.outputSize = fs.statSync(config.outputFile).size - globalStats.js.outputSize;
    callback(null, "concatTemplates");
   }
  }
  );
}

function deleteFiles(callback) {
 if (config.mode === "test" || !config.deleteInputs) {
  callback(null, "deleteFiles");
  return;
 }
 console.log("Removing files...");
 for (var ext in config.extension) {
  if (ext) {
   for (var i = 0 ; i < config[ext].length; i++) {
    var file = config[ext][i];
    console.log("Removing " + file + "...");
    fs.unlinkSync(file);
   }
  }
 }
 callback(null, "deleteFiles");

}

function calculateInputStats(item) {
 for (var i = 0; i < config[item].length; i++) {
  globalStats[item].numFiles++;
  globalStats[item].inputSize += fs.statSync(config[item][i]).size;
 }
}

function printResults(callback) {
 globalStats.total.numFiles = globalStats.js.numFiles + globalStats.html.numFiles + globalStats.css.numFiles;
 globalStats.total.inputSize = globalStats.js.inputSize + globalStats.html.inputSize + globalStats.css.inputSize;
 if (config.mode != "test") {
  globalStats.total.outputSize = fs.statSync(config.outputFile).size + globalStats.css.outputSize;
 }

 for (item in globalStats) {
  globalStats[item].compression = globalStats[item].compression();
  if (globalStats[item].compression === "NaN%" || config.mode === "test" || (!config.extension[item] && item != "total") ) {
   globalStats[item].compression = "0%";
  }
 }

 console.log("***************************************************************************************");
 console.log("STATS = " + util.inspect(globalStats));
 console.log("***************************************************************************************");
 console.log("\nthe file was saved in " + config.outputFile);
 callback(null, "printResults");
}

init();
<commit_msg>iris_packager Error fixed<commit_after>
var fs = require('fs');
var compressor = require('node-minify');
var extend = require('node.extend');
var fileset = require('fileset');
var util = require('util');
var path = require('path');
var async = require('async');

var config = {
 html: [],
 js: [],
 css: [],
 outputFile: "",
 initJs: "",
 pathBase: "",
 inputPaths: [],
 excludePaths: [],
 extension: {
  html: true,
  js: true,
  css: true
 },
 outputPath: "",
 cssSuffix:"-min",
 mode: "",
 deleteInputs: false
}

function ItemStats() {
 this.numFiles = 0;
 this.inputSize = 0;
 this.outputSize = 0;
 this.compression = function () {
  return parseInt((this.inputSize - this.outputSize) / this.inputSize * 100) + "%";
 };
}

var globalStats = {
 js:  new ItemStats(),
 html:new ItemStats(),
 css: new ItemStats(),
 total: new ItemStats()
};

function init () {
 var file_config = null;
 if (fs.existsSync("iris_packager.json") && fs.statSync("iris_packager.json").isFile()) {
  file_config = fs.readFileSync("iris_packager.json");
  config = extend(true, config, JSON.parse(file_config));
 }

 process.argv.forEach(
  function (val, index, array) {
   var param;

   param = getParam("input=", val);
   if ( param !== null ) {
    config.inputPaths = param.split(",");
   }

   param = getParam("exclude=", val);
   if ( param !== null ) {
    config.excludePaths = param.split(",");
   }

   param = getParam("ext=", val);
   if ( param !== null ) {
    config.extension = (function toObject(arr) {
     var rv = {};
     for (var i = 0; i < arr.length; ++i)
      rv[arr[i]] = true;
     return rv;
    })(param.split(","));
   }

   param = getParam("output=", val);
   if ( param !== null ) {
    config.outputPath = param;
    config.outputFile = "";
   }

   param = getParam("init=", val);
   if ( param !== null ) {
    config.initJs = param;
   }

   param = getParam("base=", val);
   if ( param !== null ) {
    config.pathBase = param;
   }

   param = getParam("cssSuffix=", val);
   if ( param !== null ) {
    config.cssSuffix = param;
   }

   param = getParam("mode=", val);
   if ( param !== null ) {
    config.mode = param;
   }

   param = getParam("delete=", val);
   if ( param !== null ) {
    config.deleteInputs = param;
   }
  }
  );

 if ( !config.inputPaths ) {
 //throw "you must specify the parameter input=path_to_directory/,path_to_file...";
 }

 if ( !config.outputPath && !config.outputFile ) {
  throw "you must specify the parameter output=path/";
 }

 if ( !config.initJs ) {
  throw "you must specify the parameter init=path/file.js";
 }

 if (!config.outputFile) {
  if (fs.existsSync(config.outputPath) && fs.statSync(config.outputPath).isDirectory()) {
   config.outputFile = validatePath(config.outputPath) + config.initJs.match(/[^/]+\.js$/);
  } else {
   config.outputFile = config.outputPath;
  }
 }

 config.js.push(config.initJs);
 console.log("***************************************************************************************");
 console.log("config = " + util.inspect(config));
 console.log("***************************************************************************************");

 var includes = "";
 var excludes = "";
 if (config.inputPaths.length > 0) {
  for (var i = 0; i < config.inputPaths.length; i++) {
   config.inputPaths[i] = config.pathBase + config.inputPaths[i];
  }
  includes = config.inputPaths.join(" ");
  if (config.excludePaths.length > 0) {
   for (i = 0; i < config.excludePaths.length; i++) {
    config.excludePaths[i] = config.pathBase + config.excludePaths[i];
   }
   excludes = config.excludePaths.join(" ");
  }

  fileset(includes,  excludes, function(err, files) {
   if (err) return console.error(err);
   for (var i = 0; i < files.length; i++) {
    var initPath = files[i];
    console.log("scanning '" + initPath + "'...");

    var stats = fs.statSync(initPath);
    if (stats.isFile()) {
     scanFile(initPath);
    } else {
     scanPath(validatePath(initPath));
    }
   }

  }).on('end', function() {

   generateOutput();
  });
 }
}

function validatePath (path) {
 if ( path.substr(-1) !== "/" ) {
  path = path + "/";
 }
 return path;
}

function getParam (label, value) {
 var idx = value.indexOf(label);
 if ( idx > -1 ) {
  return value.substring( idx + label.length );
 }
 return null;
}

function scanPath (path) {
 var filenames = fs.readdirSync(path);
 var fullPath;
 filenames.forEach(function(file) {
  fullPath = path + file;

  if ( fs.statSync(fullPath).isDirectory() ) {
   console.log("found dir '" + fullPath + "'...");
   scanPath(fullPath + "/");
  } else if ( fs.statSync(fullPath).isFile() ) {
   scanFile(fullPath);
  }
 });
}

function scanFile(file) {

 var ext = path.extname(file).replace(".","");

 if (ext === "html" || ext === "js" || ext === "css") {
  console.log("found " + ext + " '" + file + "'...");
  config[ext].push(file);
 }
}

function generateOutput () {
 console.log("Gerenating output. Wait please...");

 calculateInputStats("js");
 calculateInputStats("html");
 calculateInputStats("css");

 async.series([
  function(callback) {
   minifyJs(callback);
  },
  function(callback) {
   minifyCSS(callback);
  },
  function(callback) {
   concatTemplates(callback);
  },
  function(callback) {
   deleteFiles(callback);
  }
  ,function(callback) {
   printResults(callback);
  }
  ]);
}

function minifyJs(callback) {
 if (!config.extension.js || config.js.length == 0 || config.mode === "test") {
  callback(null, "minifyJs");
  return;
 }
 console.log("Minimizing JS...");

 // Compress using Google Closure
 new compressor.minify({
  type: 'gcc',
  fileIn: config.js,
  fileOut: config.outputFile,
  callback: function(err){
   if ( err ) {
    callback(err, "minifyJs");
   } else {
    globalStats.js.outputSize += fs.statSync(this.fileOut).size;
    callback(null, "minifyJs");
   }
  }
 });
}

function minifyCSS(callback) {
 if (!config.extension.css || config.css.length == 0 || config.mode === "test") {
  callback(null, "minifyCSS");
  return;
 }
 console.log("Minimizing CSS...");
 var filesProcessed = 0;
 for ( var f=0, F=config.css.length;f<F; f++ ) {
  var inCSS = config.css[f];
  (function (fileIn) {
   var outCSS = path.dirname(fileIn) + "/" + path.basename(fileIn, ".css") + config.cssSuffix + ".css";
   new compressor.minify({
    type: 'yui-css',
    fileIn: fileIn,
    fileOut: outCSS,
    callback: function(err){
     if ( err ) {
      callback(err, "minifyCSS");
     } else {
      filesProcessed++;
      globalStats.css.outputSize += fs.statSync(outCSS).size;
      if (filesProcessed == config.css.length) {
       callback(null, "minifyCSS");
      }
     }
    }
   });
  })(inCSS);
 }
}

function concatTemplates(callback){
 if (!config.extension.html || config.html.length == 0 || config.mode === "test") {
  callback(null, "concatTemplates");
  return;
 }
 console.log("Concatenating HTML...");
 var content = [];
 for ( var f=0, F=config.html.length;f<F; f++ ) {
  var data = fs.readFileSync(config.html[f], "utf8").replace(/[\r\n\t]/g,"");
  content.push(
   "iris.tmpl('"
   + config.html[f].replace(config.pathBase, "")
   +"','"
   + data.replace(/\'/g, "\\\'")
   + "');\n"
   );
 }

 fs.appendFile(
  config.outputFile,
  content.join(""),
  function(err) {
   if(err) {
    callback(err, "concatTemplates");
   } else {
    globalStats.html.outputSize = fs.statSync(config.outputFile).size - globalStats.js.outputSize;
    callback(null, "concatTemplates");
   }
  }
  );
}

function deleteFiles(callback) {
 if (config.mode === "test" || !config.deleteInputs || !config.cssSuffix) {
  callback(null, "deleteFiles");
  return;
 }
 console.log("Removing files...");
 for (var ext in config.extension) {
  if (ext) {
   for (var i = 0 ; i < config[ext].length; i++) {
    var file = config[ext][i];
    console.log("Removing " + file + "...");
    fs.unlinkSync(file);
   }
  }
 }
 callback(null, "deleteFiles");
}

function calculateInputStats(item) {
 for (var i = 0; i < config[item].length; i++) {
  globalStats[item].numFiles++;
  globalStats[item].inputSize += fs.statSync(config[item][i]).size;
 }
}

function printResults(callback) {
 globalStats.total.numFiles = globalStats.js.numFiles + globalStats.html.numFiles + globalStats.css.numFiles;
 globalStats.total.inputSize = globalStats.js.inputSize + globalStats.html.inputSize + globalStats.css.inputSize;
 if (config.mode != "test") {
  globalStats.total.outputSize = fs.statSync(config.outputFile).size + globalStats.css.outputSize;
 }

 for (item in globalStats) {
  globalStats[item].compression = globalStats[item].compression();
  if (globalStats[item].compression === "NaN%" || config.mode === "test" || (!config.extension[item] && item != "total") ) {
   globalStats[item].compression = "0%";
  }
 }

 console.log("***************************************************************************************");
 console.log("STATS = " + util.inspect(globalStats));
 console.log("***************************************************************************************");
 console.log("\nthe file was saved in " + config.outputFile);
 callback(null, "printResults");
}

init();
<|endoftext|>


<commit_before>var _ = require('underscore');

var buildmessage = require('./buildmessage.js');
var compiler = require('./compiler.js');
var files = require('./files.js');
var isopackModule = require('./isopack.js');
var utils = require('./utils.js');
var watch = require('./watch.js');
var colonConverter = require("./colon-converter.js");

exports.IsopackCache = function (options) {
  var self = this;
  options = options || {};

  // cacheDir may be null; in this case, we just don't ever save things to disk.
  self.cacheDir = options.cacheDir;

  // This is a bit of a hack, but basically: we really don't want to spend time
  // building web.cordova unibuilds in a project that doesn't have any Cordova
  // platforms. (Note that we need to be careful with 'meteor publish' to still
  // publish a web.cordova unibuild!)
  self._includeCordovaUnibuild = !! options.includeCordovaUnibuild;

  // Defines the versions of packages that we build. Must be set.
  self._packageMap = options.packageMap;

  // tropohouse may be null; in this case, we can't load versioned packages.
  // eg, for building isopackets.
  self._tropohouse = options.tropohouse;

  // If provided, this is another IsopackCache for the same cache dir; when
  // loading Isopacks, if they are definitely unchanged we can load the
  // in-memory objects from this cache instead of recompiling.
  self._previousIsopackCache = options.previousIsopackCache;
  if (self._previousIsopackCache &&
      self._previousIsopackCache.cacheDir !== self.cacheDir) {
    throw Error("previousIsopackCache has different cacheDir!");
  }

  // Map from package name to Isopack.
  self._isopacks = {};

  self._noLineNumbers = !! options.noLineNumbers;

  self.allLoadedLocalPackagesWatchSet = new watch.WatchSet;
};

_.extend(exports.IsopackCache.prototype, {
  buildLocalPackages: function (rootPackageNames) {
    var self = this;
    buildmessage.assertInCapture();

    if (self.cacheDir)
      files.mkdir_p(self.cacheDir);

    var onStack = {};
    if (rootPackageNames) {
      _.each(rootPackageNames, function (name) {
        self._ensurePackageLoaded(name, onStack);
      });
    } else {
      self._packageMap.eachPackage(function (name, packageInfo) {
        self._ensurePackageLoaded(name, onStack);
      });
    }
  },

  wipeCachedPackages: function (packages) {
    var self = this;
    // If we're not saving things to disk, there's nothing to wipe!
    if (! self.cacheDir)
      return;
    if (packages) {
      // Wipe specific packages.
      _.each(packages, function (packageName) {
        files.rm_recursive(self._isopackDir(packageName));
      });
    } else {
      // Wipe all packages.
      files.rm_recursive(self.cacheDir);
    }
  },

  // Returns the isopack (already loaded in memory) for a given name. It is an
  // error to call this if it's not already loaded! So it should only be called
  // after buildLocalPackages has returned, or in the process of building a
  // package whose dependencies have all already been built.
  getIsopack: function (name) {
    var self = this;
    if (! _.has(self._isopacks, name))
      throw Error("isopack " + name + " not yet loaded?");
    return self._isopacks[name];
  },

  eachBuiltIsopack: function (iterator) {
    var self = this;
    _.each(self._isopacks, function (isopack, packageName) {
      iterator(packageName, isopack);
    });
  },

  _ensurePackageLoaded: function (name, onStack) {
    var self = this;
    buildmessage.assertInCapture();
    if (_.has(self._isopacks, name))
      return;

    var ensureLoaded = function (depName) {
      if (_.has(onStack, depName)) {
        buildmessage.error("circular dependency between packages " +
                           name + " and " + depName);
        // recover by not enforcing one of the dependencies
        return;
      }
      onStack[depName] = true;
      self._ensurePackageLoaded(depName, onStack);
      delete onStack[depName];
    };

    var packageInfo = self._packageMap.getInfo(name);
    if (! packageInfo)
      throw Error("Depend on unknown package " + name + "?");
    var previousIsopack = null;
    if (self._previousIsopackCache &&
        _.has(self._previousIsopackCache._isopacks, name)) {
      var previousInfo = self._previousIsopackCache._packageMap.getInfo(name);
      if ((packageInfo.kind === 'versioned' &&
           previousInfo.kind === 'versioned' &&
           packageInfo.version === previousInfo.version) ||
          (packageInfo.kind === 'local' &&
           previousInfo.kind === 'local' &&
           (packageInfo.packageSource.sourceRoot ===
            previousInfo.packageSource.sourceRoot))) {
        previousIsopack = self._previousIsopackCache._isopacks[name];
      }
    }

    if (packageInfo.kind === 'local') {
      var packageNames =
            packageInfo.packageSource.getPackagesToLoadFirst(self._packageMap);
      buildmessage.enterJob("preparing to build package " + name, function () {
        _.each(packageNames, function (depName) {
          ensureLoaded(depName);
        });
        // If we failed to load something that this package depends on, don't
        // load it.
        if (buildmessage.jobHasMessages())
          return;
        self._loadLocalPackage(name, packageInfo, previousIsopack);
      });
    } else if (packageInfo.kind === 'versioned') {
      // We don't have to build this package, and we don't have to build its
      // dependencies either! Just load it from disk.

      if (!self._tropohouse) {
        throw Error("Can't load versioned packages without a tropohouse!");
      }

      var isopack = null, packagesToLoad = [];
      if (previousIsopack) {
        isopack = previousIsopack;
        packagesToLoad = isopack.getStrongOrderedUsedAndImpliedPackages();
      }
      if (! isopack) {
        // Load the isopack from disk.
        buildmessage.enterJob(
          "loading package " + name + "@" + packageInfo.version,
          function () {
            var isopackPath = self._tropohouse.packagePath(
              name, packageInfo.version);
            var Isopack = isopackModule.Isopack;
            isopack = new Isopack();
            isopack.initFromPath(name, isopackPath);
            // If loading the isopack fails, then we don't need to look for more
            // packages to load, but we should still recover by putting it in
            // self._isopacks.
            if (buildmessage.jobHasMessages())
              return;
            packagesToLoad = isopack.getStrongOrderedUsedAndImpliedPackages();
          });
      }

      self._isopacks[name] = isopack;
      // Also load its dependencies. This is so that if this package is being
      // built as part of a plugin, all the transitive dependencies of the
      // plugin are loaded.
      _.each(packagesToLoad, function (packageToLoad) {
        ensureLoaded(packageToLoad);
      });
    } else {
      throw Error("unknown packageInfo kind?");
    }
  },

  _loadLocalPackage: function (name, packageInfo, previousIsopack) {
    var self = this;
    buildmessage.assertInCapture();
    buildmessage.enterJob("building package " + name, function () {
      var isopack;
      if (previousIsopack && self._checkUpToDatePreloaded(previousIsopack)) {
        isopack = previousIsopack;
      } else {
        // Do we have an up-to-date package on disk?
        var isopackBuildInfoJson = self.cacheDir && files.readJSONOrNull(
          self._isopackBuildInfoPath(name));
        var upToDate = self._checkUpToDate(isopackBuildInfoJson);

        if (upToDate) {
          var Isopack = isopackModule.Isopack;
          isopack = new Isopack();
          isopack.initFromPath(name, self._isopackDir(name), {
            isopackBuildInfoJson: isopackBuildInfoJson
          });
        } else {
          // Nope! Compile it again.
          isopack = compiler.compile(packageInfo.packageSource, {
            packageMap: self._packageMap,
            isopackCache: self,
            noLineNumbers: self._noLineNumbers,
            includeCordovaUnibuild: self._includeCordovaUnibuild,
            includePluginProviderPackageMap: true
          });
          // Accept the compiler's result, even if there were errors (since it
          // at least will have a useful WatchSet and will allow us to keep
          // going and compile other packages that depend on this one).
          if (self.cacheDir && ! buildmessage.jobHasMessages()) {
            // Save to disk, for next time!
            isopack.saveToPath(self._isopackDir(name), {
              includeIsopackBuildInfo: true
            });
          }
        }
      }

      self.allLoadedLocalPackagesWatchSet.merge(isopack.getMergedWatchSet());
      self._isopacks[name] = isopack;
    });
  },

  _checkUpToDate: function (isopackBuildInfoJson) {
    var self = this;
    // If there isn't an isopack-buildinfo.json file, then we definitely aren't
    // up to date!
    if (! isopackBuildInfoJson)
      return false;

    // If we include Cordova but this Isopack doesn't, or via versa, then we're
    // not up to date.
    if (self._includeCordovaUnibuild !==
        isopackBuildInfoJson.includeCordovaUnibuild) {
      return false;
    }

    // If any of the direct dependencies changed their version or location, we
    // aren't up to date.
    if (!self._packageMap.isSupersetOfJSON(
      isopackBuildInfoJson.pluginProviderPackageMap)) {
      return false;
    }
    // Merge in the watchsets for all unibuilds and plugins in the package, then
    // check it once.
    var watchSet = watch.WatchSet.fromJSON(
      isopackBuildInfoJson.pluginDependencies);

    _.each(isopackBuildInfoJson.unibuildDependencies, function (deps) {
      watchSet.merge(watch.WatchSet.fromJSON(deps));
    });
    return watch.isUpToDate(watchSet);
  },

  _checkUpToDatePreloaded: function (previousIsopack) {
    var self = this;

    // If we include Cordova but this Isopack doesn't, or via versa, then we're
    // not up to date.
    if (self._includeCordovaUnibuild !== previousIsopack.hasCordovaUnibuild()) {
      return false;
    }

    // If any of the direct dependencies changed their version or location, we
    // aren't up to date.
    if (!self._packageMap.isSupersetOfJSON(
      previousIsopack.pluginProviderPackageMap)) {
      return false;
    }
    // Merge in the watchsets for all unibuilds and plugins in the package, then
    // check it once.
    var watchSet = previousIsopack.getMergedWatchSet();
    return watch.isUpToDate(watchSet);
  },

  _isopackDir: function (packageName) {
    var self = this;
    return files.pathJoin(self.cacheDir, colonConverter.convert(packageName));
  },

  _isopackBuildInfoPath: function (packageName) {
    var self = this;
    return files.pathJoin(
      self._isopackDir(packageName), 'isopack-buildinfo.json');
  },

  forgetPreviousIsopackCache: function () {
    var self = this;
    self._previousIsopackCache = null;
  }
});
<commit_msg>Actually use builtBy!  (Regression in 1.0.2.)<commit_after>var _ = require('underscore');

var buildmessage = require('./buildmessage.js');
var compiler = require('./compiler.js');
var files = require('./files.js');
var isopackModule = require('./isopack.js');
var utils = require('./utils.js');
var watch = require('./watch.js');
var colonConverter = require("./colon-converter.js");

exports.IsopackCache = function (options) {
  var self = this;
  options = options || {};

  // cacheDir may be null; in this case, we just don't ever save things to disk.
  self.cacheDir = options.cacheDir;

  // This is a bit of a hack, but basically: we really don't want to spend time
  // building web.cordova unibuilds in a project that doesn't have any Cordova
  // platforms. (Note that we need to be careful with 'meteor publish' to still
  // publish a web.cordova unibuild!)
  self._includeCordovaUnibuild = !! options.includeCordovaUnibuild;

  // Defines the versions of packages that we build. Must be set.
  self._packageMap = options.packageMap;

  // tropohouse may be null; in this case, we can't load versioned packages.
  // eg, for building isopackets.
  self._tropohouse = options.tropohouse;

  // If provided, this is another IsopackCache for the same cache dir; when
  // loading Isopacks, if they are definitely unchanged we can load the
  // in-memory objects from this cache instead of recompiling.
  self._previousIsopackCache = options.previousIsopackCache;
  if (self._previousIsopackCache &&
      self._previousIsopackCache.cacheDir !== self.cacheDir) {
    throw Error("previousIsopackCache has different cacheDir!");
  }

  // Map from package name to Isopack.
  self._isopacks = {};

  self._noLineNumbers = !! options.noLineNumbers;

  self.allLoadedLocalPackagesWatchSet = new watch.WatchSet;
};

_.extend(exports.IsopackCache.prototype, {
  buildLocalPackages: function (rootPackageNames) {
    var self = this;
    buildmessage.assertInCapture();

    if (self.cacheDir)
      files.mkdir_p(self.cacheDir);

    var onStack = {};
    if (rootPackageNames) {
      _.each(rootPackageNames, function (name) {
        self._ensurePackageLoaded(name, onStack);
      });
    } else {
      self._packageMap.eachPackage(function (name, packageInfo) {
        self._ensurePackageLoaded(name, onStack);
      });
    }
  },

  wipeCachedPackages: function (packages) {
    var self = this;
    // If we're not saving things to disk, there's nothing to wipe!
    if (! self.cacheDir)
      return;
    if (packages) {
      // Wipe specific packages.
      _.each(packages, function (packageName) {
        files.rm_recursive(self._isopackDir(packageName));
      });
    } else {
      // Wipe all packages.
      files.rm_recursive(self.cacheDir);
    }
  },

  // Returns the isopack (already loaded in memory) for a given name. It is an
  // error to call this if it's not already loaded! So it should only be called
  // after buildLocalPackages has returned, or in the process of building a
  // package whose dependencies have all already been built.
  getIsopack: function (name) {
    var self = this;
    if (! _.has(self._isopacks, name))
      throw Error("isopack " + name + " not yet loaded?");
    return self._isopacks[name];
  },

  eachBuiltIsopack: function (iterator) {
    var self = this;
    _.each(self._isopacks, function (isopack, packageName) {
      iterator(packageName, isopack);
    });
  },

  _ensurePackageLoaded: function (name, onStack) {
    var self = this;
    buildmessage.assertInCapture();
    if (_.has(self._isopacks, name))
      return;

    var ensureLoaded = function (depName) {
      if (_.has(onStack, depName)) {
        buildmessage.error("circular dependency between packages " +
                           name + " and " + depName);
        // recover by not enforcing one of the dependencies
        return;
      }
      onStack[depName] = true;
      self._ensurePackageLoaded(depName, onStack);
      delete onStack[depName];
    };

    var packageInfo = self._packageMap.getInfo(name);
    if (! packageInfo)
      throw Error("Depend on unknown package " + name + "?");
    var previousIsopack = null;
    if (self._previousIsopackCache &&
        _.has(self._previousIsopackCache._isopacks, name)) {
      var previousInfo = self._previousIsopackCache._packageMap.getInfo(name);
      if ((packageInfo.kind === 'versioned' &&
           previousInfo.kind === 'versioned' &&
           packageInfo.version === previousInfo.version) ||
          (packageInfo.kind === 'local' &&
           previousInfo.kind === 'local' &&
           (packageInfo.packageSource.sourceRoot ===
            previousInfo.packageSource.sourceRoot))) {
        previousIsopack = self._previousIsopackCache._isopacks[name];
      }
    }

    if (packageInfo.kind === 'local') {
      var packageNames =
            packageInfo.packageSource.getPackagesToLoadFirst(self._packageMap);
      buildmessage.enterJob("preparing to build package " + name, function () {
        _.each(packageNames, function (depName) {
          ensureLoaded(depName);
        });
        // If we failed to load something that this package depends on, don't
        // load it.
        if (buildmessage.jobHasMessages())
          return;
        self._loadLocalPackage(name, packageInfo, previousIsopack);
      });
    } else if (packageInfo.kind === 'versioned') {
      // We don't have to build this package, and we don't have to build its
      // dependencies either! Just load it from disk.

      if (!self._tropohouse) {
        throw Error("Can't load versioned packages without a tropohouse!");
      }

      var isopack = null, packagesToLoad = [];
      if (previousIsopack) {
        isopack = previousIsopack;
        packagesToLoad = isopack.getStrongOrderedUsedAndImpliedPackages();
      }
      if (! isopack) {
        // Load the isopack from disk.
        buildmessage.enterJob(
          "loading package " + name + "@" + packageInfo.version,
          function () {
            var isopackPath = self._tropohouse.packagePath(
              name, packageInfo.version);
            var Isopack = isopackModule.Isopack;
            isopack = new Isopack();
            isopack.initFromPath(name, isopackPath);
            // If loading the isopack fails, then we don't need to look for more
            // packages to load, but we should still recover by putting it in
            // self._isopacks.
            if (buildmessage.jobHasMessages())
              return;
            packagesToLoad = isopack.getStrongOrderedUsedAndImpliedPackages();
          });
      }

      self._isopacks[name] = isopack;
      // Also load its dependencies. This is so that if this package is being
      // built as part of a plugin, all the transitive dependencies of the
      // plugin are loaded.
      _.each(packagesToLoad, function (packageToLoad) {
        ensureLoaded(packageToLoad);
      });
    } else {
      throw Error("unknown packageInfo kind?");
    }
  },

  _loadLocalPackage: function (name, packageInfo, previousIsopack) {
    var self = this;
    buildmessage.assertInCapture();
    buildmessage.enterJob("building package " + name, function () {
      var isopack;
      if (previousIsopack && self._checkUpToDatePreloaded(previousIsopack)) {
        isopack = previousIsopack;
      } else {
        // Do we have an up-to-date package on disk?
        var isopackBuildInfoJson = self.cacheDir && files.readJSONOrNull(
          self._isopackBuildInfoPath(name));
        var upToDate = self._checkUpToDate(isopackBuildInfoJson);

        if (upToDate) {
          var Isopack = isopackModule.Isopack;
          isopack = new Isopack();
          isopack.initFromPath(name, self._isopackDir(name), {
            isopackBuildInfoJson: isopackBuildInfoJson
          });
        } else {
          // Nope! Compile it again.
          isopack = compiler.compile(packageInfo.packageSource, {
            packageMap: self._packageMap,
            isopackCache: self,
            noLineNumbers: self._noLineNumbers,
            includeCordovaUnibuild: self._includeCordovaUnibuild,
            includePluginProviderPackageMap: true
          });
          // Accept the compiler's result, even if there were errors (since it
          // at least will have a useful WatchSet and will allow us to keep
          // going and compile other packages that depend on this one).
          if (self.cacheDir && ! buildmessage.jobHasMessages()) {
            // Save to disk, for next time!
            isopack.saveToPath(self._isopackDir(name), {
              includeIsopackBuildInfo: true
            });
          }
        }
      }

      self.allLoadedLocalPackagesWatchSet.merge(isopack.getMergedWatchSet());
      self._isopacks[name] = isopack;
    });
  },

  _checkUpToDate: function (isopackBuildInfoJson) {
    var self = this;
    // If there isn't an isopack-buildinfo.json file, then we definitely aren't
    // up to date!
    if (! isopackBuildInfoJson)
      return false;

    // If we include Cordova but this Isopack doesn't, or via versa, then we're
    // not up to date.
    if (self._includeCordovaUnibuild !==
        isopackBuildInfoJson.includeCordovaUnibuild) {
      return false;
    }

    // Was the package built by a different compiler version?
    if (isopackBuildInfoJson.builtBy !== compiler.BUILT_BY) {
      return false;
    }

    // If any of the direct dependencies changed their version or location, we
    // aren't up to date.
    if (!self._packageMap.isSupersetOfJSON(
      isopackBuildInfoJson.pluginProviderPackageMap)) {
      return false;
    }
    // Merge in the watchsets for all unibuilds and plugins in the package, then
    // check it once.
    var watchSet = watch.WatchSet.fromJSON(
      isopackBuildInfoJson.pluginDependencies);

    _.each(isopackBuildInfoJson.unibuildDependencies, function (deps) {
      watchSet.merge(watch.WatchSet.fromJSON(deps));
    });
    return watch.isUpToDate(watchSet);
  },

  _checkUpToDatePreloaded: function (previousIsopack) {
    var self = this;

    // If we include Cordova but this Isopack doesn't, or via versa, then we're
    // not up to date.
    if (self._includeCordovaUnibuild !== previousIsopack.hasCordovaUnibuild()) {
      return false;
    }

    // We don't have to check builtBy because we don't change BUILT_BY without
    // restarting the process.

    // If any of the direct dependencies changed their version or location, we
    // aren't up to date.
    if (!self._packageMap.isSupersetOfJSON(
      previousIsopack.pluginProviderPackageMap)) {
      return false;
    }
    // Merge in the watchsets for all unibuilds and plugins in the package, then
    // check it once.
    var watchSet = previousIsopack.getMergedWatchSet();
    return watch.isUpToDate(watchSet);
  },

  _isopackDir: function (packageName) {
    var self = this;
    return files.pathJoin(self.cacheDir, colonConverter.convert(packageName));
  },

  _isopackBuildInfoPath: function (packageName) {
    var self = this;
    return files.pathJoin(
      self._isopackDir(packageName), 'isopack-buildinfo.json');
  },

  forgetPreviousIsopackCache: function () {
    var self = this;
    self._previousIsopackCache = null;
  }
});
<|endoftext|>


<commit_before>/** This section should be used as a super class for sections which create tables of entities.
 * Those entities are for example partners and branches.
 */

'use strict';

var _                       = require('mano').i18n.bind("Model: Form Entities Table")
  , memoize                 = require('memoizee/plain')
  , validDb                 = require('dbjs/valid-dbjs')
  , defineStringLine        = require('dbjs-ext/string/string-line')
  , defineFormSectionBase   = require('./form-section-base')
  , defineFormTabularEntity = require('./form-tabular-entity')
  , defineUInteger          = require('dbjs-ext/number/integer/u-integer')
  , defineProgressRule      = require('./lib/progress-rule')
  , defineProgressRules     = require('./lib/progress-rules');

module.exports = memoize(function (db) {
	var FormEntitiesTable, StringLine, FormSectionBase, FormTabularEntity, UInteger
	  , ProgressRule, ProgressRules;
	validDb(db);
	StringLine        = defineStringLine(db);
	FormSectionBase   = defineFormSectionBase(db);
	FormTabularEntity = defineFormTabularEntity(db);
	UInteger          = defineUInteger(db);
	ProgressRule      = defineProgressRule(db);
	ProgressRules     = defineProgressRules(db);
	FormEntitiesTable = FormSectionBase.extend('FormEntitiesTable', {
		progressRules: { type: ProgressRules, nested: true },
		min: { type: UInteger },
		max: { type: UInteger },
		status: {
			value: function (_observe) {
				var weight = 0, progress = 0;

				// Take into account resolvent
				progress += this.resolventStatus * this.resolventWeight;
				weight += this.resolventWeight;

				// If section is unresolved, exit just with resolvent
				if (this.isUnresolved) {
					if (!weight) return 1;
					return progress / weight;
				}

				// Take into account all rules
				progress += _observe(this.progressRules._progress) * _observe(this.progressRules._weight);
				weight += this.progressRules.weight;

				if (!weight) return 1;
				return progress / weight;
			}
		},
		weight: { value: function (_observe) {
			if (this.isUnresolved) {
				return this.resolventWeight;
			}
			return _observe(this.progressRules._weight) + this.resolventWeight;
		} },
		getWeightByEntity: {
			type: db.Function,
			value: function (entityObject, _observe) {
				if (this.sectionProperty === 'dataForms') {
					var dataForms = entityObject.resolveSKeyPath(this.sectionProperty, _observe);
					if (!dataForms) {
						return 0;
					}
					return _observe(dataForms.object.dataForms._weight);
				}
				// for backwards compatibility
				var resolved = entityObject.resolveSKeyPath(this.sectionProperty + 'Weight', _observe);
				if (!resolved) {
					return 0;
				}
				return _observe(resolved.observable);
			}
		},
		// returns a set of all currently setup entities, resolves some new / old model logic
		entitiesSet: {
			multiple: true,
			value: function (_observe) {
				var entityObjects = this.master.resolveSKeyPath(this.propertyName, _observe);
				if (!entityObjects) {
					return;
				}
				entityObjects = entityObjects.value;
				if (entityObjects instanceof this.database.NestedMap) {
					entityObjects = entityObjects.ordered;
				}
				return _observe(entityObjects);
			}
		},
		lastEditStamp: {
			value: function (_observe) {
				var res = 0, entityObjects, sectionKey, resolvent, resolventLastModified;
				entityObjects = this.entitiesSet;
				sectionKey = this.sectionProperty;
				if (this.resolventProperty) {
					resolvent = _observe(this.master.resolveSKeyPath(this.resolventProperty).observable);
					resolventLastModified = resolvent ? resolvent.lastModified : 0;
				}
				if (!entityObjects) {
					return resolventLastModified;
				}
				entityObjects.forEach(function (entityObject) {
					var sections;
					sections = entityObject.resolveSKeyPath(sectionKey, _observe);
					sections = sections.object[sections.key];
					if (sectionKey === 'dataForms') {
						sections = sections.applicable;
					}
					sections.forEach(function (section) {
						if (_observe(section._lastEditStamp) > res) res = section.lastEditStamp;
					});
				});
				if (resolventLastModified > res) res = resolventLastModified;

				return res;
			}
		},
		actionUrl: { required: false },
		// The base url around which links and postButtons to entities will be created.
		// By convention following links to following urls will be created:
		// section.baseUrl + '-add',
		// section.baseUrl + '/' + <entityId>.
		// By convention url to delete an entity will be created:
		// section.baseUrl + '/' + <entityId> + '/delete'
		baseUrl: { type: StringLine, required: true },
		// The name of section's master property to which the section refers i.e: partners
		propertyName: { type: StringLine, required: true },
		// The name of property of entity from which the header will be taken i.e:
		// name (the header will be taken from partner.name)
		entityTitleProperty: { type: StringLine, required: true },
		// Set of objects of type FormTabularEntity which represent the table columns
		entities: { type: FormTabularEntity, multiple: true, unique: true },
		// A placeholder for custom footer definition.
		// If you want to create custom definition of footer,
		// you should do it on section.generateFooter
		// (this must be ecmaScript, not dbjs definition, so place it in view/dbjs).
		// Your custom generateFooter function will receive one argument
		// (with the value of propertyName of section's master i.e. the partners set of given user).
		generateFooter: { type: db.Function },
		// The name of the property on which the sections collection has been defined on the entity.
		// For example formSections.
		sectionProperty: { type: StringLine, required: true },
		// The text of message displayed when there are no entities.
		onEmptyMessage: { type: StringLine, value: _("There are no elements added at the moment.") }
	});

	FormEntitiesTable.prototype.progressRules.map.define('entities', {
		type: ProgressRule,
		nested: true
	});
	FormEntitiesTable.prototype.progressRules.map.get('entities').setProperties({
		message: _("Some of the added items are incomplete."),
		progress: function (_observe) {
			var statusSum, tabularSection, statusKey, weightKey, i, dataForms;
			i = 0;
			statusSum = 0;
			tabularSection = this.owner.owner.owner;
			statusKey = tabularSection.sectionProperty + 'Status';
			weightKey = tabularSection.sectionProperty + 'Weight';
			if (!_observe(tabularSection._entitiesSet)) {
				return 0;
			}
			tabularSection.entitiesSet.some(function (entityObject) {
				var resolvedStatus, resolvedWeight;
				i++;
				if (tabularSection.sectionProperty === 'dataForms') {
					dataForms = entityObject.resolveSKeyPath(tabularSection.sectionProperty, _observe);
					if (!dataForms) {
						return;
					}
					resolvedStatus = _observe(dataForms.value._progress);
					resolvedWeight = _observe(dataForms.value._weight);
				} else {
					resolvedStatus = entityObject.resolveSKeyPath(statusKey, _observe);
					resolvedWeight = entityObject.resolveSKeyPath(weightKey, _observe);
					if (!resolvedStatus || !resolvedWeight) {
						return;
					}
					resolvedStatus = _observe(resolvedStatus.observable);
					resolvedWeight = _observe(resolvedWeight.observable);
				}
				if (tabularSection.max && (i > tabularSection.max)) {
					return true;
				}
				statusSum += (resolvedStatus * resolvedWeight);
			});
			return statusSum / this.weight;
		},
		weight: function (_observe) {
			var weightTotal, i, tabularSection;
			weightTotal = 0;
			i = 0;
			tabularSection = this.owner.owner.owner;
			if (!_observe(tabularSection._entitiesSet)) {
				return 0;
			}
			tabularSection.entitiesSet.some(function (entityObject) {
				++i;
				if (_observe(tabularSection._max) && (i > tabularSection.max)) {
					return true;
				}
				weightTotal += tabularSection.getWeightByEntity(entityObject, _observe);
			});

			return weightTotal;
		}
	});

	FormEntitiesTable.prototype.progressRules.map.define('min', {
		type: ProgressRule,
		nested: true
	});
	FormEntitiesTable.prototype.progressRules.map.get('min').setProperties({
		message: _("To few items added."),
		progress: function () {
			return this.weight ? 0 : 1;
		},
		weight: function (_observe) {
			var entityObjects, mockWeight
		  , objectsType, tabularSection, mockWeightObject;
			tabularSection = this.owner.owner.owner;
			if (!_observe(tabularSection._min)) {
				return 0;
			}
			entityObjects = this.master.resolveSKeyPath(tabularSection.propertyName, _observe);
			if (!entityObjects) {
				return 0;
			}
			objectsType = entityObjects.descriptor.type;
			entityObjects = entityObjects.value;
			if (entityObjects instanceof this.database.NestedMap) {
				entityObjects = entityObjects.ordered;
				mockWeightObject = entityObjects.map.get('testOnlyDontUseMe');
			} else {
				mockWeightObject = objectsType.prototype;
			}
			_observe(entityObjects);
			if (entityObjects.size < tabularSection._min) {
				mockWeight = tabularSection.getWeightByEntity(mockWeightObject, _observe);

				// we assume that each potential entity has the same weight as prototype
				return mockWeight * (tabularSection.min - entityObjects.size);
			}
			return 0;
		}
	});

	FormEntitiesTable.prototype.progressRules.map.define('max', {
		type: ProgressRule,
		nested: true
	});
	FormEntitiesTable.prototype.progressRules.map.get('max').setProperties({
		message: _("To many items added."),
		progress: function () {
			return this.weight ? 0 : 1;
		},
		weight: function (_observe) {
			var entityObjects, tabularSection;
			tabularSection = this.owner.owner.owner;
			if (!_observe(tabularSection._max)) {
				return 0;
			}
			entityObjects = this.master.resolveSKeyPath(tabularSection.propertyName, _observe);
			if (!entityObjects) {
				return 0;
			}
			entityObjects = entityObjects.value;
			if (entityObjects instanceof this.database.NestedMap) {
				entityObjects = entityObjects.ordered;
			}
			_observe(entityObjects);
			if (tabularSection.max && (entityObjects.size > tabularSection.max)) {
				return entityObjects.size - tabularSection.max;
			}
			return 0;
		}
	});

	return FormEntitiesTable;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<commit_msg>#688 delayed mock creation<commit_after>/** This section should be used as a super class for sections which create tables of entities.
 * Those entities are for example partners and branches.
 */

'use strict';

var _                       = require('mano').i18n.bind("Model: Form Entities Table")
  , memoize                 = require('memoizee/plain')
  , validDb                 = require('dbjs/valid-dbjs')
  , defineStringLine        = require('dbjs-ext/string/string-line')
  , defineFormSectionBase   = require('./form-section-base')
  , defineFormTabularEntity = require('./form-tabular-entity')
  , defineUInteger          = require('dbjs-ext/number/integer/u-integer')
  , defineProgressRule      = require('./lib/progress-rule')
  , defineProgressRules     = require('./lib/progress-rules');

module.exports = memoize(function (db) {
	var FormEntitiesTable, StringLine, FormSectionBase, FormTabularEntity, UInteger
	  , ProgressRule, ProgressRules;
	validDb(db);
	StringLine        = defineStringLine(db);
	FormSectionBase   = defineFormSectionBase(db);
	FormTabularEntity = defineFormTabularEntity(db);
	UInteger          = defineUInteger(db);
	ProgressRule      = defineProgressRule(db);
	ProgressRules     = defineProgressRules(db);
	FormEntitiesTable = FormSectionBase.extend('FormEntitiesTable', {
		progressRules: { type: ProgressRules, nested: true },
		min: { type: UInteger },
		max: { type: UInteger },
		status: {
			value: function (_observe) {
				var weight = 0, progress = 0;

				// Take into account resolvent
				progress += this.resolventStatus * this.resolventWeight;
				weight += this.resolventWeight;

				// If section is unresolved, exit just with resolvent
				if (this.isUnresolved) {
					if (!weight) return 1;
					return progress / weight;
				}

				// Take into account all rules
				progress += _observe(this.progressRules._progress) * _observe(this.progressRules._weight);
				weight += this.progressRules.weight;

				if (!weight) return 1;
				return progress / weight;
			}
		},
		weight: { value: function (_observe) {
			if (this.isUnresolved) {
				return this.resolventWeight;
			}
			return _observe(this.progressRules._weight) + this.resolventWeight;
		} },
		getWeightByEntity: {
			type: db.Function,
			value: function (entityObject, _observe) {
				if (this.sectionProperty === 'dataForms') {
					var dataForms = entityObject.resolveSKeyPath(this.sectionProperty, _observe);
					if (!dataForms) {
						return 0;
					}
					return _observe(dataForms.object.dataForms._weight);
				}
				// for backwards compatibility
				var resolved = entityObject.resolveSKeyPath(this.sectionProperty + 'Weight', _observe);
				if (!resolved) {
					return 0;
				}
				return _observe(resolved.observable);
			}
		},
		// returns a set of all currently setup entities, resolves some new / old model logic
		entitiesSet: {
			multiple: true,
			value: function (_observe) {
				var entityObjects = this.master.resolveSKeyPath(this.propertyName, _observe);
				if (!entityObjects) {
					return;
				}
				entityObjects = entityObjects.value;
				if (entityObjects instanceof this.database.NestedMap) {
					entityObjects = entityObjects.ordered;
				}
				return _observe(entityObjects);
			}
		},
		lastEditStamp: {
			value: function (_observe) {
				var res = 0, entityObjects, sectionKey, resolvent, resolventLastModified;
				entityObjects = this.entitiesSet;
				sectionKey = this.sectionProperty;
				if (this.resolventProperty) {
					resolvent = _observe(this.master.resolveSKeyPath(this.resolventProperty).observable);
					resolventLastModified = resolvent ? resolvent.lastModified : 0;
				}
				if (!entityObjects) {
					return resolventLastModified;
				}
				entityObjects.forEach(function (entityObject) {
					var sections;
					sections = entityObject.resolveSKeyPath(sectionKey, _observe);
					sections = sections.object[sections.key];
					if (sectionKey === 'dataForms') {
						sections = sections.applicable;
					}
					sections.forEach(function (section) {
						if (_observe(section._lastEditStamp) > res) res = section.lastEditStamp;
					});
				});
				if (resolventLastModified > res) res = resolventLastModified;

				return res;
			}
		},
		actionUrl: { required: false },
		// The base url around which links and postButtons to entities will be created.
		// By convention following links to following urls will be created:
		// section.baseUrl + '-add',
		// section.baseUrl + '/' + <entityId>.
		// By convention url to delete an entity will be created:
		// section.baseUrl + '/' + <entityId> + '/delete'
		baseUrl: { type: StringLine, required: true },
		// The name of section's master property to which the section refers i.e: partners
		propertyName: { type: StringLine, required: true },
		// The name of property of entity from which the header will be taken i.e:
		// name (the header will be taken from partner.name)
		entityTitleProperty: { type: StringLine, required: true },
		// Set of objects of type FormTabularEntity which represent the table columns
		entities: { type: FormTabularEntity, multiple: true, unique: true },
		// A placeholder for custom footer definition.
		// If you want to create custom definition of footer,
		// you should do it on section.generateFooter
		// (this must be ecmaScript, not dbjs definition, so place it in view/dbjs).
		// Your custom generateFooter function will receive one argument
		// (with the value of propertyName of section's master i.e. the partners set of given user).
		generateFooter: { type: db.Function },
		// The name of the property on which the sections collection has been defined on the entity.
		// For example formSections.
		sectionProperty: { type: StringLine, required: true },
		// The text of message displayed when there are no entities.
		onEmptyMessage: { type: StringLine, value: _("There are no elements added at the moment.") }
	});

	FormEntitiesTable.prototype.progressRules.map.define('entities', {
		type: ProgressRule,
		nested: true
	});
	FormEntitiesTable.prototype.progressRules.map.get('entities').setProperties({
		message: _("Some of the added items are incomplete."),
		progress: function (_observe) {
			var statusSum, tabularSection, statusKey, weightKey, i, dataForms;
			i = 0;
			statusSum = 0;
			tabularSection = this.owner.owner.owner;
			statusKey = tabularSection.sectionProperty + 'Status';
			weightKey = tabularSection.sectionProperty + 'Weight';
			if (!_observe(tabularSection._entitiesSet)) {
				return 0;
			}
			tabularSection.entitiesSet.some(function (entityObject) {
				var resolvedStatus, resolvedWeight;
				i++;
				if (tabularSection.sectionProperty === 'dataForms') {
					dataForms = entityObject.resolveSKeyPath(tabularSection.sectionProperty, _observe);
					if (!dataForms) {
						return;
					}
					resolvedStatus = _observe(dataForms.value._progress);
					resolvedWeight = _observe(dataForms.value._weight);
				} else {
					resolvedStatus = entityObject.resolveSKeyPath(statusKey, _observe);
					resolvedWeight = entityObject.resolveSKeyPath(weightKey, _observe);
					if (!resolvedStatus || !resolvedWeight) {
						return;
					}
					resolvedStatus = _observe(resolvedStatus.observable);
					resolvedWeight = _observe(resolvedWeight.observable);
				}
				if (tabularSection.max && (i > tabularSection.max)) {
					return true;
				}
				statusSum += (resolvedStatus * resolvedWeight);
			});
			return statusSum / this.weight;
		},
		weight: function (_observe) {
			var weightTotal, i, tabularSection;
			weightTotal = 0;
			i = 0;
			tabularSection = this.owner.owner.owner;
			if (!_observe(tabularSection._entitiesSet)) {
				return 0;
			}
			tabularSection.entitiesSet.some(function (entityObject) {
				++i;
				if (_observe(tabularSection._max) && (i > tabularSection.max)) {
					return true;
				}
				weightTotal += tabularSection.getWeightByEntity(entityObject, _observe);
			});

			return weightTotal;
		}
	});

	FormEntitiesTable.prototype.progressRules.map.define('min', {
		type: ProgressRule,
		nested: true
	});
	FormEntitiesTable.prototype.progressRules.map.get('min').setProperties({
		message: _("To few items added."),
		progress: function () {
			return this.weight ? 0 : 1;
		},
		weight: function (_observe) {
			var entityObjects, mockWeight, objectsType, tabularSection, mockWeightObject;
			tabularSection = this.owner.owner.owner;
			if (!_observe(tabularSection._min)) {
				return 0;
			}
			entityObjects = this.master.resolveSKeyPath(tabularSection.propertyName, _observe);
			if (!entityObjects) {
				return 0;
			}
			objectsType   = entityObjects.descriptor.type;
			entityObjects = entityObjects.value;
			if (entityObjects instanceof this.database.NestedMap) {
				entityObjects = entityObjects.ordered;
			} else {
				mockWeightObject = objectsType.prototype;
			}
			_observe(entityObjects);
			if (entityObjects.size < tabularSection._min) {
				if (!mockWeightObject) {
					mockWeightObject = entityObjects.map.get('testOnlyDontUseMe');
				}
				mockWeight = tabularSection.getWeightByEntity(mockWeightObject, _observe);

				// we assume that each potential entity has the same weight as prototype
				return mockWeight * (tabularSection.min - entityObjects.size);
			}
			return 0;
		}
	});

	FormEntitiesTable.prototype.progressRules.map.define('max', {
		type: ProgressRule,
		nested: true
	});
	FormEntitiesTable.prototype.progressRules.map.get('max').setProperties({
		message: _("To many items added."),
		progress: function () {
			return this.weight ? 0 : 1;
		},
		weight: function (_observe) {
			var entityObjects, tabularSection;
			tabularSection = this.owner.owner.owner;
			if (!_observe(tabularSection._max)) {
				return 0;
			}
			entityObjects = this.master.resolveSKeyPath(tabularSection.propertyName, _observe);
			if (!entityObjects) {
				return 0;
			}
			entityObjects = entityObjects.value;
			if (entityObjects instanceof this.database.NestedMap) {
				entityObjects = entityObjects.ordered;
			}
			_observe(entityObjects);
			if (tabularSection.max && (entityObjects.size > tabularSection.max)) {
				return entityObjects.size - tabularSection.max;
			}
			return 0;
		}
	});

	return FormEntitiesTable;
}, { normalizer: require('memoizee/normalizers/get-1')() });
<|endoftext|>


<commit_before>/*
Add this file to $(ipython locate)/nbextensions/gist.js
And load it with:

require(["nbextensions/gist"], function (gist_extension) {
    console.log('gist extension loaded');
    gist_extension.load_extension();
});

*/
define( function () {

    var token_name = "gist_github_token";

    // dialog to request GitHub OAuth token
    // I'm not sure it's possible to step through OAuth purely client side,
    // so just ask the user to go create a token manually.

    var token_dialog = function () {
        var dialog = $('
').append(
            $("
")
                .html('Enter a GitHub OAuth token:')
        ).append(
            $("
")
        ).append(
            $('<input/>').attr('type','text').attr('size','40')
        );
        IPython.dialog.modal({
            title: "GitHub OAuth",
            body: dialog,
            buttons : {
                "Cancel": {},
                "OK": {
                    class: "btn-primary",
                    click: function () {
                        var token = $(this).find('input').val();
                        localStorage[token_name] = token;
                        gist_notebook();
                    }
                }
            },
            open : function (event, ui) {
                var that = $(this);
                // Upon ENTER, click the OK button.
                that.find('input[type="text"]').keydown(function (event, ui) {
                    if (event.which === utils.keycodes.ENTER) {
                        that.find('.btn-primary').first().click();
                        return false;
                    }
                });
                that.find('input[type="text"]').focus().select();
            }
        });
    };
    // get the GitHub token, via cookie or
    var get_github_token = function () {
        var token = localStorage[token_name];
        if (!token) {
            token_dialog();
            return null;
        }
        return token;
    };

    var gist_notebook = function () {
        var gist_id = IPython.notebook.metadata.gist_id;
        console.log(gist_id);
        var token = get_github_token();
        if (!token) {
            // dialog's are async, so we can't do anything yet.
            // the dialog OK callback will continue the process.
            console.log("waiting for auth dialog");
            return;
        }
        var method = "POST";
        var url = "https://api.github.com/gists";
        if (gist_id) {
            url = url + "/" + gist_id;
            method = "PATCH";
        }
        var filedata = {};
        var nbj = IPython.notebook.toJSON();
        nbj.nbformat = 3;
        filedata[IPython.notebook.notebook_name] = {content : JSON.stringify(nbj, undefined, 1)};
        console.log(token);
        var settings = {
            type : method,
            headers : { Authorization: "token " + token },
            data : JSON.stringify({
                public : true,
                files : filedata,
            }),
            success : function (data, status) {
                console.log("gist succeeded: " + data.id);
                IPython.notebook.metadata.gist_id = data.id;
                update_gist_link(data.id);
                IPython.notification_area.get_widget("notebook").set_message("gist succeeded: " + data.id, 500);
            },
            error : function (jqXHR, status, err) {
                console.log(jqXHR);
                if (jqXHR.status == 403) {
                    // authentication failed,
                    // delete the cookie so that we prompt again next time
                    delete localStorage[token_name];
                }
                alert("Uploading gist failed: " + err);
            }
        };
        $.ajax(url, settings);
    };

    var update_gist_link = function(gist_id) {
        if (!gist_id) {
            gist_id = IPython.notebook.metadata.gist_id;
        } else {
            IPython.notebook.metadata.gist_id = gist_id;
        }
        if (!gist_id) {
            return;
        }
        var toolbar = IPython.toolbar.element;
        var link = toolbar.find("a#nbviewer");
        if ( ! link.length ) {
            link = $('');
            toolbar.append(
                $('').append(link)
            );
        }

        link.attr("href", "http://nbviewer.ipython.org/" + gist_id);
        link.text("http://nbviewer.ipython.org/" + gist_id);
    };

    var gist_button = function () {
        if (!IPython.toolbar) {
            $([IPython.events]).on("app_initialized.NotebookApp", gist_button);
            return;
        }
        if ($("#gist_notebook").length === 0) {
            IPython.toolbar.add_buttons_group([
                {
                    'label'   : 'Share Notebook as gist',
                    'icon'    : 'icon-share',
                    'callback': gist_notebook,
                    'id'      : 'gist_notebook'
                },
            ]);
        }
        update_gist_link();
    };

    var load_extension = function () {
        gist_button();
        update_gist_link();
        $([IPython.events]).on("notebook_loaded.Notebook", function () {update_gist_link();});
    };

    return {
        load_extension : load_extension,
    };

});<commit_msg>longer gist success message<commit_after>/*
Add this file to $(ipython locate)/nbextensions/gist.js
And load it with:

require(["nbextensions/gist"], function (gist_extension) {
    console.log('gist extension loaded');
    gist_extension.load_extension();
});

*/
define( function () {

    var token_name = "gist_github_token";

    // dialog to request GitHub OAuth token
    // I'm not sure it's possible to step through OAuth purely client side,
    // so just ask the user to go create a token manually.

    var token_dialog = function () {
        var dialog = $('
').append(
            $("
")
                .html('Enter a GitHub OAuth token:')
        ).append(
            $("
")
        ).append(
            $('<input/>').attr('type','text').attr('size','40')
        );
        IPython.dialog.modal({
            title: "GitHub OAuth",
            body: dialog,
            buttons : {
                "Cancel": {},
                "OK": {
                    class: "btn-primary",
                    click: function () {
                        var token = $(this).find('input').val();
                        localStorage[token_name] = token;
                        gist_notebook();
                    }
                }
            },
            open : function (event, ui) {
                var that = $(this);
                // Upon ENTER, click the OK button.
                that.find('input[type="text"]').keydown(function (event, ui) {
                    if (event.which === utils.keycodes.ENTER) {
                        that.find('.btn-primary').first().click();
                        return false;
                    }
                });
                that.find('input[type="text"]').focus().select();
            }
        });
    };
    // get the GitHub token, via cookie or
    var get_github_token = function () {
        var token = localStorage[token_name];
        if (!token) {
            token_dialog();
            return null;
        }
        return token;
    };

    var gist_notebook = function () {
        var gist_id = IPython.notebook.metadata.gist_id;
        console.log(gist_id);
        var token = get_github_token();
        if (!token) {
            // dialog's are async, so we can't do anything yet.
            // the dialog OK callback will continue the process.
            console.log("waiting for auth dialog");
            return;
        }
        var method = "POST";
        var url = "https://api.github.com/gists";
        if (gist_id) {
            url = url + "/" + gist_id;
            method = "PATCH";
        }
        var filedata = {};
        var nbj = IPython.notebook.toJSON();
        nbj.nbformat = 3;
        filedata[IPython.notebook.notebook_name] = {content : JSON.stringify(nbj, undefined, 1)};
        console.log(token);
        var settings = {
            type : method,
            headers : { Authorization: "token " + token },
            data : JSON.stringify({
                public : true,
                files : filedata,
            }),
            success : function (data, status) {
                console.log("gist succeeded: " + data.id);
                IPython.notebook.metadata.gist_id = data.id;
                update_gist_link(data.id);
                IPython.notification_area.get_widget("notebook").set_message("gist succeeded: " + data.id, 1500);
            },
            error : function (jqXHR, status, err) {
                console.log(jqXHR);
                if (jqXHR.status == 403) {
                    // authentication failed,
                    // delete the cookie so that we prompt again next time
                    delete localStorage[token_name];
                }
                alert("Uploading gist failed: " + err);
            }
        };
        $.ajax(url, settings);
    };

    var update_gist_link = function(gist_id) {
        if (!gist_id) {
            gist_id = IPython.notebook.metadata.gist_id;
        } else {
            IPython.notebook.metadata.gist_id = gist_id;
        }
        if (!gist_id) {
            return;
        }
        var toolbar = IPython.toolbar.element;
        var link = toolbar.find("a#nbviewer");
        if ( ! link.length ) {
            link = $('');
            toolbar.append(
                $('').append(link)
            );
        }

        link.attr("href", "http://nbviewer.ipython.org/" + gist_id);
        link.text("http://nbviewer.ipython.org/" + gist_id);
    };

    var gist_button = function () {
        if (!IPython.toolbar) {
            $([IPython.events]).on("app_initialized.NotebookApp", gist_button);
            return;
        }
        if ($("#gist_notebook").length === 0) {
            IPython.toolbar.add_buttons_group([
                {
                    'label'   : 'Share Notebook as gist',
                    'icon'    : 'icon-share',
                    'callback': gist_notebook,
                    'id'      : 'gist_notebook'
                },
            ]);
        }
        update_gist_link();
    };

    var load_extension = function () {
        gist_button();
        update_gist_link();
        $([IPython.events]).on("notebook_loaded.Notebook", function () {update_gist_link();});
    };

    return {
        load_extension : load_extension,
    };

});<|endoftext|>


<commit_before>moduleAid.VERSION = '2.0.0';
moduleAid.LAZY = true;

// objectWatcher - This acts as a replacement for the event DOM Attribute Modified, works for both attributes and object properties
//	addPropertyWatcher(obj, prop, handler, capture) - registers handler as a watcher for obj property prop changes
//		obj - (xul element or object) to watch for changes
//		prop - (string) property name in obj to watch
//		handler - (function) method to fire when prop is set or changed
//		(optional) capture - when (bool) true it cancels setting the property if handler returns (bool) false, defaults to (bool) false
//	removePropertyWatcher(obj, prop, handler, capture) - unregisters handler as a watcher for prop changes
//		see addPropertyWatcher()
//	addAttributeWatcher(obj, attr, handler, capture) - registers handler as a watcher for object attribute attr changes
//		obj - (xul element or object) to watch for changes
//		attr - (string) attribute name in obj to watch
//		handler - (function) method to fire when attr is set, removed or changed
//		(optional) capture - when (bool) true it cancels setting the attribute if handler returns (bool) false, defaults to (bool) false
//	removeAttributeWatcher(obj, attr, handler, capture) - unregisters handler as a watcher for object attribute attr changes
//		see addAttributeWatcher()
// All handlers expect function(obj, prop, oldVal, newVal), where:
//	obj - (xul element or object) where the change occured
//	prop - (string) name of the property or attribute being set or changed
//	oldVal - the current value of prop
//	newVal - the new value of prop
// Note: deleting a watched property does not trigger the watchers, so don't do it! Also setting the watchers on an unset property won't work either.
// DOM Mutation Observers were implemented in Firefox 14
this.objectWatcher = {
	// Properties part, works by replacing the get and set accessor methods of a property with custom ones
	addPropertyWatcher: function(obj, prop, handler, capture) {
		if(typeof(obj[prop]) == 'undefined' || !this.setWatchers(obj)) { return false; }
		capture = (capture) ? true : false;

		if(typeof(obj._propWatchers.properties[prop]) == 'undefined') {
			var tempVal = obj[prop];
			// can't watch constants
			if(!(delete obj[prop])) {
				this.unsetWatchers(obj);
				return false;
			}

			obj._propWatchers.properties[prop] = {
				value: tempVal,
				handlers: []
			};

			obj.__defineGetter__(prop, function () { return this._propWatchers.properties[prop].value; });
			obj.__defineSetter__(prop, function (newVal) {
				var oldVal = this._propWatchers.properties[prop].value;
				for(var i = 0; i < this._propWatchers.properties[prop].handlers.length; i++) {
					if(this._propWatchers.properties[prop].handlers[i].capture) {
						var continueHandlers = true;
						try { continueHandlers = this._propWatchers.properties[prop].handlers[i].handler(this, prop, oldVal, newVal); }
						catch(ex) { Cu.reportError(ex); }
						if(continueHandlers === false) {
							return this._propWatchers.properties[prop].value;
						}
					}
				}
				this._propWatchers.properties[prop].value = newVal;
				for(var i = 0; i < this._propWatchers.properties[prop].handlers.length; i++) {
					if(!this._propWatchers.properties[prop].handlers[i].capture) {
						try { this._propWatchers.properties[prop].handlers[i].handler(this, prop, oldVal, newVal); }
						catch(ex) { Cu.reportError(ex); }
					}
				}
				return this._propWatchers.properties[prop].value;
			});
		}
		else {
			for(var i=0; i<obj._propWatchers.properties[prop].handlers.length; i++) {
				if(compareFunction(obj._propWatchers.properties[prop].handlers[i].handler, handler)
				&& capture == obj._propWatchers.properties[prop].handlers[i].capture) { return true; }
			}
		}

		obj._propWatchers.properties[prop].handlers.push({ handler: handler, capture: capture });
		obj._propWatchers.setters++;
		return true;
	},

	removePropertyWatcher: function(obj, prop, handler, capture) {
		if(!obj._propWatchers || typeof(obj._propWatchers.properties[prop]) == 'undefined') { return false; }
		capture = (capture) ? true : false;

		for(var i=0; i<obj._propWatchers.properties[prop].handlers.length; i++) {
			if(compareFunction(obj._propWatchers.properties[prop].handlers[i].handler, handler)
			&& capture == obj._propWatchers.properties[prop].handlers[i].capture) {
				obj._propWatchers.properties[prop].handlers.splice(i, 1);
				if(obj._propWatchers.properties[prop].handlers.length == 0) {
					delete obj[prop]; // remove accessors
					obj[prop] = obj._propWatchers.properties[prop].value;
					delete obj._propWatchers.properties[prop];
				}

				obj._propWatchers.setters--;
				this.unsetWatchers(obj);
				return true;
			}
		}

		return false;
	},

	// Attributes part, works through delayed DOM Mutation Observers
	addAttributeWatcher: function(obj, attr, handler, capture) {
		if(!this.setWatchers(obj)) { return false; }
		capture = (capture) ? true : false;

		if(typeof(obj._propWatchers.attributes[attr]) == 'undefined') {
			obj._propWatchers.disconnect();

			obj._propWatchers.attributes[attr] = {
				value: (obj.hasAttribute(attr)) ? obj.getAttribute(attr) : null,
				handlers: []
			};

			obj._propWatchers.reconnect();
		}
		else {
			for(var i=0; i<obj._propWatchers.attributes[attr].handlers.length; i++) {
				if(compareFunction(obj._propWatchers.attributes[attr].handlers[i].handler, handler)
				&& capture == obj._propWatchers.attributes[attr].handlers[i].capture) { return true; }
			}
		}

		obj._propWatchers.attributes[attr].handlers.push({ handler: handler, capture: capture });
		obj._propWatchers.setters++;
		return true;
	},

	removeAttributeWatcher: function(obj, attr, handler, capture) {
		if(!obj._propWatchers || typeof(obj._propWatchers.attributes[attr]) == 'undefined') { return false; }
		capture = (capture) ? true : false;

		for(var i=0; i<obj._propWatchers.attributes[attr].handlers.length; i++) {
			if(compareFunction(obj._propWatchers.attributes[attr].handlers[i].handler, handler)
			&& capture == obj._propWatchers.attributes[attr].handlers[i].capture) {
				obj._propWatchers.attributes[attr].handlers.splice(i, 1);
				if(obj._propWatchers.attributes[attr].handlers.length == 0) {
					obj._propWatchers.disconnect();

					delete obj._propWatchers.attributes[attr];

					obj._propWatchers.reconnect();
				}

				obj._propWatchers.setters--;
				this.unsetWatchers(obj);
				return true;
			}
		}

		return false;
	},

	setWatchers: function(obj) {
		if(!obj || typeof(obj) != 'object') { return false; }
		if(obj._propWatchers) { return true; }

		obj._propWatchers = {
			setters: 0,
			properties: {},
			attributes: {},
			mutations: [],
			scheduler: null,
			reconnect: function() {
				var attrList = [];
				for(var a in this.attributes) {
					attrList.push(a);
				}
				if(attrList.length > 0) {
					var observerProperties = {
						attributes: true,
						attributeOldValue: true
					};
					observerProperties.attributeFilter = attrList;
					this.mutationObserver.observe(obj, observerProperties);
				}
			},
			disconnect: function() {
				this.mutationObserver.disconnect();
			},
			scheduleWatchers: function(mutations, observer) {
				if(obj._propWatchers.schedule) {
					obj._propWatchers.schedule.cancel();
					obj._propWatchers.schedule = null;
				}

				for(var m=0; m<mutations.length; m++) {
					obj._propWatchers.mutations.push(mutations[m]);
				}
				// the script could become really heavy if it called the main function everytime (width attribute on sidebar and dragging it for instance)
				// half a second delay ought to relieve things
				obj._propWatchers.schedule = aSync(obj._propWatchers.callAttrWatchers, 500);
			},
			callAttrWatchers: function() {
				obj._propWatchers.disconnect();
				var muts = obj._propWatchers.mutations;
				obj._propWatchers.mutations = [];

				var attrList = [];
				for(var attr in obj._propWatchers.attributes) {
					attrList.push(attr);

					var changes = 0;
					var oldValue = false;
					var newValue = obj.hasAttribute(attr) ? obj.getAttribute(attr) : null;
					captureMutations_loop: for(var m=0; m<muts.length; m++) {
						if(muts[m].attributeName != attr) { continue; }

						oldValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
						newValue = false;
						for(var n=m+1; n<muts.length; n++) {
							if(muts[n].attributeName == attr) {
								newValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
								break;
							}
						}
						if(newValue === false) {
							newValue = obj.hasAttribute(attr) ? obj.getAttribute(attr) : null;
						}

						if(oldValue === newValue) {
							newValue = oldValue;
							muts.splice(m, 1);
							m--;
							continue captureMutations_loop;
						}

						for(var h=0; h<obj._propWatchers.attributes[attr].handlers.length; h++) {
							if(obj._propWatchers.attributes[attr].handlers[h].capture) {
								var continueHandlers = true;
								try { continueHandlers = obj._propWatchers.attributes[attr].handlers[h].handler(obj, attr, oldValue, newValue); }
								catch(ex) { Cu.reportError(ex); }

								if(continueHandlers === false) {
									for(var n=m+1; n<muts.length; n++) {
										if(muts[n].attributeName == attr) {
											muts[n].realOldValue = oldValue;
											break;
										}
									}
									newValue = oldValue;
									muts.splice(m, 1);
									m--;
									continue captureMutations_loop;
								}
							}
						}

						changes++;
					}

					if(newValue !== null) {
						obj.setAttribute(attr, newValue);
					} else {
						obj.removeAttribute(attr);
					}

					if(changes > 0) {
						for(var m=0; m<muts.length; m++) {
							if(muts[m].attributeName != attr) { continue; }

							oldValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
							newValue = false;
							for(var n=m+1; n<muts.length; n++) {
								if(muts[n].attributeName == attr) {
									newValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
									break;
								}
							}
							if(newValue === false) {
								newValue = obj.hasAttribute(attr) ? obj.getAttribute(attr) : null;
							}

							for(var h=0; h<obj._propWatchers.attributes[attr].handlers.length; h++) {
								if(!obj._propWatchers.attributes[attr].handlers[h].capture) {
									try { obj._propWatchers.attributes[attr].handlers[h].handler(obj, attr, oldValue, newValue); }
									catch(ex) { Cu.reportError(ex); }
								}
							}
						}
					}
				}

				obj._propWatchers.reconnect();
			}
		};
		obj._propWatchers.mutationObserver = new obj.ownerDocument.defaultView.MutationObserver(obj._propWatchers.scheduleWatchers);

		return true;
	},

	unsetWatchers: function(obj) {
		if(typeof(obj) != 'object' || obj === null || !obj._propWatchers || obj._propWatchers.setters > 0) { return false; }

		obj._propWatchers.disconnect();
		delete obj._propWatchers;
		return true;
	}
};
<commit_msg>property watchers can change the property when triggered without re-triggering the watchers<commit_after>moduleAid.VERSION = '2.0.1';
moduleAid.LAZY = true;

// objectWatcher - This acts as a replacement for the event DOM Attribute Modified, works for both attributes and object properties
//	addPropertyWatcher(obj, prop, handler, capture) - registers handler as a watcher for obj property prop changes
//		obj - (xul element or object) to watch for changes
//		prop - (string) property name in obj to watch
//		handler - (function) method to fire when prop is set or changed
//		(optional) capture - when (bool) true it cancels setting the property if handler returns (bool) false, defaults to (bool) false
//	removePropertyWatcher(obj, prop, handler, capture) - unregisters handler as a watcher for prop changes
//		see addPropertyWatcher()
//	addAttributeWatcher(obj, attr, handler, capture) - registers handler as a watcher for object attribute attr changes
//		obj - (xul element or object) to watch for changes
//		attr - (string) attribute name in obj to watch
//		handler - (function) method to fire when attr is set, removed or changed
//		(optional) capture - when (bool) true it cancels setting the attribute if handler returns (bool) false, defaults to (bool) false
//	removeAttributeWatcher(obj, attr, handler, capture) - unregisters handler as a watcher for object attribute attr changes
//		see addAttributeWatcher()
// All handlers expect function(obj, prop, oldVal, newVal), where:
//	obj - (xul element or object) where the change occured
//	prop - (string) name of the property or attribute being set or changed
//	oldVal - the current value of prop
//	newVal - the new value of prop
// Note: deleting a watched property does not trigger the watchers, so don't do it! Also setting the watchers on an unset property won't work either.
// DOM Mutation Observers were implemented in Firefox 14
this.objectWatcher = {
	// Properties part, works by replacing the get and set accessor methods of a property with custom ones
	addPropertyWatcher: function(obj, prop, handler, capture) {
		if(typeof(obj[prop]) == 'undefined' || !this.setWatchers(obj)) { return false; }
		capture = (capture) ? true : false;

		if(typeof(obj._propWatchers.properties[prop]) == 'undefined') {
			var tempVal = obj[prop];
			// can't watch constants
			if(!(delete obj[prop])) {
				this.unsetWatchers(obj);
				return false;
			}

			obj._propWatchers.properties[prop] = {
				value: tempVal,
				handlers: [],
				handling: false
			};

			obj.__defineGetter__(prop, function () { return this._propWatchers.properties[prop].value; });
			obj.__defineSetter__(prop, function (newVal) {
				if(this._propWatchers.properties[prop].handling) {
					this._propWatchers.properties[prop].value = newVal;
					return this._propWatchers.properties[prop].value;
				}
				this._propWatchers.properties[prop].handling = true;

				var oldVal = this._propWatchers.properties[prop].value;
				for(var i = 0; i < this._propWatchers.properties[prop].handlers.length; i++) {
					if(this._propWatchers.properties[prop].handlers[i].capture) {
						var continueHandlers = true;
						try { continueHandlers = this._propWatchers.properties[prop].handlers[i].handler(this, prop, oldVal, newVal); }
						catch(ex) { Cu.reportError(ex); }
						if(continueHandlers === false) {
							this._propWatchers.properties[prop].handling = false;
							return this._propWatchers.properties[prop].value;
						}
					}
				}
				this._propWatchers.properties[prop].value = newVal;
				for(var i = 0; i < this._propWatchers.properties[prop].handlers.length; i++) {
					if(!this._propWatchers.properties[prop].handlers[i].capture) {
						try { this._propWatchers.properties[prop].handlers[i].handler(this, prop, oldVal, newVal); }
						catch(ex) { Cu.reportError(ex); }
					}
				}

				this._propWatchers.properties[prop].handling = false;
				return this._propWatchers.properties[prop].value;
			});
		}
		else {
			for(var i=0; i<obj._propWatchers.properties[prop].handlers.length; i++) {
				if(compareFunction(obj._propWatchers.properties[prop].handlers[i].handler, handler)
				&& capture == obj._propWatchers.properties[prop].handlers[i].capture) { return true; }
			}
		}

		obj._propWatchers.properties[prop].handlers.push({ handler: handler, capture: capture });
		obj._propWatchers.setters++;
		return true;
	},

	removePropertyWatcher: function(obj, prop, handler, capture) {
		if(!obj._propWatchers || typeof(obj._propWatchers.properties[prop]) == 'undefined') { return false; }
		capture = (capture) ? true : false;

		for(var i=0; i<obj._propWatchers.properties[prop].handlers.length; i++) {
			if(compareFunction(obj._propWatchers.properties[prop].handlers[i].handler, handler)
			&& capture == obj._propWatchers.properties[prop].handlers[i].capture) {
				obj._propWatchers.properties[prop].handlers.splice(i, 1);
				if(obj._propWatchers.properties[prop].handlers.length == 0) {
					delete obj[prop]; // remove accessors
					obj[prop] = obj._propWatchers.properties[prop].value;
					delete obj._propWatchers.properties[prop];
				}

				obj._propWatchers.setters--;
				this.unsetWatchers(obj);
				return true;
			}
		}

		return false;
	},

	// Attributes part, works through delayed DOM Mutation Observers
	addAttributeWatcher: function(obj, attr, handler, capture) {
		if(!this.setWatchers(obj)) { return false; }
		capture = (capture) ? true : false;

		if(typeof(obj._propWatchers.attributes[attr]) == 'undefined') {
			obj._propWatchers.disconnect();

			obj._propWatchers.attributes[attr] = {
				value: (obj.hasAttribute(attr)) ? obj.getAttribute(attr) : null,
				handlers: []
			};

			obj._propWatchers.reconnect();
		}
		else {
			for(var i=0; i<obj._propWatchers.attributes[attr].handlers.length; i++) {
				if(compareFunction(obj._propWatchers.attributes[attr].handlers[i].handler, handler)
				&& capture == obj._propWatchers.attributes[attr].handlers[i].capture) { return true; }
			}
		}

		obj._propWatchers.attributes[attr].handlers.push({ handler: handler, capture: capture });
		obj._propWatchers.setters++;
		return true;
	},

	removeAttributeWatcher: function(obj, attr, handler, capture) {
		if(!obj._propWatchers || typeof(obj._propWatchers.attributes[attr]) == 'undefined') { return false; }
		capture = (capture) ? true : false;

		for(var i=0; i<obj._propWatchers.attributes[attr].handlers.length; i++) {
			if(compareFunction(obj._propWatchers.attributes[attr].handlers[i].handler, handler)
			&& capture == obj._propWatchers.attributes[attr].handlers[i].capture) {
				obj._propWatchers.attributes[attr].handlers.splice(i, 1);
				if(obj._propWatchers.attributes[attr].handlers.length == 0) {
					obj._propWatchers.disconnect();

					delete obj._propWatchers.attributes[attr];

					obj._propWatchers.reconnect();
				}

				obj._propWatchers.setters--;
				this.unsetWatchers(obj);
				return true;
			}
		}

		return false;
	},

	setWatchers: function(obj) {
		if(!obj || typeof(obj) != 'object') { return false; }
		if(obj._propWatchers) { return true; }

		obj._propWatchers = {
			setters: 0,
			properties: {},
			attributes: {},
			mutations: [],
			scheduler: null,
			reconnect: function() {
				var attrList = [];
				for(var a in this.attributes) {
					attrList.push(a);
				}
				if(attrList.length > 0) {
					var observerProperties = {
						attributes: true,
						attributeOldValue: true
					};
					observerProperties.attributeFilter = attrList;
					this.mutationObserver.observe(obj, observerProperties);
				}
			},
			disconnect: function() {
				this.mutationObserver.disconnect();
			},
			scheduleWatchers: function(mutations, observer) {
				if(obj._propWatchers.schedule) {
					obj._propWatchers.schedule.cancel();
					obj._propWatchers.schedule = null;
				}

				for(var m=0; m<mutations.length; m++) {
					obj._propWatchers.mutations.push(mutations[m]);
				}
				// the script could become really heavy if it called the main function everytime (width attribute on sidebar and dragging it for instance)
				// half a second delay ought to relieve things
				obj._propWatchers.schedule = aSync(obj._propWatchers.callAttrWatchers, 500);
			},
			callAttrWatchers: function() {
				obj._propWatchers.disconnect();
				var muts = obj._propWatchers.mutations;
				obj._propWatchers.mutations = [];

				var attrList = [];
				for(var attr in obj._propWatchers.attributes) {
					attrList.push(attr);

					var changes = 0;
					var oldValue = false;
					var newValue = obj.hasAttribute(attr) ? obj.getAttribute(attr) : null;
					captureMutations_loop: for(var m=0; m<muts.length; m++) {
						if(muts[m].attributeName != attr) { continue; }

						oldValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
						newValue = false;
						for(var n=m+1; n<muts.length; n++) {
							if(muts[n].attributeName == attr) {
								newValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
								break;
							}
						}
						if(newValue === false) {
							newValue = obj.hasAttribute(attr) ? obj.getAttribute(attr) : null;
						}

						if(oldValue === newValue) {
							newValue = oldValue;
							muts.splice(m, 1);
							m--;
							continue captureMutations_loop;
						}

						for(var h=0; h<obj._propWatchers.attributes[attr].handlers.length; h++) {
							if(obj._propWatchers.attributes[attr].handlers[h].capture) {
								var continueHandlers = true;
								try { continueHandlers = obj._propWatchers.attributes[attr].handlers[h].handler(obj, attr, oldValue, newValue); }
								catch(ex) { Cu.reportError(ex); }

								if(continueHandlers === false) {
									for(var n=m+1; n<muts.length; n++) {
										if(muts[n].attributeName == attr) {
											muts[n].realOldValue = oldValue;
											break;
										}
									}
									newValue = oldValue;
									muts.splice(m, 1);
									m--;
									continue captureMutations_loop;
								}
							}
						}

						changes++;
					}

					if(newValue !== null) {
						obj.setAttribute(attr, newValue);
					} else {
						obj.removeAttribute(attr);
					}

					if(changes > 0) {
						for(var m=0; m<muts.length; m++) {
							if(muts[m].attributeName != attr) { continue; }

							oldValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
							newValue = false;
							for(var n=m+1; n<muts.length; n++) {
								if(muts[n].attributeName == attr) {
									newValue = typeof(muts[m].realOldValue) != 'undefined' ? muts[m].realOldValue : muts[m].oldValue;
									break;
								}
							}
							if(newValue === false) {
								newValue = obj.hasAttribute(attr) ? obj.getAttribute(attr) : null;
							}

							for(var h=0; h<obj._propWatchers.attributes[attr].handlers.length; h++) {
								if(!obj._propWatchers.attributes[attr].handlers[h].capture) {
									try { obj._propWatchers.attributes[attr].handlers[h].handler(obj, attr, oldValue, newValue); }
									catch(ex) { Cu.reportError(ex); }
								}
							}
						}
					}
				}

				obj._propWatchers.reconnect();
			}
		};
		obj._propWatchers.mutationObserver = new obj.ownerDocument.defaultView.MutationObserver(obj._propWatchers.scheduleWatchers);

		return true;
	},

	unsetWatchers: function(obj) {
		if(typeof(obj) != 'object' || obj === null || !obj._propWatchers || obj._propWatchers.setters > 0) { return false; }

		obj._propWatchers.disconnect();
		delete obj._propWatchers;
		return true;
	}
};
<|endoftext|>